githubkit 0.16.1__tar.gz → 0.17.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {githubkit-0.16.1 → githubkit-0.17.0}/PKG-INFO +3 -3
  2. githubkit-0.17.0/githubkit/_httpx.py +39 -0
  3. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/auth/_url.py +1 -1
  4. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/auth/action.py +1 -2
  5. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/auth/app.py +8 -7
  6. githubkit-0.17.0/githubkit/auth/base.py +33 -0
  7. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/auth/oauth.py +31 -13
  8. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/auth/token.py +1 -1
  9. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/auth/unauth.py +1 -1
  10. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/compat.py +8 -33
  11. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/config.py +1 -1
  12. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/core.py +15 -13
  13. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/exception.py +1 -1
  14. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/github.py +1 -2
  15. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/hishel.py +2 -2
  16. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/response.py +3 -3
  17. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/rest/paginator.py +1 -2
  18. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/throttling.py +2 -1
  19. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/typing.py +2 -1
  20. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/utils.py +3 -8
  21. {githubkit-0.16.1 → githubkit-0.17.0}/pyproject.toml +56 -19
  22. {githubkit-0.16.1 → githubkit-0.17.0}/pyproject.toml.orig +71 -18
  23. githubkit-0.16.1/githubkit/auth/base.py +0 -13
  24. {githubkit-0.16.1 → githubkit-0.17.0}/README.md +0 -0
  25. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/__init__.py +0 -0
  26. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/auth/__init__.py +0 -0
  27. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/cache/__init__.py +0 -0
  28. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/cache/base.py +0 -0
  29. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/cache/mem_cache.py +0 -0
  30. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/cache/redis.py +0 -0
  31. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/graphql/__init__.py +0 -0
  32. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/graphql/models.py +0 -0
  33. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/graphql/paginator.py +0 -0
  34. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/log.py +0 -0
  35. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/module_hooks/alias_module.py +0 -0
  36. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/module_hooks/lazy_module.py +0 -0
  37. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/paginator.py +0 -0
  38. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/py.typed +0 -0
  39. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/rest/__init__.py +0 -0
  40. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/retry.py +0 -0
  41. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/versions/__init__.py +0 -0
  42. {githubkit-0.16.1 → githubkit-0.17.0}/githubkit/webhooks/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GitHubKit
3
- Version: 0.16.1
3
+ Version: 0.17.0
4
4
  Summary: GitHub SDK for Python
5
5
  Keywords: github,octokit
6
6
  Author: yanyongyu
@@ -8,8 +8,8 @@ Author-email: yanyongyu <yyy@yyydl.top>
8
8
  License-Expression: MIT
9
9
  Requires-Dist: anyio>=3.6.1,<5.0.0
10
10
  Requires-Dist: httpx>=0.23.0,<1.0.0
11
- Requires-Dist: hishel[async,httpx]>=1.3.0,<=2.0.0
12
- Requires-Dist: typing-extensions>=4.11.0,<5.0.0
11
+ Requires-Dist: hishel[async,httpx]>=1.4.0,<=2.0.0
12
+ Requires-Dist: typing-extensions>=4.13.0,<5.0.0
13
13
  Requires-Dist: pydantic>=1.9.1,!=2.5.0,!=2.5.1,<3.0.0
14
14
  Requires-Dist: githubkit-schemas>=26.5.7
15
15
  Requires-Dist: pyjwt[crypto]>=2.4.0,<3.0.0 ; extra == 'all'
@@ -0,0 +1,39 @@
1
+ import os
2
+ from typing import TYPE_CHECKING
3
+ import warnings
4
+
5
+ GITHUBKIT_HTTPX2_DISABLE_FLAG = "GITHUBKIT_HTTPX2_DISABLE_FLAG"
6
+
7
+ if TYPE_CHECKING:
8
+ from hishel.httpx2 import AsyncCacheTransport as AsyncCacheTransport
9
+ from hishel.httpx2 import SyncCacheTransport as SyncCacheTransport
10
+ import httpx2 as httpx
11
+ else:
12
+ httpx = None
13
+ if not os.getenv(GITHUBKIT_HTTPX2_DISABLE_FLAG):
14
+ try:
15
+ from hishel.httpx2 import AsyncCacheTransport, SyncCacheTransport
16
+ import httpx2 as httpx
17
+ except ImportError:
18
+ # Hishel wraps a missing HTTP client in ImportError.
19
+ pass
20
+
21
+ if httpx is None:
22
+ try:
23
+ from hishel.httpx import AsyncCacheTransport as AsyncCacheTransport
24
+ from hishel.httpx import SyncCacheTransport as SyncCacheTransport
25
+ import httpx as httpx
26
+ except ImportError:
27
+ raise RuntimeError(
28
+ "GitHubKit requires httpx2 and its Hishel integration "
29
+ "to be installed.\n"
30
+ "You can install them with:\n"
31
+ ' $ pip install httpx2 "hishel[async,httpx2]>=1.4.0"\n'
32
+ ) from None
33
+ else:
34
+ warnings.warn(
35
+ "Using `httpx` with GitHubKit is deprecated; install `httpx2` "
36
+ "and `hishel[async,httpx2]>=1.4.0` instead.",
37
+ DeprecationWarning,
38
+ stacklevel=2,
39
+ )
@@ -1,6 +1,6 @@
1
1
  import re
2
2
 
3
- import httpx
3
+ from githubkit._httpx import httpx
4
4
 
