Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/pyright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ on:
- "uv.lock"
- "packages/**/*.py"
- "packages/**/pyproject.toml"
- "packages/pyrightconfig.json"

jobs:
lint-githubkit:
Expand Down Expand Up @@ -51,6 +52,14 @@ jobs:
env:
NODE_OPTIONS: --max-old-space-size=12288

- name: Run Pyright on generated schemas
uses: jakebailey/pyright-action@v3
with:
working-directory: packages
pylance-version: latest-release
env:
NODE_OPTIONS: --max-old-space-size=12288

lint-codegen:
name: Codegen Lint
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ poetry.toml
.ruff_cache/

# LSP config files
pyrightconfig.json
# pyrightconfig.json

### VisualStudioCode ###
.vscode/*
Expand Down
7 changes: 5 additions & 2 deletions codegen/parser/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import builtins
from keyword import iskeyword
import re
from typing import TYPE_CHECKING, Any, TypeVar
from typing import TYPE_CHECKING, Any, TypeVar, cast
from typing_extensions import TypeForm

import openapi_pydantic as oas
from pydantic import TypeAdapter
Expand Down Expand Up @@ -173,6 +174,8 @@ def type_ref_from_source(source: "Source", type_: type[T]) -> oas.Reference | T:
data = source.data

try:
return TypeAdapter(oas.Reference | type_).validate_python(data)
return TypeAdapter(
cast(TypeForm[oas.Reference | T], oas.Reference | type_)
).validate_python(data)
except Exception as e:
raise TypeError(f"Invalid {type_} from {source.uri}") from e
41 changes: 8 additions & 33 deletions githubkit/compat.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import Callable, Generator
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, overload
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast
from typing_extensions import TypeForm

from pydantic import VERSION

Expand Down Expand Up @@ -36,24 +37,10 @@ class GitHubModel(BaseModel):
class ExtraGitHubModel(GitHubModel):
model_config = ConfigDict(extra="allow")

# Remove the overload once [PEP747](https://peps.python.org/pep-0747/) is accepted
Comment thread
yanyongyu marked this conversation as resolved.
Resolved
# We should use TypeForm here
@overload
def type_validate_python(type_: type[T], data: Any) -> T: ...

@overload
def type_validate_python(type_: Any, data: Any) -> Any: ...

def type_validate_python(type_: type[T], data: Any) -> T:
def type_validate_python(type_: TypeForm[T], data: Any) -> T:
return TypeAdapter(type_).validate_python(data)

@overload
Comment thread
yanyongyu marked this conversation as resolved.
Resolved
def type_validate_json(type_: type[T], data: Any) -> T: ...

@overload
def type_validate_json(type_: Any, data: Any) -> Any: ...

def type_validate_json(type_: type[T], data: Any) -> T:
def type_validate_json(type_: TypeForm[T], data: Any) -> T:
return TypeAdapter(type_).validate_json(data)

def model_dump(model: BaseModel, by_alias: bool = True) -> dict[str, Any]:
Expand Down Expand Up @@ -97,23 +84,11 @@ class ExtraGitHubModel(BaseModel):
class Config:
extra = Extra.allow

@overload
def type_validate_python(type_: type[T], data: Any) -> T: ...

@overload
def type_validate_python(type_: Any, data: Any) -> Any: ...

def type_validate_python(type_: type[T], data: Any) -> T:
return parse_obj_as(type_, data)

@overload
def type_validate_json(type_: type[T], data: Any) -> T: ...

@overload
def type_validate_json(type_: Any, data: Any) -> Any: ...
def type_validate_python(type_: TypeForm[T], data: Any) -> T:
return parse_obj_as(cast(type[T], type_), data)

def type_validate_json(type_: type[T], data: Any) -> T:
return parse_raw_as(type_, data)
def type_validate_json(type_: TypeForm[T], data: Any) -> T:
return parse_raw_as(cast(type[T], type_), data)

def to_jsonable_python(obj: Any) -> Any:
if isinstance(obj, dict):
Expand Down
25 changes: 13 additions & 12 deletions githubkit/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import time
from types import TracebackType
from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload
from typing_extensions import TypeForm

import anyio

Expand Down Expand Up @@ -396,30 +397,30 @@ async def _arequest(
raise RequestError(e) from e

# check and parse response
def _check_is_error(self, response: httpx.Response) -> bool:
"""Check if the response is an error."""
return response.is_error

@overload
def _check(
self,
response: httpx.Response,
response_model: type[T],
error_models: Mapping[str, type] | None = None,
response_model: TypeForm[T],
error_models: Mapping[str, Any] | None = None,
) -> Response[T]: ...

@overload
def _check(
self,
response: httpx.Response,
response_model: UnsetType = UNSET,
error_models: Mapping[str, type] | None = None,
error_models: Mapping[str, Any] | None = None,
) -> Response[Any]: ...

def _check_is_error(self, response: httpx.Response) -> bool:
"""Check if the response is an error."""
return response.is_error

def _check(
self,
response: httpx.Response,
response_model: type[T] | UnsetType = UNSET,
response_model: TypeForm[T] | UnsetType = UNSET,
error_models: Mapping[str, Any] | None = None,
) -> Response[T] | Response[Any]:
if self._check_is_error(response):
Expand Down Expand Up @@ -510,7 +511,7 @@ def request(
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
stream: bool = False,
response_model: type[T],
response_model: TypeForm[T],
error_models: Mapping[str, Any] | None = None,
) -> Response[T]: ...

Expand Down Expand Up @@ -545,7 +546,7 @@ def request(
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
stream: bool = False,
response_model: type[T] | UnsetType = UNSET,
response_model: TypeForm[T] | UnsetType = UNSET,
error_models: Mapping[str, Any] | None = None,
) -> Response[T] | Response[Any]:
"""Send a request.
Expand Down Expand Up @@ -600,7 +601,7 @@ async def arequest(
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
stream: bool = False,
response_model: type[T],
response_model: TypeForm[T],
error_models: Mapping[str, Any] | None = None,
) -> Response[T]: ...

Expand Down Expand Up @@ -635,7 +636,7 @@ async def arequest(
headers: HeaderTypes | None = None,
cookies: CookieTypes | None = None,
stream: bool = False,
response_model: type[T] | UnsetType = UNSET,
response_model: TypeForm[T] | UnsetType = UNSET,
error_models: Mapping[str, Any] | None = None,
) -> Response[T] | Response[Any]:
"""Asynchronously send a request.
Expand Down
4 changes: 2 additions & 2 deletions githubkit/response.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from collections.abc import AsyncIterator, Iterator
from contextlib import asynccontextmanager, contextmanager
from typing import Any, Generic
from typing_extensions import TypeVar
from typing_extensions import TypeForm, TypeVar

from githubkit._httpx import httpx

Expand All @@ -15,7 +15,7 @@
class Response(Generic[MT, JT]):
"""A wrapper around httpx.Response that provides data model validation."""

def __init__(self, response: httpx.Response, data_model: type[MT]):
def __init__(self, response: httpx.Response, data_model: TypeForm[MT]):
self._response = response
self._data_model = data_model
self._cached_parsed_data: MT | None = None
Expand Down
11 changes: 3 additions & 8 deletions githubkit/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from enum import Enum
from functools import partial
import inspect
from typing import Any, Generic, Literal, TypeVar, final, overload
from typing import Any, Generic, Literal, TypeVar, final
from typing_extensions import TypeForm

from pydantic import BaseModel

Expand Down Expand Up @@ -96,13 +97,7 @@ def parse_query_params(params: dict[str, Any]) -> dict[str, Any]:
class TaggedUnion(Generic[T]):
__slots__ = ("discriminator", "tag", "type_")

@overload
def __init__(self, type_: type[T], discriminator: str, tag: str) -> None: ...

@overload
def __init__(self, type_: Any, discriminator: str, tag: str) -> None: ...

def __init__(self, type_: type[T], discriminator: str, tag: str) -> None:
def __init__(self, type_: TypeForm[T], discriminator: str, tag: str) -> None:
self.type_ = type_
self.discriminator = discriminator
self.tag = tag
Expand Down
5 changes: 5 additions & 0 deletions packages/pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../pyproject.toml",
"reportOverlappingOverload": false,
"reportIncompatibleMethodOverride": false
}
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ dependencies = [
"anyio >=3.6.1, <5.0.0",
"httpx >=0.23.0, <1.0.0",
"hishel[async,httpx] >=1.4.0, <=2.0.0",
"typing-extensions >=4.11.0, <5.0.0",
"typing-extensions >=4.13.0, <5.0.0",
"pydantic >=1.9.1, <3.0.0, !=2.5.0, !=2.5.1",
"githubkit-schemas >=26.5.7",
]
Expand Down Expand Up @@ -163,15 +163,15 @@ keep-runtime-typing = true
[tool.pyright]
typeCheckingMode = "standard"
reportPrivateImportUsage = false
reportShadowedImports = false
disableBytesTypePromotions = true

pythonPlatform = "All"
pythonVersion = "3.10"
defineConstant = { PYDANTIC_V2 = true }

executionEnvironments = [
# disable overload check for generated version codes
{ root = "packages/**/githubkit_schemas/", pythonVersion = "3.10", reportOverlappingOverload = false },
# disable overload and override checks for generated version codes
{ root = "packages/**/githubkit_schemas/", pythonVersion = "3.10", reportOverlappingOverload = false, reportIncompatibleMethodOverride = false },
# codegen env config for dev
{ root = "codegen", pythonVersion = "3.10" },
{ root = ".", pythonVersion = "3.10" },
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading