mrkeyoor.com_
Sun 20 Sept 11:41 UTC
PyPIUtilsupdated 20 Sept 2026

PyGithub review

PyGithub 2.10.0 is a synchronous, object-oriented wrapper around GitHub's REST API. Repositories, issues, pull requests, workflows, users, and organizations arrive as Python objects, while the client handles authentication, pagination, connection reuse, Enterprise base URLs, retries, and GitHub App installation tokens. Version 2.10.0 drops Python 3.9, adds selectable GitHub API versions, issue dependencies, workflow attempt and dispatch details, search `incomplete_results`, and a maximum rate-limit wait. Its GraphQL method remains a low-level dictionary response. Our wheel was pure Python and typed.

Verdict

PyGithub 2.10.0 installed in 0.7 seconds and occupied 25 MB across 12 packages in our sandbox, with typed pure-Python code and no audit findings. It fits synchronous REST automation; async or GraphQL-heavy services should choose a client designed for that execution model, and long-lived products should review the LGPL terms and open maintainer request.

We installed it

Lab card: what happened when we installed PyGithubScreenshot of PyGithub documentation
Install✓ · 0.7s12 packages on disk · 25 MB
Importimport github in 1.05s · pure Python · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does PyGithub install cleanly?

Yes. In a fresh container with an empty cache, pip install PyGithub finished in 0.7s, leaving 12 packages and 25 MB on disk. pip-audit reported no known vulnerabilities.

What does PyGithub need to run?

Python >=3.10, and nothing compiled: it is pure Python. In our run import github succeeded in 1.05s, and the package ships py.typed for type checkers.

PyGithub or GitHubKit: which should you use?

GitHubKit: Use it when one typed library must cover async and sync REST calls plus GraphQL. PyGithub 2.10.0 installed in 0.7 seconds and occupied 25 MB across 12 packages in our sandbox, with typed pure-Python code and no audit findings.

When should you not use PyGithub?

The service is asyncio-based and makes concurrent GitHub requests. PyGithub blocks its caller; GitHubKit supports async execution.

API stability3/5Repository, Issue, PullRequest, PaginatedList, and Workflow objects remain recognizable across the 2.x line, yet active migrations are visible. Authentication moved under `Auth`, rate-limit details moved under `resources`, lazy configuration changed, and singular assignee plus several team and reaction methods are deprecated. Version 2.10 drops Python 3.9 and adds API-version, retry, workflow, pull, and pagination options. Pinning a minor release and reading deprecations is warranted.
Docs3/5Read the Docs provides class and method references with links to corresponding GitHub REST operations. The README covers token, Enterprise, and App authentication, and the changelog names deprecations and 2.10.0 additions. Task discovery is still awkward: developers often find a GitHub REST endpoint first, map it to a PyGithub class method, then check whether lazy loading or pagination adds requests. GraphQL, request budgeting, and cross-version Enterprise behavior receive less guided treatment.
Maintenance3/5PyGithub 2.10.0 was released on August 20, 2026, and GitHub showed an unarchived repository pushed on August 25, 2026 with 397 open issues and pull requests. The release adds GitHub API coverage, retry controls, OpenAPI work, bug fixes, and Python 3.15 support. That is current activity. The README also says the project is actively seeking maintainers for triage, review, and releases, which is direct evidence of capacity risk for adopters.
Ecosystem5/5The supplied registry figure is 16,043,071 weekly downloads, and the repository has 7,765 GitHub stars. PyGithub is widely used in repository administration, release jobs, issue bots, and GitHub App services, so many REST tasks have prior examples. Its object names mirror familiar GitHub resources. Newer clients are a better fit where async concurrency, generated endpoint parity, or GraphQL is central, but the installed base makes PyGithub easy to recognize and staff.

Use it if

  • A synchronous Python job performs several GitHub REST tasks and benefits from Repository, Issue, PullRequest, and WorkflowRun objects.
  • The program should iterate paginated REST collections without implementing Link-header traversal itself.
  • A GitHub App needs JWT signing, installation-token refresh, and a client scoped to each installation.
  • The same automation must switch between GitHub.com and GitHub Enterprise Server through a REST base URL.
Skip it if

Setup reality

We installed PyGithub 2.10.0 in a fresh Python 3.12 Bookworm sandbox. pip finished in 0.7 seconds and left 12 packages using 25 MB. The package declares five direct dependencies, requires Python 3.10 or later, is pure Python, and includes py.typed. import github succeeded in 1.05 seconds. pip-audit found zero known vulnerabilities. The distribution identifies the GNU Library or Lesser General Public License.

Authentication now belongs in Auth.Token, Auth.AppAuth, or another Auth class; old positional token forms are deprecated. Store the token or private key outside source and grant only required repository or organization permissions. Enterprise clients need the REST base URL, commonly ending in /api/v3, plus an API version compatible with that server. A 404 can mean the resource is private and the token lacks permission, so it does not prove absence.

Paginated lists fetch pages during iteration, and lazy objects can make their first request when a property is read. A harmless-looking loop can therefore issue one page request plus many object completion requests. Use slices when only the first results matter, select per_page deliberately, and inspect 2.10's incomplete_results on searches. GitHub search also caps some result sets; partition an inventory rather than presenting totalCount as a complete audit without checking endpoint semantics.

PyGithub is synchronous and adds request or write throttling. Version 2.10.0 adds max_rate_limit_wait, allowing a batch job to fail instead of sleeping past its deadline. Keep rate-limit retries bounded, especially for secondary limits, and do not remove default spacing without replacement logic. Reuse the client for pooled connections, then call close() or use a context manager. Async servers should offload calls to workers or choose an async-native client.

Patterns

Authenticate and close the client token-authentication

import os
from github import Auth, Github

auth = Auth.Token(os.environ['GITHUB_TOKEN'])
with Github(auth=auth) as client:
    for repo in client.get_user().get_repos():
        print(repo.full_name)

Version 2.10 expects an Auth object. The older positional-token constructor is deprecated; keep the token out of source and use minimal permissions.

Connect to GitHub Enterprise Server enterprise-client

import os
from github import Auth, Github

client = Github(
    base_url='https://ghe.example.com/api/v3',
    auth=Auth.Token(os.environ['GHE_TOKEN']),
    api_version='2022-11-28',
)

The REST base URL usually includes `/api/v3`. Match the selected API version and TLS trust to the Enterprise Server release.

Create a GitHub App installation client github-app-installation

from pathlib import Path
from github import Auth, GithubIntegration

app_auth = Auth.AppAuth(
    app_id=123456,
    private_key=Path('/run/secrets/github-app.pem').read_text(),
)
integration = GithubIntegration(auth=app_auth)
client = integration.get_github_for_installation(987654)

AppAuth signs the application JWT; the installation client obtains and refreshes the repository-scoped installation token.

Slice a paginated issue list limit-pagination

repo = client.get_repo('PyGithub/PyGithub')
open_issues = repo.get_issues(state='open')

for issue in open_issues[:20]:
    print(issue.number, issue.title)

Iteration can fetch the whole collection. A short slice limits calls; add `is:issue` to searches when pull requests must be excluded.

Open and update an issue create-issue

repo = client.get_repo('owner/project')
issue = repo.create_issue(
    title='Nightly build failed',
    body='The failing run is linked in the log.',
    labels=['bug', 'ci'],
    assignees=['octocat'],
)
issue.create_comment('Retry produced the same failure.')
issue.edit(state='closed', state_reason='completed')

Labels must already exist and the token needs issue write permission. Pull requests also appear through GitHub issue endpoints.

Update a file with its current blob SHA update-file

repo = client.get_repo('owner/project')
current = repo.get_contents('README.md', ref='main')
text = current.decoded_content.decode('utf-8')
repo.update_file(
    path=current.path,
    message='docs: fix typo',
    content=text.replace('teh', 'the'),
    sha=current.sha,
    branch='main',
)

A concurrent edit makes the SHA stale and GitHub rejects the write. Fetch again instead of overwriting the newer blob.

Create a branch and draft pull request open-pull-request

repo = client.get_repo('owner/project')
base = repo.get_branch('main')
repo.create_git_ref(
    ref='refs/heads/bot/update',
    sha=base.commit.sha,
)
pr = repo.create_pull(
    base='main',
    head='bot/update',
    title='chore: update generated data',
    body='Automated update.',
    draft=True,
)

Git references need the full `refs/heads/` prefix. Handle an existing branch before retrying an interrupted automation run.

Search only real issues search-issues

results = client.search_issues(
    'repo:PyGithub/PyGithub is:issue is:open label:bug'
)
print(results.totalCount)
for issue in results[:10]:
    print(issue.number, issue.title)

GitHub search has its own rate-limit bucket and can cap or mark results incomplete. Version 2.10 exposes `incomplete_results` for that check.

Read core and search budgets rate-limits

limits = client.get_rate_limit()
print(limits.resources.core.remaining)
print(limits.resources.core.reset)
print(limits.resources.search.remaining)

Current 2.x releases place buckets under `resources`; older examples using `limits.core` no longer match the returned object.

Handle GitHub failure classes classify-api-errors

from github import (
    BadCredentialsException,
    GithubException,
    RateLimitExceededException,
    UnknownObjectException,
)

try:
    repo = client.get_repo('owner/private-project')
except UnknownObjectException:
    report_missing_or_hidden()
except RateLimitExceededException:
    reschedule()
except BadCredentialsException:
    rotate_token()
except GithubException as error:
    report_api_error(error.status, error.data)

GitHub may return 404 for a private resource hidden from the token, so UnknownObjectException does not establish that it does not exist.

Dispatch a workflow and request run details dispatch-workflow

repo = client.get_repo('owner/project')
workflow = repo.get_workflow('deploy.yml')
run = workflow.create_dispatch(
    ref='main',
    inputs={'environment': 'staging'},
    throw=True,
    return_run_details=True,
)
print(run.id, run.html_url)

Version 2.10 can return run details. The workflow needs `workflow_dispatch`, and the token needs Actions write permission.

Stop rate-limit waits at a job deadline bounded-rate-retry

import os
from github import Auth, Github, GithubRetry

retry = GithubRetry(total=5, max_rate_limit_wait=120)
client = Github(
    auth=Auth.Token(os.environ['GITHUB_TOKEN']),
    retry=retry,
    per_page=100,
    lazy=True,
)

Version 2.10 raises when the required rate-limit sleep exceeds 120 seconds. Lazy properties can still trigger requests later inside loops.

Alternatives

PackageRegistryPick it when
GitHubKitPyPIUse it when one typed library must cover async and sync REST calls plus GraphQL.
ghapiPyPIChoose it for a thinner endpoint-shaped interface generated from GitHub's API description.
gidgethubPyPIUse it for asynchronous GitHub integrations when its lower-level API and transport model fit the service.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.