5
5
  APP_ROUTES = {
6
6
  r"/app",
@@ -2,8 +2,7 @@ from collections.abc import Generator
2
2
  import os
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- import httpx
6
-
5
+ from githubkit._httpx import httpx
7
6
  from githubkit.exception import AuthCredentialError
8
7
 
9
8
  from .base import BaseAuthStrategy
@@ -4,14 +4,13 @@ from datetime import datetime, timedelta, timezone
4
4
  from typing import TYPE_CHECKING, ClassVar
5
5
  from typing_extensions import LiteralString
6
6
 
7
- import httpx
8
-
7
+ from githubkit._httpx import httpx
9
8
  from githubkit.compat import model_dump, type_validate_python
10
9
  from githubkit.exception import AuthCredentialError
11
10
  from githubkit.utils import UNSET, Unset, exclude_unset
12
11
 
13
12
  from ._url import require_app_auth, require_basic_auth, require_bypass
14
- from .base import BaseAuthStrategy
13
+ from .base import BaseAuthStrategy, build_request_with_options
15
14
  from .oauth import OAuthAppAuthStrategy
16
15
 
17
16
  try:
@@ -104,7 +103,7 @@ class AppAuth(httpx.Auth):
104
103
  await cache.aset(cache_key, token, timedelta(minutes=8))
105
104
  return token
106
105
 
107
- def _build_installation_auth_request(self) -> httpx.Request:
106
+ def _build_installation_auth_request(self, request: httpx.Request) -> httpx.Request:
108
107
  from githubkit_schemas.latest.models import (
109
108
  AppInstallationsInstallationIdAccessTokensPostBody,
110
109
  )
@@ -130,9 +129,11 @@ class AppAuth(httpx.Auth):
130
129
  },
131
130
  )
132
131
  )
133
- return httpx.Request(
132
+ return build_request_with_options(
133
+ self.github,
134
134
  "POST",
135
135
  url,
136
+ request=request,
136
137
  json=exclude_unset(body),
137
138
  headers={
138
139
  "User-Agent": self.github.config.user_agent,
@@ -207,7 +208,7 @@ class AppAuth(httpx.Auth):
207
208
  cache = self.github.config.cache_strategy.get_cache_storage()
208
209
  key = self._get_installation_cache_key()
209
210
  if not (token := cache.get(key)):
210
- token_request = self._build_installation_auth_request()
211
+ token_request = self._build_installation_auth_request(request)
211
212
  token_request.headers["Authorization"] = f"Bearer {self.get_jwt()}"
212
213
  response = yield token_request
213
214
  response.read()
@@ -249,7 +250,7 @@ class AppAuth(httpx.Auth):
249
250
  cache = await self.github.config.cache_strategy.get_async_cache_storage()
250
251
  key = self._get_installation_cache_key()
251
252
  if not (token := await cache.aget(key)):
252
- token_request = self._build_installation_auth_request()
253
+ token_request = self._build_installation_auth_request(request)
253
254
  token_request.headers["Authorization"] = f"Bearer {await self.aget_jwt()}"
254
255
  response = yield token_request
255
256
  await response.aread()
@@ -0,0 +1,33 @@
1
+ import abc
2
+ from typing import TYPE_CHECKING, Any
3
+
4
+ from githubkit._httpx import httpx
5
+
6
+ if TYPE_CHECKING:
7
+ from githubkit import GitHubCore
8
+
9
+
10
+ class BaseAuthStrategy(abc.ABC):
11
+ @abc.abstractmethod
12
+ def get_auth_flow(self, github: "GitHubCore") -> httpx.Auth:
13
+ raise NotImplementedError
14
+
15
+
16
+ def build_request_with_options(
17
+ github: "GitHubCore",
18
+ method: str,
19
+ url: httpx.URL,
20
+ *,
21
+ request: httpx.Request | None = None,
22
+ **kwargs: Any,
23
+ ) -> httpx.Request:
24
+ """Build a request used inside auth flow, keeping the client timeout.
25
+
26
+ httpx only sets the timeout extension on the request passed to `send`,
27
+ not on requests yielded by an auth flow, so copy it from the original
28
+ request, or fall back to the client config.
29
+ """
30
+ timeout = (
31
+ request.extensions.get("timeout") if request is not None else None
32
+ ) or github.config.timeout.as_dict()
33
+ return httpx.Request(method, url, extensions={"timeout": timeout}, **kwargs)
@@ -5,13 +5,12 @@ from time import sleep
5
5
  from typing import TYPE_CHECKING, Any, ClassVar, TypedDict, cast
6
6
  from typing_extensions import Self
7
7
 
8
- import httpx
9
-
8
+ from githubkit._httpx import httpx
10
9
  from githubkit.exception import AuthCredentialError, AuthExpiredError
11
10
  from githubkit.utils import is_async
12
11
 
13
12
  from ._url import get_oauth_base_url, require_basic_auth, require_bypass
14
- from .base import BaseAuthStrategy
13
+ from .base import BaseAuthStrategy, build_request_with_options
15
14
 
16
15
  try:
17
16
  import anyio
@@ -29,7 +28,10 @@ if TYPE_CHECKING:
29
28
 
30
29
 
31
30
  def create_device_code(
32
- github: "GitHubCore", client_id: str, scopes: Sequence[str] | None = None
31
+ github: "GitHubCore",
32
+ client_id: str,
33
+ scopes: Sequence[str] | None = None,
34
+ request: httpx.Request | None = None,
33
35
  ) -> Generator[httpx.Request, httpx.Response, dict[str, Any]]:
34
36
  """Create a device code for OAuth."""
35
37
  base_url = get_oauth_base_url(github.config.base_url)
@@ -37,9 +39,11 @@ def create_device_code(
37
39
  body = {"client_id": client_id}
38
40
  if scopes:
39
41
  body["scope"] = " ".join(scopes)
40
- response = yield httpx.Request(
42
+ response = yield build_request_with_options(
43
+ github,
41
44
  "POST",
42
45
  url,
46
+ request=request,
43
47
  json=body,
44
48
  headers={
45
49
  "User-Agent": github.config.user_agent,
@@ -56,6 +60,7 @@ def exchange_web_flow_code(
56
60
  client_secret: str,
57
61
  code: str,
58
62
  redirect_uri: str | None = None,
63
+ request: httpx.Request | None = None,
59
64
  ) -> Generator[httpx.Request, httpx.Response, dict[str, Any]]:
60
65
  """Exchange web flow code for token."""
61
66
  base_url = get_oauth_base_url(github.config.base_url)
@@ -67,9 +72,11 @@ def exchange_web_flow_code(
67
72
  }
68
73
  if redirect_uri:
69
74
  body["redirect_uri"] = redirect_uri
70
- response = yield httpx.Request(
75
+ response = yield build_request_with_options(
76
+ github,
71
77
  "POST",
72
78
  url,
79
+ request=request,
73
80
  json=body,
74
81
  headers={
75
82
  "User-Agent": github.config.user_agent,
@@ -81,14 +88,19 @@ def exchange_web_flow_code(
81
88
 
82
89
 
83
90
  def exchange_device_code(
84
- github: "GitHubCore", client_id: str, device_code: str
91
+ github: "GitHubCore",
92
+ client_id: str,
93
+ device_code: str,
94
+ request: httpx.Request | None = None,
85
95
  ) -> Generator[httpx.Request, httpx.Response, dict[str, Any]]:
86
96
  """Exchange device code for token."""
87
97
  base_url = get_oauth_base_url(github.config.base_url)
88
98
  url = base_url.copy_with(raw_path=base_url.raw_path + b"login/oauth/access_token")
89
- response = yield httpx.Request(
99
+ response = yield build_request_with_options(
100
+ github,
90
101
  "POST",
91
102
  url,
103
+ request=request,
92
104
  json={
93
105
  "client_id": client_id,
94
106
  "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
@@ -108,13 +120,16 @@ def refresh_token(
108
120
  client_id: str,
109
121
  client_secret: str | None, # client secret is optional in device flow
110
122
  refresh_token: str,
123
+ request: httpx.Request | None = None,
111
124
  ) -> Generator[httpx.Request, httpx.Response, dict[str, Any]]:
112
125
  """Refresh token."""
113
126
  base_url = get_oauth_base_url(github.config.base_url)
114
127
  url = base_url.copy_with(raw_path=base_url.raw_path + b"login/oauth/access_token")
115
- response = yield httpx.Request(
128
+ response = yield build_request_with_options(
129
+ github,
116
130
  "POST",
117
131
  url,
132
+ request=request,
118
133
  json={
119
134
  "client_id": client_id,
120
135
  "client_secret": client_secret,
@@ -287,6 +302,7 @@ class OAuthTokenAuth(httpx.Auth):
287
302
  self.client_id,
288
303
  self.client_secret,
289
304
  cast(str, self.refresh_token),
305
+ request,
290
306
  )
291
307
  result = _parse_token_exchange_response(data)
292
308
  self.token = token = result["token"]
@@ -343,6 +359,7 @@ class OAuthWebAuth(httpx.Auth):
343
359
  self.client_secret,
344
360
  self.code,
345
361
  self.redirect_uri,
362
+ request,
346
363
  )
347
364
  exchange_request = next(flow)
348
365
  while True:
@@ -374,6 +391,7 @@ class OAuthWebAuth(httpx.Auth):
374
391
  self.client_secret,
375
392
  self.code,
376
393
  self.redirect_uri,
394
+ request,
377
395
  )
378
396
  exchange_request = next(flow)
379
397
  while True:
@@ -442,7 +460,7 @@ class OAuthDeviceAuth(httpx.Auth):
442
460
  # exchange token for the first time
443
461
  if (auth_strategy := self._token_auth_strategy) is None:
444
462
  # create device code
445
- flow = create_device_code(self.github, self.client_id, self.scopes)
463
+ flow = create_device_code(self.github, self.client_id, self.scopes, request)
446
464
  create_request = next(flow)
447
465
  while True:
448
466
  response = yield create_request
@@ -462,7 +480,7 @@ class OAuthDeviceAuth(httpx.Auth):
462
480
  if datetime.now(timezone.utc) > result["expire_time"]:
463
481
  raise AuthExpiredError("Device code expired.")
464
482
  flow = exchange_device_code(
465
- self.github, self.client_id, result["device_code"]
483
+ self.github, self.client_id, result["device_code"], request
466
484
  )
467
485
  auth_request = next(flow)
468
486
  while True:
@@ -503,7 +521,7 @@ class OAuthDeviceAuth(httpx.Auth):
503
521
  # exchange token for the first time
504
522
  if (auth_strategy := self._token_auth_strategy) is None:
505
523
  # create device code
506
- flow = create_device_code(self.github, self.client_id, self.scopes)
524
+ flow = create_device_code(self.github, self.client_id, self.scopes, request)
507
525
  create_request = next(flow)
508
526
  while True:
509
527
  response = yield create_request
@@ -523,7 +541,7 @@ class OAuthDeviceAuth(httpx.Auth):
523
541
  if datetime.now(timezone.utc) > result["expire_time"]:
524
542
  raise AuthExpiredError("Device code expired.")
525
543
  flow = exchange_device_code(
526
- self.github, self.client_id, result["device_code"]
544
+ self.github, self.client_id, result["device_code"], request
527
545
  )
528
546
  auth_request = next(flow)
529
547
  while True:
@@ -2,7 +2,7 @@ from collections.abc import Generator
2
2
  from dataclasses import dataclass
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- import httpx
5
+ from githubkit._httpx import httpx
6
6
 
7
7
  from .base import BaseAuthStrategy
8
8
 
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- import httpx
3
+ from githubkit._httpx import httpx
4
4
 
5
5
  from .base import BaseAuthStrategy
6
6
 
@@ -1,5 +1,6 @@
1
1
  from collections.abc import Callable, Generator
2
- from typing import TYPE_CHECKING, Any, Protocol, TypeVar, overload
2
+ from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast
3
+ from typing_extensions import TypeForm
3
4
 
4
5
  from pydantic import VERSION
5
6
 
@@ -36,24 +37,10 @@ if PYDANTIC_V2: # pragma: pydantic-v2
36
37
  class ExtraGitHubModel(GitHubModel):
37
38
  model_config = ConfigDict(extra="allow")
38
39
 
39
- # Remove the overload once [PEP747](https://peps.python.org/pep-0747/) is accepted
40
- # We should use TypeForm here
41
- @overload
42
- def type_validate_python(type_: type[T], data: Any) -> T: ...
43
-
44
- @overload
45
- def type_validate_python(type_: Any, data: Any) -> Any: ...
46
-
47
- def type_validate_python(type_: type[T], data: Any) -> T:
40
+ def type_validate_python(type_: TypeForm[T], data: Any) -> T:
48
41
  return TypeAdapter(type_).validate_python(data)
49
42
 
50
- @overload
51
- def type_validate_json(type_: type[T], data: Any) -> T: ...
52
-
53
- @overload
54
- def type_validate_json(type_: Any, data: Any) -> Any: ...
55
-
56
- def type_validate_json(type_: type[T], data: Any) -> T:
43
+ def type_validate_json(type_: TypeForm[T], data: Any) -> T:
57
44
  return TypeAdapter(type_).validate_json(data)
58
45
 
59
46
  def model_dump(model: BaseModel, by_alias: bool = True) -> dict[str, Any]:
@@ -97,23 +84,11 @@ else: # pragma: pydantic-v1
97
84
  class Config:
98
85
  extra = Extra.allow
99
86
 
100
- @overload
101
- def type_validate_python(type_: type[T], data: Any) -> T: ...
102
-
103
- @overload
104
- def type_validate_python(type_: Any, data: Any) -> Any: ...
105
-
106
- def type_validate_python(type_: type[T], data: Any) -> T:
107
- return parse_obj_as(type_, data)
108
-
109
- @overload
110
- def type_validate_json(type_: type[T], data: Any) -> T: ...
111
-
112
- @overload
113
- def type_validate_json(type_: Any, data: Any) -> Any: ...
87
+ def type_validate_python(type_: TypeForm[T], data: Any) -> T:
88
+ return parse_obj_as(cast(type[T], type_), data)
114
89
 
115
- def type_validate_json(type_: type[T], data: Any) -> T:
116
- return parse_raw_as(type_, data)
90
+ def type_validate_json(type_: TypeForm[T], data: Any) -> T:
91
+ return parse_raw_as(cast(type[T], type_), data)
117
92
 
118
93
  def to_jsonable_python(obj: Any) -> Any:
119
94
  if isinstance(obj, dict):
@@ -3,7 +3,7 @@ from dataclasses import dataclass, fields
3
3
  from typing import TYPE_CHECKING, Any
4
4
  from typing_extensions import Self
5
5
 
6
- import httpx
6
+ from githubkit._httpx import httpx
7
7
 
8
8
  from .cache import DEFAULT_CACHE_STRATEGY, BaseCacheStrategy
9
9
  from .retry import RETRY_DEFAULT
@@ -5,9 +5,11 @@ from datetime import datetime, timedelta, timezone
5
5
  import time
6
6
  from types import TracebackType
7
7
  from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload
8
+ from typing_extensions import TypeForm
8
9
 
9
10
  import anyio
10
- import httpx
11
+
12
+ from githubkit._httpx import httpx
11
13
 
12
14
  from .auth import BaseAuthStrategy, TokenAuthStrategy, UnauthAuthStrategy
13
15
  from .cache import BaseCacheStrategy
@@ -395,12 +397,16 @@ class GitHubCore(Generic[A]):
395
397
  raise RequestError(e) from e
396
398
 
397
399
  # check and parse response
400
+ def _check_is_error(self, response: httpx.Response) -> bool:
401
+ """Check if the response is an error."""
402
+ return response.is_error
403
+
398
404
  @overload
399
405
  def _check(
400
406
  self,
401
407
  response: httpx.Response,
402
- response_model: type[T],
403
- error_models: Mapping[str, type] | None = None,
408
+ response_model: TypeForm[T],
409
+ error_models: Mapping[str, Any] | None = None,
404
410
  ) -> Response[T]: ...
405
411
 
406
412
  @overload
@@ -408,17 +414,13 @@ class GitHubCore(Generic[A]):
408
414
  self,
409
415
  response: httpx.Response,
410
416
  response_model: UnsetType = UNSET,
411
- error_models: Mapping[str, type] | None = None,
417
+ error_models: Mapping[str, Any] | None = None,
412
418
  ) -> Response[Any]: ...
413
419
 
414
- def _check_is_error(self, response: httpx.Response) -> bool:
415
- """Check if the response is an error."""
416
- return response.is_error
417
-
418
420
  def _check(
419
421
  self,
420
422
  response: httpx.Response,
421
- response_model: type[T] | UnsetType = UNSET,
423
+ response_model: TypeForm[T] | UnsetType = UNSET,
422
424
  error_models: Mapping[str, Any] | None = None,
423
425
  ) -> Response[T] | Response[Any]:
424
426
  if self._check_is_error(response):
@@ -509,7 +511,7 @@ class GitHubCore(Generic[A]):
509
511
  headers: HeaderTypes | None = None,
510
512
  cookies: CookieTypes | None = None,
511
513
  stream: bool = False,
512
- response_model: type[T],
514
+ response_model: TypeForm[T],
513
515
  error_models: Mapping[str, Any] | None = None,
514
516
  ) -> Response[T]: ...
515
517
 
@@ -544,7 +546,7 @@ class GitHubCore(Generic[A]):
544
546
  headers: HeaderTypes | None = None,
545
547
  cookies: CookieTypes | None = None,
546
548
  stream: bool = False,
547
- response_model: type[T] | UnsetType = UNSET,
549
+ response_model: TypeForm[T] | UnsetType = UNSET,
548
550
  error_models: Mapping[str, Any] | None = None,
549
551
  ) -> Response[T] | Response[Any]:
550
552
  """Send a request.
@@ -599,7 +601,7 @@ class GitHubCore(Generic[A]):
599
601
  headers: HeaderTypes | None = None,
600
602
  cookies: CookieTypes | None = None,
601
603
  stream: bool = False,
602
- response_model: type[T],
604
+ response_model: TypeForm[T],
603
605
  error_models: Mapping[str, Any] | None = None,
604
606
  ) -> Response[T]: ...
605
607
 
@@ -634,7 +636,7 @@ class GitHubCore(Generic[A]):
634
636
  headers: HeaderTypes | None = None,
635
637
  cookies: CookieTypes | None = None,
636
638
  stream: bool = False,
637
- response_model: type[T] | UnsetType = UNSET,
639
+ response_model: TypeForm[T] | UnsetType = UNSET,
638
640
  error_models: Mapping[str, Any] | None = None,
639
641
  ) -> Response[T] | Response[Any]:
640
642
  """Asynchronously send a request.
@@ -1,7 +1,7 @@
1
1
  from datetime import timedelta
2
2
  from typing import TYPE_CHECKING, Generic, TypeVar
3
3
 
4
- import httpx
4
+ from ._httpx import httpx
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from .graphql import GraphQLResponse
@@ -14,8 +14,7 @@ from .versions import RestVersionSwitcher, WebhooksVersionSwitcher
14
14
  if TYPE_CHECKING:
15
15
  import ssl
16
16
 
17
- import httpx
18
-
17
+ from ._httpx import httpx
19
18
  from .auth import TokenAuthStrategy, UnauthAuthStrategy
20
19
  from .cache import BaseCacheStrategy
21
20
  from .config import Config
@@ -10,8 +10,8 @@ from hishel import (
10
10
  SyncBaseStorage,
11
11
  SyncSqliteStorage,
12
12
  )
13
- from hishel.httpx import AsyncCacheTransport, SyncCacheTransport
14
- import httpx
13
+
14
+ from ._httpx import AsyncCacheTransport, SyncCacheTransport, httpx
15
15
 
16
16
 
17
17
  class SyncCacheClient(httpx.Client):
@@ -1,9 +1,9 @@
1
1
  from collections.abc import AsyncIterator, Iterator
2
2
  from contextlib import asynccontextmanager, contextmanager
3
3
  from typing import Any, Generic
4
- from typing_extensions import TypeVar
4
+ from typing_extensions import TypeForm, TypeVar
5
5
 
6
- import httpx
6
+ from githubkit._httpx import httpx
7
7
 
8
8
  from .compat import type_validate_json
9
9
  from .exception import RequestError, RequestTimeout
@@ -15,7 +15,7 @@ JT = TypeVar("JT", default=Any) # pyright: ignore[reportGeneralTypeIssues]
15
15
  class Response(Generic[MT, JT]):
16
16
  """A wrapper around httpx.Response that provides data model validation."""
17
17
 
18
- def __init__(self, response: httpx.Response, data_model: type[MT]):
18
+ def __init__(self, response: httpx.Response, data_model: TypeForm[MT]):
19
19
  self._response = response
20
20
  self._data_model = data_model
21
21
  self._cached_parsed_data: MT | None = None
@@ -11,8 +11,7 @@ from typing import (
11
11
  )
12
12
  from typing_extensions import ParamSpec, Self
13
13
 
14
- import httpx
15
-
14
+ from githubkit._httpx import httpx
16
15
  from githubkit.response import Response
17
16
  from githubkit.typing import HeaderTypes
18
17
  from githubkit.utils import is_async
@@ -6,7 +6,8 @@ from typing import Any
6
6
  from typing_extensions import override
7
7
 
8
8
  import anyio
9
- import httpx
9
+
10
+ from githubkit._httpx import httpx
10
11
 
11
12
 
12
13
  class BaseThrottler(abc.ABC):
@@ -11,9 +11,10 @@ from typing import (
11
11
  TypeVar,
12
12
  )
13
13
 
14
- import httpx
15
14
  from pydantic import Field
16
15
 
16
+ from githubkit._httpx import httpx
17
+
17
18
  from .compat import PYDANTIC_V2
18
19
  from .exception import GitHubException
19
20
  from .utils import Unset
@@ -1,7 +1,8 @@
1
1
  from enum import Enum
2
2
  from functools import partial
3
3
  import inspect
4
- from typing import Any, Generic, Literal, TypeVar, final, overload
4
+ from typing import Any, Generic, Literal, TypeVar, final
5
+ from typing_extensions import TypeForm
5
6
 
6
7
  from pydantic import BaseModel
7
8
 
@@ -96,13 +97,7 @@ def parse_query_params(params: dict[str, Any]) -> dict[str, Any]:
96
97
  class TaggedUnion(Generic[T]):
97
98
  __slots__ = ("discriminator", "tag", "type_")
98
99
 
99
- @overload
100
- def __init__(self, type_: type[T], discriminator: str, tag: str) -> None: ...
101
-
102
- @overload
103
- def __init__(self, type_: Any, discriminator: str, tag: str) -> None: ...
104
-
105
- def __init__(self, type_: type[T], discriminator: str, tag: str) -> None:
100
+ def __init__(self, type_: TypeForm[T], discriminator: str, tag: str) -> None:
106
101
  self.type_ = type_
107
102
  self.discriminator = discriminator
108
103
  self.tag = tag
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "GitHubKit"
3
- version = "0.16.1"
3
+ version = "0.17.0"
4
4
  description = "GitHub SDK for Python"
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -12,8 +12,8 @@ requires-python = ">=3.10, <4.0"
12
12
  dependencies = [
13
13
  "anyio >=3.6.1, <5.0.0",
14
14
  "httpx >=0.23.0, <1.0.0",
15
- "hishel[async,httpx] >=1.3.0, <=2.0.0",
16
- "typing-extensions >=4.11.0, <5.0.0",
15
+ "hishel[async,httpx] >=1.4.0, <=2.0.0",
16
+ "typing-extensions >=4.13.0, <5.0.0",
17
17
  "pydantic >=1.9.1, <3.0.0, !=2.5.0, !=2.5.1",
18
18
  "githubkit-schemas >=26.5.7",
19
19
  ]
@@ -46,7 +46,7 @@ dev = [
46
46
  { include-group = "codegen" },
47
47
  { include-group = "test" },
48
48
  { include-group = "docs" },
49
- "ruff >=0.16.0, <0.17.0",
49
+ "ruff >=0.17.0, <0.18.0",
50
50
  "nonemoji >=0.1.2, <0.2.0",
51
51
  "pre-commit >=4.0.0, <5.0.0",
52
52
  ]
@@ -55,9 +55,11 @@ codegen = [
55
55
  "tomlkit >=0.13.3, <0.16.0",
56
56
  "jsonpointer >=3.0.0, <4.0.0",
57
57
  "openapi-pydantic >=0.5.0, <0.6.0",
58
+ "httpx2 >=2.13.1, <3.0.0",
58
59
  ]
59
60
  test = [
60
61
  { include-group = "thirdparty" },
62
+ "httpx2 >=2.13.1, <3.0.0",
61
63
  "pytest >=8.1.0, <10.0.0",
62
64
  "pytest-cov >=7.0.0, <8.0.0",
63
65
  "pytest-xdist >=3.5.0, <4.0.0",
@@ -177,9 +179,9 @@ keep-runtime-typing = true
177
179
  [tool.pyright]
178
180
  typeCheckingMode = "standard"
179
181
  reportPrivateImportUsage = false
180
- reportShadowedImports = false
181
182
  disableBytesTypePromotions = true
182
183
  pythonPlatform = "All"
184
+ pythonVersion = "3.10"
183
185
 
184
186
  [tool.pyright.defineConstant]
185
187
  PYDANTIC_V2 = true
@@ -188,6 +190,7 @@ PYDANTIC_V2 = true
188
190
  root = "packages/**/githubkit_schemas/"
189
191
  pythonVersion = "3.10"
190
192
  reportOverlappingOverload = false
193
+ reportIncompatibleMethodOverride = false
191
194
 
192
195
  [[tool.pyright.executionEnvironments]]
193
196
  root = "codegen"
@@ -401,6 +404,12 @@ type = [
401
404
  "null",
402
405
  ]
403
406
 
407
+ [tool.codegen.overrides.schema_overrides."/components/schemas/pull-request-stack/properties/base/properties/sha"]
408
+ type = [
409
+ "string",
410
+ "null",
411
+ ]
412
+
404
413
  [tool.codegen.overrides.schema_overrides."/components/schemas/repository/properties/temp_clone_token"]
405
414
  type = [
406
415
  "string",
@@ -560,23 +569,24 @@ type = [
560
569
  [tool.codegen.overrides.schema_overrides."/components/schemas/app-permissions/properties/organization_copilot_seat_management/enum"]
561
570
  "<add>" = ["read"]
562
571
 
563
- [tool.codegen.overrides.schema_overrides."/components/parameters/created/schema"]
564
- format = "<unset>"
572
+ [[tool.codegen.overrides.schema_overrides."/components/schemas/budget/properties/budget_type/oneOf"."<add>"]]
573
+ type = "string"
574
+ enum = ["BundlePricing"]
565
575
 
566
- [tool.codegen.overrides.schema_overrides."/components/schemas/simple-classroom-assignment/properties/editor"]
567
- type = [
568
- "string",
569
- "null",
570
- ]
576
+ [[tool.codegen.overrides.schema_overrides."/components/schemas/create-budget/properties/budget/properties/budget_type/oneOf"."<add>"]]
577
+ type = "string"
578
+ enum = ["BundlePricing"]
571
579
 
572
- [tool.codegen.overrides.schema_overrides."/components/schemas/simple-classroom-assignment/properties/language"]
573
- type = [
574
- "string",
575
- "null",
576
- ]
580
+ [[tool.codegen.overrides.schema_overrides."/components/schemas/get-budget/properties/budget_type/oneOf"."<add>"]]
581
+ type = "string"
582
+ enum = ["BundlePricing"]
577
583
 
578
- [tool.codegen.overrides.schema_overrides."/components/schemas/simple-classroom-assignment/required"]
579
- "<remove>" = ["submitted"]
584
+ [[tool.codegen.overrides.schema_overrides."/components/schemas/update-budget/properties/budget/properties/budget_type/oneOf"."<add>"]]
585
+ type = "string"
586
+ enum = ["BundlePricing"]
587
+
588
+ [tool.codegen.overrides.schema_overrides."/components/parameters/created/schema"]
589
+ format = "<unset>"
580
590
 
581
591
  [tool.codegen.overrides.schema_overrides."/paths/~1repos~1{owner}~1{repo}~1check-runs/post/requestBody/content/application~1json/schema"]
582
592
  discriminator = "<unset>"
@@ -603,6 +613,9 @@ discriminator = "<unset>"
603
613
  [tool.codegen.overrides.schema_overrides."/paths/~1users~1{username}/get/responses/200/content/application~1json/schema"]
604
614
  discriminator = "<unset>"
605
615
 
616
+ [tool.codegen.overrides.schema_overrides."/components/schemas/rule-suite/properties/rule_evaluations/items/properties/metadata"]
617
+ discriminator = "<unset>"
618
+
606
619
  [tool.codegen.overrides.schema_overrides."/components/schemas/webhook-check-run-completed/required"]
607
620
  "<add>" = ["action"]
608
621
 
@@ -698,6 +711,30 @@ type = "string"
698
711
  type = "object"
699
712
  additionalProperties = true
700
713
 
714
+ [tool.codegen.overrides.schema_overrides."/components/schemas/webhooks_label/properties/archived_by"]
715
+ type = [
716
+ "null",
717
+ "object",
718
+ ]
719
+
720
+ [tool.codegen.overrides.schema_overrides."/components/schemas/webhooks_label_unarchived/allOf/1/properties/archived_by"]
721
+ type = [
722
+ "null",
723
+ "object",
724
+ ]
725
+
726
+ [tool.codegen.overrides.schema_overrides."/components/schemas/webhook-issues-labeled/properties/issue/properties/labels/items/properties/archived_by"]
727
+ type = [
728
+ "null",
729
+ "object",
730
+ ]
731
+
732
+ [tool.codegen.overrides.schema_overrides."/components/schemas/webhook-pull-request-labeled/properties/pull_request/properties/labels/items/properties/archived_by"]
733
+ type = [
734
+ "null",
735
+ "object",
736
+ ]
737
+
701
738
  [tool.codegen.overrides.schema_overrides."/components/schemas/webhook-deployment-protection-rule-requested"]
702
739
  required = ["action"]
703
740
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "GitHubKit"
3
- version = "0.16.1"
3
+ version = "0.17.0"
4
4
  description = "GitHub SDK for Python"
5
5
  authors = [{ name = "yanyongyu", email = "yyy@yyydl.top" }]
6
6
  license = "MIT"
@@ -10,8 +10,8 @@ requires-python = ">=3.10, <4.0"
10
10
  dependencies = [
11
11
  "anyio >=3.6.1, <5.0.0",
12
12
  "httpx >=0.23.0, <1.0.0",
13
- "hishel[async,httpx] >=1.3.0, <=2.0.0",
14
- "typing-extensions >=4.11.0, <5.0.0",
13
+ "hishel[async,httpx] >=1.4.0, <=2.0.0",
14
+ "typing-extensions >=4.13.0, <5.0.0",
15
15
  "pydantic >=1.9.1, <3.0.0, !=2.5.0, !=2.5.1",
16
16
  "githubkit-schemas >=26.5.7",
17
17
  ]
@@ -37,7 +37,7 @@ dev = [
37
37
  { include-group = "codegen" },
38
38
  { include-group = "test" },
39
39
  { include-group = "docs" },
40
- "ruff >=0.16.0, <0.17.0",
40
+ "ruff >=0.17.0, <0.18.0",
41
41
  "nonemoji >=0.1.2, <0.2.0",
42
42
  "pre-commit >=4.0.0, <5.0.0",
43
43
  ]
@@ -46,9 +46,11 @@ codegen = [
46
46
  "tomlkit >=0.13.3, <0.16.0",
47
47
  "jsonpointer >=3.0.0, <4.0.0",
48
48
  "openapi-pydantic >=0.5.0, <0.6.0",
49
+ "httpx2 >=2.13.1, <3.0.0",
49
50
  ]
50
51
  test = [
51
52
  { include-group = "thirdparty" },
53
+ "httpx2 >=2.13.1, <3.0.0", # exercise the opt-in backend alongside httpx
52
54
  "pytest >=8.1.0, <10.0.0",
53
55
  "pytest-cov >=7.0.0, <8.0.0",
54
56
  "pytest-xdist >=3.5.0, <4.0.0",
@@ -161,15 +163,15 @@ keep-runtime-typing = true
161
163
  [tool.pyright]
162
164
  typeCheckingMode = "standard"
163
165
  reportPrivateImportUsage = false
164
- reportShadowedImports = false
165
166
  disableBytesTypePromotions = true
166
167
 
167
168
  pythonPlatform = "All"
169
+ pythonVersion = "3.10"
168
170
  defineConstant = { PYDANTIC_V2 = true }
169
171
 
170
172
  executionEnvironments = [
171
- # disable overload check for generated version codes
172
- { root = "packages/**/githubkit_schemas/", pythonVersion = "3.10", reportOverlappingOverload = false },
173
+ # disable overload and override checks for generated version codes
174
+ { root = "packages/**/githubkit_schemas/", pythonVersion = "3.10", reportOverlappingOverload = false, reportIncompatibleMethodOverride = false },
173
175
  # codegen env config for dev
174
176
  { root = "codegen", pythonVersion = "3.10" },
175
177
  { root = ".", pythonVersion = "3.10" },
@@ -313,6 +315,12 @@ target_descriptions = ["ghec-2022-11-28", "ghec-2026-03-10"]
313
315
  "null",
314
316
  ] }
315
317
 
318
+ # https://github.com/yanyongyu/githubkit/issues/326
319
+ "/components/schemas/pull-request-stack/properties/base/properties/sha" = { type = [
320
+ "string",
321
+ "null",
322
+ ] }
323
+
316
324
  # https://github.com/github/rest-api-description/issues/621
317
325
  "/components/schemas/repository/properties/temp_clone_token" = { type = [
318
326
  "string",
@@ -465,22 +473,46 @@ target_descriptions = ["ghec-2022-11-28", "ghec-2026-03-10"]
465
473
  "read",
466
474
  ] }
467
475
 
476
+ # https://github.com/yanyongyu/githubkit/issues/322
477
+ # budget type can be "BundlePricing"
478
+ "/components/schemas/budget/properties/budget_type/oneOf" = { "<add>" = [
479
+ { "type" = "string", "enum" = [
480
+ "BundlePricing",
481
+ ] },
482
+ ] }
483
+ "/components/schemas/create-budget/properties/budget/properties/budget_type/oneOf" = { "<add>" = [
484
+ { "type" = "string", "enum" = [
485
+ "BundlePricing",
486
+ ] },
487
+ ] }
488
+ "/components/schemas/get-budget/properties/budget_type/oneOf" = { "<add>" = [
489
+ { "type" = "string", "enum" = [
490
+ "BundlePricing",
491
+ ] },
492
+ ] }
493
+ "/components/schemas/update-budget/properties/budget/properties/budget_type/oneOf" = { "<add>" = [
494
+ { "type" = "string", "enum" = [
495
+ "BundlePricing",
496
+ ] },
497
+ ] }
498
+
468
499
  # date-time search syntax should be string
469
500
  # https://docs.github.com/search-github/getting-started-with-searching-on-github/understanding-the-search-syntax#query-for-dates
470
501
  "/components/parameters/created/schema" = { format = "<unset>" }
471
502
 
472
503
  # https://github.com/yanyongyu/githubkit/issues/144
473
- "/components/schemas/simple-classroom-assignment/properties/editor" = { type = [
474
- "string",
475
- "null",
476
- ] }
477
- "/components/schemas/simple-classroom-assignment/properties/language" = { type = [
478
- "string",
479
- "null",
480
- ] }
481
- "/components/schemas/simple-classroom-assignment/required" = { "<remove>" = [
482
- "submitted",
483
- ] }
504
+ # [REMOVED] github classroom closed
505
+ # "/components/schemas/simple-classroom-assignment/properties/editor" = { type = [
506
+ # "string",
507
+ # "null",
508
+ # ] }
509
+ # "/components/schemas/simple-classroom-assignment/properties/language" = { type = [
510
+ # "string",
511
+ # "null",
512
+ # ] }
513
+ # "/components/schemas/simple-classroom-assignment/required" = { "<remove>" = [
514
+ # "submitted",
515
+ # ] }
484
516
 
485
517
  # https://github.com/yanyongyu/githubkit/issues/148
486
518
  "/paths/~1repos~1{owner}~1{repo}~1check-runs/post/requestBody/content/application~1json/schema" = { discriminator = "<unset>" }
@@ -506,6 +538,9 @@ target_descriptions = ["ghec-2022-11-28", "ghec-2026-03-10"]
506
538
  # get user by username response discriminator not supported
507
539
  "/paths/~1users~1{username}/get/responses/200/content/application~1json/schema" = { discriminator = "<unset>" }
508
540
 
541
+ # rule suite rule metadata type discriminator not supported
542
+ "/components/schemas/rule-suite/properties/rule_evaluations/items/properties/metadata" = { discriminator = "<unset>" }
543
+
509
544
  # Webhook schema overrides
510
545
  # webhook check run action is required
511
546
  "/components/schemas/webhook-check-run-completed/required" = { "<add>" = [
@@ -555,6 +590,24 @@ target_descriptions = ["ghec-2022-11-28", "ghec-2026-03-10"]
555
590
  { type = "object", additionalProperties = true },
556
591
  ] }
557
592
 
593
+ # webhook label archived_by user should be null or object
594
+ "/components/schemas/webhooks_label/properties/archived_by" = { type = [
595
+ "null",
596
+ "object",
597
+ ] }
598
+ "/components/schemas/webhooks_label_unarchived/allOf/1/properties/archived_by" = { type = [
599
+ "null",
600
+ "object",
601
+ ] }
602
+ "/components/schemas/webhook-issues-labeled/properties/issue/properties/labels/items/properties/archived_by" = { type = [
603
+ "null",
604
+ "object",
605
+ ] }
606
+ "/components/schemas/webhook-pull-request-labeled/properties/pull_request/properties/labels/items/properties/archived_by" = { type = [
607
+ "null",
608
+ "object",
609
+ ] }
610
+
558
611
  # webhook deployment protection rule action is required
559
612
  "/components/schemas/webhook-deployment-protection-rule-requested" = { required = [
560
613
  "action",
@@ -1,13 +0,0 @@
1
- import abc
2
- from typing import TYPE_CHECKING
3
-
4
- import httpx
5
-
6
- if TYPE_CHECKING:
7
- from githubkit import GitHubCore
8
-
9
-
10
- class BaseAuthStrategy(abc.ABC):
11
- @abc.abstractmethod
12
- def get_auth_flow(self, github: "GitHubCore") -> httpx.Auth:
13
- raise NotImplementedError
File without changes
File without changes