Code Monkey home page Code Monkey logo

githubkit's Introduction

Typing SVG

  • ๐ŸŒฑ Iโ€™m currently learning: NLP, Rust, Reverse, Cyber Security
  • ๐Ÿ‘ฏ Iโ€™m collaborating on: NoneBot, QQ Bot
  • ๐Ÿ’ฌ Ask me about: NoneBot, CAI

๐ŸŒŸ My Skills

Code Time

๐Ÿ“Š This Week I Spent My Time On

๐Ÿ•‘๏ธŽ Time Zone: Asia/Shanghai

๐Ÿ’ฌ Programming Languages: 
Python                   14 hrs 2 mins       โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   46.31 % 
Other                    11 hrs 58 mins      โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   39.51 % 
TOML                     40 mins             โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   02.21 % 
Go                       39 mins             โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   02.16 % 
Markdown                 37 mins             โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   02.05 % 

๐Ÿ”ฅ Editors: 
VS Code                  18 hrs 59 mins      โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   62.66 % 
Chrome                   11 hrs 19 mins      โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   37.34 % 

๐Ÿฑโ€๐Ÿ’ป Projects: 
PRIVATE PROJECT          16 hrs 43 mins      โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   55.19 % 
nonebot2                 3 hrs 42 mins       โ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   12.24 % 
QQ-GitHub-Bot            1 hr 29 mins        โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   04.93 % 
plugin-localstore        1 hr 28 mins        โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   04.84 % 
GMAT                     1 hr 15 mins        โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   04.17 % 

๐Ÿ’ป Operating System: 
Linux                    18 hrs 59 mins      โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   62.66 % 
Mac                      11 hrs 19 mins      โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   37.34 % 

I Mostly Code in Python

Python                   57 repos            โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   67.06 % 
Vue                      5 repos             โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   05.88 % 
TypeScript               4 repos             โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   04.71 % 
Shell                    3 repos             โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   03.53 % 
Jupyter Notebook         1 repo              โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   01.18 % 

Last Updated on 09/08/2024 01:37:30 UTC

githubkit's People

Contributors

dependabot[bot] avatar dosisod avatar eric-nguyen-cs avatar frankie567 avatar iyume avatar jecluis avatar karpetrosyan avatar netomi avatar pre-commit-ci[bot] avatar schelv avatar sudosubin avatar udangel-r7 avatar yanyongyu avatar zegl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

githubkit's Issues

Bug: duplicate argument 'name' in path and body

Minimal reproduction:

from githubkit import GitHub, TokenAuthStrategy
gh = GitHub(TokenAuthStrategy("")).rest

Error:

Traceback (most recent call last):

  File "/usr/local/lib/python3.8/dist-packages/IPython/core/interactiveshell.py", line 3326, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)

  File ["<ipython-input-3-4ec18c08ad11>"](https://localhost:8080/#), line 1, in <module>
    from githubkit import GitHub, TokenAuthStrategy

  File "/usr/local/lib/python3.8/dist-packages/githubkit/__init__.py", line 2, in <module>
    from .github import GitHub as GitHub

  File "/usr/local/lib/python3.8/dist-packages/githubkit/github.py", line 16, in <module>
    from .core import GitHubCore

  File "/usr/local/lib/python3.8/dist-packages/githubkit/core.py", line 24, in <module>
    from .auth import BaseAuthStrategy, TokenAuthStrategy, UnauthAuthStrategy

  File "/usr/local/lib/python3.8/dist-packages/githubkit/auth/__init__.py", line 1, in <module>
    from .app import AppAuthStrategy as AppAuthStrategy

  File "/usr/local/lib/python3.8/dist-packages/githubkit/auth/app.py", line 10, in <module>
    from githubkit.rest import (

  File "/usr/local/lib/python3.8/dist-packages/githubkit/rest/__init__.py", line 25, in <module>
    from .actions import ActionsClient

  File "/usr/local/lib/python3.8/dist-packages/githubkit/rest/actions.py", line 2189
    def update_org_variable(
    ^
SyntaxError: duplicate argument 'name' in function definition

Not a python expert, but the error seems to be here:

name: Union[Unset, str] = UNSET,

name: Union[Unset, str] = UNSET,

Same possible issue in the async version of the signature. Issue is not present in 0.9.4

Bug: Webhooks `InstallationCreated` field `requester` schema error

Hi,

First of all: Thanks for an amazing effort and contribution creating this library! I had the same idea a few weeks ago before I fortunately stumbled over this one <3

Issue

  • Receiving a Webhook of type installation.created
  • requester is null (None) in the webhook received
  • Pydantic throws a validation error that requester cannot be None

Proposal

Change template to expand Unset to Unset, None within the Union type definitions and set None as the default? Or change the validator to check for is None as well UNSET?

Bug: `pull request simple` field `labels description` schema incorrect

@yanyongyu There are still some other failures in the schema after #6 fixed. Let me show you the completed code snippet I used:

g = GitHub(TokenAuthStrategy(<My-Personal-Token>))
prs = g.paginate(g.rest.pulls.list, owner='apache', repo='pulsar', state='open')

origins = []
for pr in prs:
    pr: PullRequestSimple
    requested_reviewers = pr.requested_reviewers
    for reviewers in requested_reviewers:
        origins.append((reviewers.login, pr.html_url))

gives:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/lib/python3.10/site-packages/githubkit/paginator.py", line 76, in __next__
    contents = self._get_next_page()
  File "/opt/homebrew/lib/python3.10/site-packages/githubkit/paginator.py", line 112, in _get_next_page
    cast(Response[List[RT]], response).parsed_data
  File "/opt/homebrew/lib/python3.10/site-packages/githubkit/response.py", line 50, in parsed_data
    return parse_raw_as(self._data_model, self._response.content)
  File "pydantic/tools.py", line 82, in pydantic.tools.parse_raw_as
  File "pydantic/tools.py", line 38, in pydantic.tools.parse_obj_as
  File "pydantic/main.py", line 342, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 58 validation errors for ParsingModel[List[githubkit.rest.models.PullRequestSimple]]
__root__ -> 37 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 38 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 40 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 41 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 42 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 43 -> labels -> 1 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 44 -> labels -> 5 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 45 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 46 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 47 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 48 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 49 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 50 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 51 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 52 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 53 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 54 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 55 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 56 -> labels -> 4 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 57 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 58 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 59 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 60 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 61 -> labels -> 1 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 62 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 63 -> labels -> 4 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 64 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 65 -> labels -> 1 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 66 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 67 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 68 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 69 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 70 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 71 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 72 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 73 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 74 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 75 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 76 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 77 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 78 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 79 -> labels -> 4 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 80 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 81 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 82 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 83 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 84 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 85 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 86 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 88 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 89 -> labels -> 1 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 90 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 91 -> labels -> 1 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 92 -> labels -> 3 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 94 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 96 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 97 -> labels -> 2 -> description
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 99 -> labels -> 1 -> description
  none is not an allowed value (type=type_error.none.not_allowed)

Bug: `TeamOrganization` company and email may be null

Any time I try to access the parsed_data field on various responses, I receive a pydantic validation error, I believe because the org data is missing a value for company and email. These values are null in the REST API response for orgs/<org_name>.

For example:

test_team_resp = ghkapi.rest.teams.get_by_name(org=ORG_NAME, team_slug="DevSecOps")
test_team = resp.parsed_data

Gives:

pydantic.error_wrappers.ValidationError: 2 validation errors for ParsingModel[TeamFull]
__root__ -> organization -> company
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> organization -> email
  none is not an allowed value (type=type_error.none.not_allowed)

Release Pre-Release to install pydantic 2>

Current release does not support pydantic v2 and above, I already updated my projects to version 2 and above and have to install the pre release of githubkit explicitly.

GitHubKit: Hello World!

This issue is created by GitHubKit.

่ฟ™ไธช issue ็”ฑ GitHubKit ๅˆ›ๅปบใ€‚

The source code for creating this issue:

import re
import asyncio
import inspect

from githubkit import GitHub

_current_module = inspect.getmodule(inspect.currentframe())
assert _current_module
_file_content = re.sub(
    r"GitHub\(\"\w+?\"\)", f"GitHub(\"{'*' * 20}\")", inspect.getsource(_current_module)
)
ISSUE_BODY = f"""
This issue is created by GitHubKit.

่ฟ™ไธช issue ็”ฑ GitHubKit ๅˆ›ๅปบใ€‚

The source code for creating this issue:

```python
{_file_content}
```
"""

OWNER = "yanyongyu"
REPO = "githubkit"


async def test():
    async with GitHub("********************") as github:
        resp = await github.rest.issues.async_create(
            OWNER, REPO, title="GitHubKit: Hello World!", body=ISSUE_BODY
        )
        issue = resp.parsed_data
        await github.rest.issues.async_lock(OWNER, REPO, issue.number, lock_reason="resolved")
        await github.rest.issues.async_update(OWNER, REPO, issue.number, state="closed")


if __name__ == "__main__":
    asyncio.run(test())

Pydantic V2 - Error when parsing a `pull_request` webhook event

I'm currently trying the latest alpha with Pydantic V2 and I encounter an error when trying to parse a pull_request webhook event.

Here is a reproducible example:

from githubkit.webhooks import parse_obj

payload = {
    "action": "opened",
    "assignee": None,
    "number": 1,
    "pull_request": {},
    "repository": {},
    "sender": {},
}

event = parse_obj("pull_request", payload)

Expected behavior

The payload should be parsed.

Actual behavior

Pydantic raises a TypeError, complaining that the discrimininator action is mapped to multiple choices.

Stack trace
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/githubkit/webhooks/parse.py", line 27, in parse_obj
    return TypeAdapter(webhook_event_types[name]).validate_python(payload)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/type_adapter.py", line 243, in __init__
    core_schema = _discriminated_union.apply_discriminators(_core_utils.simplify_schema_references(core_schema))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_discriminated_union.py", line 57, in apply_discriminators
    return simplify_schema_references(_core_utils.walk_core_schema(schema, inner))
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_core_utils.py", line 426, in walk_core_schema
    return f(schema.copy(), _dispatch)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_discriminated_union.py", line 45, in inner
    s = recurse(s, inner)
        ^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_core_utils.py", line 202, in walk
    return f(schema, self._walk)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_discriminated_union.py", line 45, in inner
    s = recurse(s, inner)
        ^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_core_utils.py", line 205, in _walk
    schema = self._schema_type_to_method[schema['type']](schema.copy(), f)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_core_utils.py", line 235, in handle_definitions_schema
    new_inner_schema = self.walk(schema['schema'], f)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_core_utils.py", line 202, in walk
    return f(schema, self._walk)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_discriminated_union.py", line 54, in inner
    s = apply_discriminator(s, discriminator, definitions)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_discriminated_union.py", line 86, in apply_discriminator
    return _ApplyInferredDiscriminator(discriminator, definitions or {}).apply(schema)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_discriminated_union.py", line 181, in apply
    schema = self._apply_to_root(schema)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_discriminated_union.py", line 221, in _apply_to_root
    self._handle_choice(choice)
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_discriminated_union.py", line 296, in _handle_choice
    self._set_unique_choice_for_values(choice, inferred_discriminator_values)
  File "/Users/fvoron/Development/polar/server/.venv/lib/python3.11/site-packages/pydantic/_internal/_discriminated_union.py", line 491, in _set_unique_choice_for_values
    raise TypeError(
TypeError: Value 'review_request_removed' for discriminator 'action' mapped to multiple choices

Additional context

The problem seems to come from here:

Union[
PullRequestReviewRequestRemovedOneof0, PullRequestReviewRequestRemovedOneof1
],
Union[PullRequestReviewRequestedOneof0, PullRequestReviewRequestedOneof1],

Pydantic doesn't like to have multiple choices for the same action. Apparently, this was working with Pydantic V1 (don't know how, the behavior was probably unexpected).

I don't really see how we could solve that, given that the action is the same; the only difference being on the presence of the fields requested_team/requested_reviewer. Maybe the simplest way could could be to tweak the generator to merge those two models?

Support for Enterprise API

For some reason for their v2.0.0 release GitHub split the API reference for regular GitHub and GitHub Enterprise Cloud (Not the GitHub Server you run locally but the GitHub hosted Enterprise). The endpoint is the same api.github.com, but the group enterprise-admin, scim, and server-statistics are not in the regular API specification.

This is a common issue for other packages and I can't figure out why they split it like this. But since this appears to be the one SDK that appears to be able to choose your API could you add support for using the ghec api?

Recommend a way how to test code consuming githubkit's models

This is more of a question rather than issue, but I didn't see a Discussions tab, so posting it like this. I build an app which gets some data from GitHub and githubkit has proven very useful so far, thank you so much for building it!

What I struggle with is that I'd like to test my functions, but I don't know how to do it in a convenient way. Imagine a function like this:

async def has_linkedin(social_accounts: list[SocialAccount]) -> tuple[ResultType, str]:
    for account in social_accounts:
        if account.provider == "linkedin":
            return ResultType.DONE, f"Found LinkedIn: {account.url}"
    return ResultType.ERROR, "Add a link to your LinkedIn profile."

To test it, I need to somehow feed it with a list of SocialAccount objects which normally come from githubkit, populated by data from the API, but here I'd need to craft them manually. Which is expected for a test case, but these objects are typically very complex and full of various attributes.

Perhaps I could somehow record the API responses (how?) to fixtures (Python dict, JSON... doesn't really matter I guess) and populate the SocialAccount objects with it for the test case (how?), changing only the attributes which are important for the particular case.

But it feels like a lot of work. It's an atypical use case, which I believe isn't very well covered in the docs, so before I dive into reading code, I wanted to ask here for guidance, because maybe this is something you know how to solve with two lines of code which I'd be looking for several hours. I checked your tests folder, but I didn't find much inspiration on how to solve my problem.

Bug: repo activity event issue comment field missing `issue_url`

Kinda strange error occur when I'm trying to get events for repository. It happens in code like:

auth = TokenAuthStrategy(GITHUB_TOKEN)
gh = GitHub(auth=auth)
 async for event in gh.paginate(
        gh.rest.activity.async_list_repo_events,
        owner=owner,
        repo=repository_name,
):
    print(event.repo.name)

Error looks like:
unexpected value; permitted: <UNSET> (type=value_error.const; given={'url': 'https://api.github.com/repos/{owner}/{repo}/pulls/comments/{id}', 'pull_request_review_id': {}, 'id': {}, 'node_id': 'PRRC_', 'diff_hunk': "@@ -1154,14 +1162,14 @@ {bunch of code from pr}', 'commit_id': '', 'original_commit_id': '', 'user': {'login': '', 'id': , 'node_id': '', 'avatar_url': 'https://avatars.githubusercontent.com/u/', 'gravatar_id': '', 'url': 'https://api.github.com/users/', 'html_url': 'https://github.com/', 'followers_url': 'https://api.github.com/users/followers', 'following_url': 'https://api.github.com/users/following{/other_user}', 'gists_url': 'https://api.github.com/users/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/subscriptions', 'organizations_url': 'https://api.github.com/users/orgs', 'repos_url': 'https://api.github.com/users/repos', 'events_url': 'https://api.github.com/users/events{/privacy}', 'received_events_url': 'https://api.github.com/users/received_events', 'type': 'User', 'site_admin': False}, 'body': "", 'created_at': '2023-12-19T21:52:26Z', 'updated_at': '2023-12-19T21:52:27Z', 'html_url': 'https://github.com/owner/repo/pull/513#discussion_r', 'pull_request_url': 'https://api.github.com/repos/owner/repo/pulls/51', 'author_association': 'CONTRIBUTOR', '_links': {'self': {'href': 'https://api.github.com/repos/owner/repo/pulls/comments/143'}, 'html': {'href': 'https://github.com/owner/repo/pull/512#discussion_r14'}, 'pull_request': {'href': 'https://api.github.com/repos/owner/repo/pulls/53'}}, 'reactions': {'url': 'https://api.github.com/repos/owner/repo/pulls/comments/123/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'start_line': None, 'original_start_line': None, 'start_side': None, 'line': 1167, 'original_line': 1167, 'side': 'RIGHT', 'in_reply_to_id': 1431963291, 'original_position': 97, 'position': 97, 'subject_type': 'line'}; permitted=(<UNSET>,)) __root__ -> 98 -> payload -> comment -> issue_url field required (type=value_error.missing)

I have no idea what it means. I've found some issues with similar error. You said it has been fixed in #68 .
But as you can see, I still getting it.

Is it any way to fix it?

Release upgrade to pydantic

Hey, we're using githubkit together with apache-airflow. The current apache-airflow version requires pydantic v2, but this package pulls in v1, resulting in a conflict.
Would it be possible to cut a new release that includes #45?
Thanks a lot in advance!

Use HTTP caching

Hi! First, thanks for this amazing package.

I'm wondering if you can use Hishel to reduce network traffic and possibly save some rate limits.
Users will be able to save time on responses that support caching (almost every github response).

You could enable in-memory caching at the library level and provide an API to the hishel so users can configure caching and possibly store responses in persistent storage so they can reuse cached responses across multiple executions.

There is also a useful example in the documentation that shows how effective caching can be when working with the github APIs.

If you decide to add this feature, I will gladly submit a pull request!

Type checking error when using github.paginate

When trying to use the paginate function like that:

org_id = "blabla"
github = GitHub("token")
for repo in github.paginate(github.rest.repos.list_for_org, org=org_id):
    print(repo)

I get the following type checking error in PyCharm 2023.1:

Unexpected type(s): ((org: str, type: Literal["all", "public", "private", "forks", "sources", "member"] | Unset, sort: Literal["created", "updated", "pushed", "full_name"] | Unset, direction: Literal["asc", "desc"] | Unset, per_page: Unset | int, page: Unset | int) -> Response[list[MinimalRepository]]) Possible type(s): ((org: str, type: Literal["all", "public", "private", "forks", "sources", "member"] | Unset, sort: Literal["created", "updated", "pushed", "full_name"] | Unset, direction: Literal["asc", "desc"] | Unset, per_page: Unset | int, page: Unset | int) -> Response | (org: str, type: Literal["all", "public", "private", "forks", "sources", "member"] | Unset, sort: Literal["created", "updated", "pushed", "full_name"] | Unset, direction: Literal["asc", "desc"] | Unset, per_page: Unset | int, page: Unset | int) -> Awaitable[Response]) ((org: str, type: Literal["all", "public", "private", "forks", "sources", "member"] | Unset, sort: Literal["created", "updated", "pushed", "full_name"] | Unset, direction: Literal["asc", "desc"] | Unset, per_page: Unset | int, page: Unset | int) -> Response | (org: str, type: Literal["all", "public", "private", "forks", "sources", "member"] | Unset, sort: Literal["created", "updated", "pushed", "full_name"] | Unset, direction: Literal["asc", "desc"] | Unset, per_page: Unset | int, page: Unset | int) -> Awaitable[Response])

running it works though.

I could fix that error, changing the following code (there are 3 overriden paginate methods, I did change all of them accordingly):

    @staticmethod
    def paginate(
        request: R[CP, CT],
        page: int = 1,
        per_page: int = 100,
        map_func: Optional[Callable[[Response[CT]], List[RT]]] = None,
        *args: CP.args,
        **kwargs: CP.kwargs,
    ) -> Paginator[RT]:
        return Paginator(request, page, per_page, map_func, *args, **kwargs)  # type: ignore

to

    @staticmethod
    def paginate(
        request: R,
        page: int = 1,
        per_page: int = 100,
        map_func: Optional[Callable[[Response[CT]], List[RT]]] = None,
        *args: CP.args,
        **kwargs: CP.kwargs,
    ) -> Paginator[RT]:
        return Paginator(request, page, per_page, map_func, *args, **kwargs)  # type: ignore

so not providing type arguments to R itself, which is not clear to me why this even works as R is not a generic type imho.

Excessive Token Exchange Operations in OAuth Implementation

Description

There is an issue in the OAuth-related login implementation. After using OAuth to authenticate users, a token exchange operation is performed for each request. This is mainly caused by the OAuthWebAuth class being instantiated before every request.

Problems

In theory, we can refer to the AppAuth implementation and cache the Access Token after a single exchange to avoid multiple authentications. However, in the OAuthWebAuthStrategy implementation, the only unique parameter that can identify the user is the code attribute. Since code is a temporary credential, it is not suitable for use as a cache key.

Additionally, obtaining the unique user ID as a cache key after each Exchange process in the Auth class is not feasible. The Auth class is instantiated each time, and there is no suitable storage location for the obtained unique user ID. Moreover, due to the immutability of the ...Strategy class, we cannot write the acquired user credentials into the Strategy class.

Proposed Solutions

At present, we have two possible solutions:

  1. Refactor the OAuthAppAuthStrategy's as_web_user method. Complete the exchange process directly within this method.
    Pros: This approach is very intuitive.
    Cons: It relies on the github object, which goes against the design purpose.
@dataclass
class OAuthAppAuthStrategy(BaseAuthStrategy):
    """OAuth App Authentication"""

    client_id: str
    client_secret: str

    def as_web_user(
        self, github: "GitHubCore", code: str, redirect_uri: Optional[str] = None
    ) -> "OAuthWebAuthStrategy":
        exchange_auth = _OAuthWebFlowExchangeAuth(
            github, self.client_id, self.client_secret, code, redirect_uri
        )
        with github.get_sync_client() as client:
            client.auth = exchange_auth
            response = client.get("/user")
            response.raise_for_status()
        user_id = response.json()["id"]
        access_token, expire_time = exchange_auth.access_token_info
        refresh_token, refresh_token_expire_time = exchange_auth.refresh_token_info
        assert access_token is not None
        return OAuthWebAuthStrategy(
            self.client_id,
            self.client_secret,
            user_id,
            access_token,
            refresh_token,
        )

    def get_auth_flow(self, github: "GitHubCore") -> httpx.Auth:
        return httpx.BasicAuth(self.client_id, self.client_secret)
  1. Establish a multi-level caching in ...Auth class: create a code-to-userid cache mapping, and then create a userid-to-token cache mapping. When the code does not exist in the cache, perform the exchange process first, and write the userid:token cache mapping. If it exists, obtain the userid and then get the token. Also, add an API that can directly obtain the token auth through the userid in the cache.
    Pros: This approach is compatible with the existing code.
    Cons: The implementation and usage are more complex and hard to understand.

Please feel free to comment about this issue.

Bug: dataclass compatibility for py38

python3.8็‰ˆๆœฌไธญ๏ผŒdataclassๅนถๆœชๆไพ›slotsๅ…ณ้”ฎๅญ—๏ผŒ่€Œไปฃ็ ไธญๅคšๆฌกไฝฟ็”จไบ†dataclass(slots=True)๏ผŒไผšๅฏผ่‡ดๅœจ3.8็Žฏๅขƒไธ‹่ฟ่กŒไปฃ็ ๅคฑ่ดฅ

How to conveniently convert paginate result to pandas DataFrame?

Said I fetch the pulls as:

    c = obtain_client()
    prs = c.paginate(
        c.rest.pulls.list,
        owner="apache",
        repo="pulsar",
        state="open")

Now prs is a generator of PullRequestSimple list. How can I convert it to a pandas DataFrame as:

   url id node_id ... draft
0 ... nnn mmm ... False
...

Support Pydantic v2

Pydantic v2 just came out, so it would be nice if githubkit could support the newer, faster version of Pydantic. I attempted to upgrade it myself, but there are a few bugs and non-trivial design decisions that would need to be made to support Pydantic v2, so I thought I'd bring this up now.

The migration guide does a good job of explaining what's changed, and how to upgrade your code. I'm just showing what I've encountered thus far, though there is probably additional work that needs to be done.

First up, regex is being renamed to pattern in Field() objects. Easy enough to fix, just change it in the codegen.

Second thing I ran into is that parse_raw_as has been removed, and instead you need to use a TypeAdapter:

    @property
    def parsed_data(self) -> RT:
        # old
        return parse_raw_as(self._data_model, self._response.content)

        # new
        return TypeAdapter(self._data_model).validate_python(self._response.content)

Lastly (or at least, where I stopped) is with Missing[]. This is a bug, as Pydantic v2 does not seem to like literal values like Literal[UNSET]. For example, the following code is throwing an error:

class Dependency(GitHubRestModel):
    """Dependency"""

    package_url: Missing[str] = Field(
        description="Package-url (PURL) of dependency. See https://github.com/package-url/purl-spec for more details.",
        pattern="^pkg",
        default=UNSET,
    )

This produces:

...

  File "githubkit/rest/models.py", line 7876, in <module>
    class Dependency(GitHubRestModel):
  File ".venv/lib/python3.11/site-packages/pydantic/_internal/_model_construction.py", line 172, in __new__
    complete_model_class(
  File ".venv/lib/python3.11/site-packages/pydantic/_internal/_model_construction.py", line 438, in complete_model_class
    cls.__pydantic_validator__ = SchemaValidator(simplified_core_schema, core_config)
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.SchemaError: Invalid Schema:
model.schema.model-fields.fields.package_url.schema.default.schema.union.pattern
  Extra inputs are not permitted [type=extra_forbidden, input_value='^pkg', input_type=str]
    For further information visit https://errors.pydantic.dev/2.1.2/v/extra_forbidden

Replacing Missing[str] with Union[Literal["xyz"], str] still fails, but Optional[str] works fine.

This seems to be tracked in pydantic/pydantic#6601. The error looks slightly different, though it still is an issue with Literal values in Unions.

I'm happy to work on a PR for this, though I thought I would get your input on a few things before I go ahead:

  • From what I can tell, this is a backwards-incompatible change. If you upgrade to Pydantic v2, you probably won't be able to support v1 as well
  • If the Literal issue doesn't get fixed soon (though it probably should), do you see any potential workarounds? We could just use Optional[str], though None doesn't convey the same thing as UNSET.

Let me know what your thoughts are on this, thanks!

Should `Missing` type implicitly allow `None`?

GitHub API schema is not always consistent with the required and nullable fields. Currently, this is solved by maintaining the schema_overrides table and report them to GitHub (hoping they'll solve it).

Unfortunately, this is fragile and can break code if GitHub suddenly decides to pop-up nullable fields in its API response. For example, this was working well some days ago:

from githubkit import GitHub
github = GitHub()
r = github.rest.orgs.get("frankie567-test-org-renamed")
r.parsed_data

But not anymore:

Traceback (most recent call last):
File "", line 1, in
File "/Users/fvoron/Development/githubkit/githubkit/response.py", line 50, in parsed_data
return parse_raw_as(self._data_model, self._response.content)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "pydantic/tools.py", line 82, in pydantic.tools.parse_raw_as
File "pydantic/tools.py", line 38, in pydantic.tools.parse_obj_as
File "pydantic/main.py", line 341, in pydantic.main.BaseModel.init
pydantic.error_wrappers.ValidationError: 2 validation errors for ParsingModel[OrganizationFull]
root -> name
none is not an allowed value (type=type_error.none.not_allowed)
root -> blog
none is not an allowed value (type=type_error.none.not_allowed)


That's why I was wondering if we could tweak the Missing type so it implicitly allows None. So basically this part:

Missing: TypeAlias = Union[Literal[UNSET], T]

becomes:

Missing: TypeAlias = Union[Literal[UNSET], T, None]

Admittedly, this is not perfectly accurate regarding the schema but it seems more bullet-proof while GitHub make up their mind about this.

Bug: `repos/get-content` response model uncorrect

schema:
  oneOf:
    - "$ref": "#/components/schemas/content-directory"
    - "$ref": "#/components/schemas/content-file"
    - "$ref": "#/components/schemas/content-symlink"
    - "$ref": "#/components/schemas/content-submodule"

TypeError: Object of type datetime is not JSON serializable

The following snippet:

import asyncio
from datetime import datetime, timezone

from githubkit.github import GitHub

github = GitHub()

async def main():
    await github.rest.checks.async_create(
        "user",
        "repo",
        name="name",
        head_sha="0000000000000000000000000000000000000000",
        started_at=datetime.now(timezone.utc)  # problematic line here
    )

asyncio.run(main())

Fails with the following exception(s):

Traceback (most recent call last):
  File "[redacted]/.venv/lib/python3.10/site-packages/githubkit/core.py", line 265, in _arequest
    return await client.request(
  File "[redacted]/.venv/lib/python3.10/site-packages/httpx/_client.py", line 1520, in request
    request = self.build_request(
  File "[redacted]/.venv/lib/python3.10/site-packages/httpx/_client.py", line 360, in build_request
    return Request(
  File "[redacted]/.venv/lib/python3.10/site-packages/httpx/_models.py", line 339, in __init__
    headers, stream = encode_request(
  File "[redacted]/.venv/lib/python3.10/site-packages/httpx/_content.py", line 215, in encode_request
    return encode_json(json)
  File "[redacted]/.venv/lib/python3.10/site-packages/httpx/_content.py", line 178, in encode_json
    body = json_dumps(json).encode("utf-8")
  File "/usr/lib/python3.10/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python3.10/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.10/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python3.10/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "[redacted]/main.py", line 34, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "[redacted]/main.py", line 20, in main
    x = await github.rest.checks.async_create(
  File "[redacted]/.venv/lib/python3.10/site-packages/githubkit/rest/checks.py", line 276, in async_create
    return await self._github.arequest(
  File "[redacted]/.venv/lib/python3.10/site-packages/githubkit/core.py", line 346, in arequest
    raw_resp = await self._arequest(
  File "[redacted]/.venv/lib/python3.10/site-packages/githubkit/core.py", line 279, in _arequest
    raise RequestError(repr(e)) from e
githubkit.exception.RequestError: TypeError('Object of type datetime is not JSON serializable')

When you look at the code for the async_check function:

json = json.dict(by_alias=True) if isinstance(json, BaseModel) else json

You will see that the .dict() method is called, which will return a Python dict, not a JSON-safe dict (which is what the json variable implies). This "json" object gets propogated all the way to httpx, and when it tries to JSON encode it, it fails.

Some ideas for how to fix this:

  1. Pydantic does have a .json() function, though it returns a string and not a dict. We could replace every call to json.dict(...) with loads(json.json(...)), where loads is the json.loads method from the stdlib, though there are a lot of API calls we would have to do this for, and it would introduce some overhead.
  2. Write a function that walks a dict object that stringifies datetime objects using .isoformat(). By adding said function right here (and to all the other API helper functions):

    githubkit/githubkit/core.py

    Lines 346 to 356 in bc1eb96

    raw_resp = await self._arequest(
    method,
    url,
    params=params,
    content=content,
    data=data,
    files=files,
    json=json,
    headers=headers,
    cookies=cookies,
    )

    We could stringify all datetime objects without changing every API function. There might be other object types other than datetimes which we might also want to support, datetimes just happened to be the ones that where giving me trouble.

I really like this library, but this bug is keeping me from using it in production ๐Ÿ˜ข. I wouldn't mind opening a PR for this, it sounds easy enough, assuming we go with option 2.

TypeError: unhashable type: 'list' with pydantic version <2.5.2

when i run this code i get an TypeError after i upgraded to pydantic version 2.5:

from githubkit import GitHub

with GitHub() as github:
    resp = github.rest.repos.get(owner="yanyongyu", repo="githubkit")
    repo = resp.parsed_data

following error is raised:

Traceback (most recent call last):
  File "githubkit\test.py", line 5, in <module>
    repo = resp.parsed_data
           ^^^^^^^^^^^^^^^^
  File "githubkit\githubkit\response.py", line 50, in parsed_data
    return TypeAdapter(self._data_model).validate_json(self.content)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "githubkit\venv\Lib\site-packages\pydantic\type_adapter.py", line 256, in validate_json
    return self.validator.validate_json(__data, strict=strict, context=context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: unhashable type: 'list'

maybe this has something todo with the new json parsing library pydantic hast introduced with version 2.5: https://docs.pydantic.dev/2.5/concepts/json/#json-parsing

Underlying HTTPX clients are shared among several GitHub instances

I discovered a strange behavior while testing out version v0.11.0a2. It seems that if you create several GitHub client in the same process/thread, they reuse the underlying HTTPX client, including the auth flow.

It's a problem if you need to have several clients with different kind of authentications running in your process.

Reproducible example

from githubkit import GitHub, TokenAuthStrategy, UnauthAuthStrategy


def multiple_clients() -> None:
    github_unauth = GitHub(UnauthAuthStrategy())
    response_unauth = github_unauth.rest.rate_limit.get()
    authorization_unauth = response_unauth.headers.get("authorization")
    print(authorization_unauth)  # None, as expected

    github_token = GitHub(TokenAuthStrategy("MY_TOKEN"))
    response_token = github_token.rest.rate_limit.get()
    authorization_token = response_token.headers.get("authorization")
    print(authorization_token)  # Also None, but should be `token MY TOKEN`


if __name__ == "__main__":
    multiple_clients()

Additional context

I suspect it's because ContextVar is used to retain the HTTPX client:

githubkit/githubkit/core.py

Lines 147 to 152 in 8a381cb

self.__sync_client: ContextVar[Optional[httpx.Client]] = ContextVar(
"sync_client", default=None
)
self.__async_client: ContextVar[Optional[httpx.AsyncClient]] = ContextVar(
"async_client", default=None
)


However, what I can't explain is that the same code works correctly in version 0.10.7, while ContextVar were already there. Maybe a side effect of d005552?

Bug: `pull request simple` field `head.repo` schema incorrect

g = ... # obtain the client
prs = g.paginate(g.rest.pulls.list, owner='apache', repo='pulsar', state='open')
pr = next(prs)

gives:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/lib/python3.10/site-packages/githubkit/paginator.py", line 76, in __next__
    contents = self._get_next_page()
  File "/opt/homebrew/lib/python3.10/site-packages/githubkit/paginator.py", line 112, in _get_next_page
    cast(Response[List[RT]], response).parsed_data
  File "/opt/homebrew/lib/python3.10/site-packages/githubkit/response.py", line 50, in parsed_data
    return parse_raw_as(self._data_model, self._response.content)
  File "pydantic/tools.py", line 82, in pydantic.tools.parse_raw_as
  File "pydantic/tools.py", line 38, in pydantic.tools.parse_obj_as
  File "pydantic/main.py", line 342, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 7 validation errors for ParsingModel[List[githubkit.rest.models.PullRequestSimple]]
__root__ -> 27 -> head -> repo
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 36 -> head -> repo
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 39 -> head -> repo
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 44 -> head -> repo
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 67 -> head -> repo
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 85 -> head -> repo
  none is not an allowed value (type=type_error.none.not_allowed)
__root__ -> 94 -> head -> repo
  none is not an allowed value (type=type_error.none.not_allowed)

Pydantic v2 - performance issues on import

Hi,

when testing the pydantic v2 prelease (0.11.0a0), we're seeing a big performance decrease on process startup time.
My test file:

from githubkit import GitHub

went from ca. 1.6sec to 3.8sec on my M1 macbook. On production systems that have less powerful CPUs and slower I/Os, we're seeing this take 15-20sec now.

When interrupting the loading process with Ctrl+C, we see a stacktrace ala which hints at pydantic being the issue.

  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/githubkit/__init__.py", line 2, in <module>
    from .github import GitHub as GitHub
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/githubkit/github.py", line 16, in <module>
    from .core import GitHubCore
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/githubkit/core.py", line 25, in <module>
    from .auth import BaseAuthStrategy, TokenAuthStrategy, UnauthAuthStrategy
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/githubkit/auth/__init__.py", line 1, in <module>
    from .app import AppAuthStrategy as AppAuthStrategy
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/githubkit/auth/app.py", line 10, in <module>
    from githubkit.rest import (
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/githubkit/rest/__init__.py", line 13, in <module>
    from .models import *
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/githubkit/rest/models.py", line 17358, in <module>
    class ReposOwnerRepoCodespacesGetResponse200(GitHubRestModel):
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_model_construction.py", line 184, in __new__
    complete_model_class(
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_model_construction.py", line 481, in complete_model_class
    schema = cls.__get_pydantic_core_schema__(cls, handler)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/main.py", line 576, in __get_pydantic_core_schema__
    return __handler(__source)
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_schema_generation_shared.py", line 82, in __call__
    schema = self._handler(__source_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 452, in generate_schema
    schema = self._generate_schema(obj)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 684, in _generate_schema
    schema = self._post_process_generated_schema(self._generate_schema_inner(obj))
                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 706, in _generate_schema_inner
    return self._model_schema(obj)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 525, in _model_schema
    {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 525, in <dictcomp>
    {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 870, in _generate_md_field_schema
    common_field = self._common_field_schema(name, field_info, decorators)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 923, in _common_field_schema
    schema = self._apply_annotations(
             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 1592, in _apply_annotations
    schema = get_inner_schema(source_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_schema_generation_shared.py", line 82, in __call__
    schema = self._handler(__source_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 1682, in new_handler
    schema = metadata_get_schema(source, get_inner_schema)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_std_types_schema.py", line 315, in __get_pydantic_core_schema__
    items_schema = handler.generate_schema(self.item_source_type)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_schema_generation_shared.py", line 96, in generate_schema
    return self._generate_schema.generate_schema(__source_type)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 447, in generate_schema
    from_property = self._generate_schema_from_property(obj, obj)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 600, in _generate_schema_from_property
    schema = get_schema(
             ^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/main.py", line 576, in __get_pydantic_core_schema__
    return __handler(__source)
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_schema_generation_shared.py", line 82, in __call__
    schema = self._handler(__source_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 684, in _generate_schema
    schema = self._post_process_generated_schema(self._generate_schema_inner(obj))
                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 706, in _generate_schema_inner
    return self._model_schema(obj)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 525, in _model_schema
    {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 525, in <dictcomp>
    {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 870, in _generate_md_field_schema
    common_field = self._common_field_schema(name, field_info, decorators)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 923, in _common_field_schema
    schema = self._apply_annotations(
             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 1592, in _apply_annotations
    schema = get_inner_schema(source_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_schema_generation_shared.py", line 82, in __call__
    schema = self._handler(__source_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 1571, in inner_handler
    from_property = self._generate_schema_from_property(obj, obj)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 600, in _generate_schema_from_property
    schema = get_schema(
             ^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/main.py", line 576, in __get_pydantic_core_schema__
    return __handler(__source)
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_schema_generation_shared.py", line 82, in __call__
    schema = self._handler(__source_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 684, in _generate_schema
    schema = self._post_process_generated_schema(self._generate_schema_inner(obj))
                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 706, in _generate_schema_inner
    return self._model_schema(obj)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 525, in _model_schema
    {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 525, in <dictcomp>
    {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 870, in _generate_md_field_schema
    common_field = self._common_field_schema(name, field_info, decorators)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 923, in _common_field_schema
    schema = self._apply_annotations(
             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 1592, in _apply_annotations
    schema = get_inner_schema(source_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_schema_generation_shared.py", line 82, in __call__
    schema = self._handler(__source_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 1576, in inner_handler
    metadata_js_function = _extract_get_pydantic_json_schema(obj, schema)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/pydantic/_internal/_generate_schema.py", line 1924, in _extract_get_pydantic_json_schema
    js_modify_function = getattr(tp, '__get_pydantic_json_schema__', None)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.11.4/Frameworks/Python.framework/Versions/3.11/lib/python3.11/typing.py", line 1280, in __getattr__
    def __getattr__(self, attr):

Is there anything that can be done about this?

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • We receive the funding once the issue is completed & confirmed by you.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar

Parsing pull requests fails due to Pydantic validation (missing PullRequestSimplePropHead.label)

If you run something like for pr in github.paginate(github.rest.pulls.list, owner="yarnpkg", repo="yarn", state="all"): ... you will get an error such as

pydantic.error_wrappers.ValidationError: 2 validation errors for ParsingModel[List[githubkit.rest.models.PullRequestSimple]] __root__ -> 31 -> head -> label none is not an allowed value (type=type_error.none.not_allowed)

because the label is missing in the response. Here is an example element contained in the response:

{
    "url": "https://api.github.com/repos/yarnpkg/yarn/pulls/1477",
    "id": 90980048,
    "node_id": "MDExOlB1bGxSZXF1ZXN0OTA5ODAwNDg=",
    "html_url": "https://github.com/yarnpkg/yarn/pull/1477",
    "diff_url": "https://github.com/yarnpkg/yarn/pull/1477.diff",
    "patch_url": "https://github.com/yarnpkg/yarn/pull/1477.patch",
    "issue_url": "https://api.github.com/repos/yarnpkg/yarn/issues/1477",
    "number": 1477,
    "state": "closed",
    "locked": false,
    "title": "fix-1158: resolve dep verions by ranges, not exact versions (#1158) (#1272) (#1404)",
    "user": {
      "login": "ghost",
      "id": 10137,
      "node_id": "MDQ6VXNlcjEwMTM3",
      "avatar_url": "https://avatars.githubusercontent.com/u/10137?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/ghost",
      "html_url": "https://github.com/ghost",
      "followers_url": "https://api.github.com/users/ghost/followers",
      "following_url": "https://api.github.com/users/ghost/following{/other_user}",
      "gists_url": "https://api.github.com/users/ghost/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/ghost/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/ghost/subscriptions",
      "organizations_url": "https://api.github.com/users/ghost/orgs",
      "repos_url": "https://api.github.com/users/ghost/repos",
      "events_url": "https://api.github.com/users/ghost/events{/privacy}",
      "received_events_url": "https://api.github.com/users/ghost/received_events",
      "type": "User",
      "site_admin": false
    },
    "body": "<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. The two fields below are mandatory. -->\n\n**Summary**\n\nSmarter/looser dep version number resolving -- by range instead of exact version. ([comment and code disagree](https://github.com/yarnpkg/yarn/blob/master/src/package-request.js#L218-L220))\n\n`package-request` will resolve a package to the **highest** available version, and check if that package version has already been resolved with `package-resolver`'s **exact version match** `getExactVersionMatch`. \n\nInstead of matching the exact highest version: match satisfying resolved ranges of the requested package, and pick the highest available one, _if_ possible. _If not_ possible, continue and resolve to the highest possible version as before.\n\nhttps://github.com/yarnpkg/yarn/issues/112 originally says and tests the opposite of this. The concerns in this issue have been tested as-well and are succeeding: `npm shrinkwrap` && `yarn check`. Tests have been adapted, since the setup was largely the same.\n\n<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->\n\nResolves:\n- https://github.com/yarnpkg/yarn/issues/1158\n- https://github.com/yarnpkg/yarn/issues/1272\n- https://github.com/yarnpkg/yarn/issues/1404\n\n**Test plan**\n\n<!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. -->\n\n```\n{\n  \"dependencies\": {\n    \"devbridge-autocomplete\": \"^1.2.26\",\n    \"jquery\": \"2.1.2\"\n  }\n}\n```\n\n```\n$ yarn install\n$ npm shrinkwrap --dev\n$ yarn check\n```\n\nOpen `yarn.lock` and see that range-matching dependencies are shared.\n",
    "created_at": "2016-10-26T10:50:52Z",
    "updated_at": "2016-11-11T19:27:28Z",
    "closed_at": "2016-10-26T15:00:13Z",
    "merged_at": "2016-10-26T15:00:13Z",
    "merge_commit_sha": "ec6a4508ad4aa748fc9ff9b2c665228bf5770696",
    "assignee": null,
    "assignees": [],
    "requested_reviewers": [],
    "requested_teams": [],
    "labels": [],
    "milestone": null,
    "draft": false,
    "commits_url": "https://api.github.com/repos/yarnpkg/yarn/pulls/1477/commits",
    "review_comments_url": "https://api.github.com/repos/yarnpkg/yarn/pulls/1477/comments",
    "review_comment_url": "https://api.github.com/repos/yarnpkg/yarn/pulls/comments{/number}",
    "comments_url": "https://api.github.com/repos/yarnpkg/yarn/issues/1477/comments",
    "statuses_url": "https://api.github.com/repos/yarnpkg/yarn/statuses/5a0e147f4b54573dc529a48a3545a7bf38ce6043",
    "head": {
      "label": null,
      "ref": "fix-1158-version-resolving",
      "sha": "5a0e147f4b54573dc529a48a3545a7bf38ce6043",
      "user": null,
      "repo": null
    },
    "base": {
      "label": "yarnpkg:master",
      "ref": "master",
      "sha": "86899fea2718dfd6da3666cca1166cbedec79f28",
      "user": {
        "login": "yarnpkg",
        "id": 22247014,
        "node_id": "MDEyOk9yZ2FuaXphdGlvbjIyMjQ3MDE0",
        "avatar_url": "https://avatars.githubusercontent.com/u/22247014?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/yarnpkg",
        "html_url": "https://github.com/yarnpkg",
        "followers_url": "https://api.github.com/users/yarnpkg/followers",
        "following_url": "https://api.github.com/users/yarnpkg/following{/other_user}",
        "gists_url": "https://api.github.com/users/yarnpkg/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/yarnpkg/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/yarnpkg/subscriptions",
        "organizations_url": "https://api.github.com/users/yarnpkg/orgs",
        "repos_url": "https://api.github.com/users/yarnpkg/repos",
        "events_url": "https://api.github.com/users/yarnpkg/events{/privacy}",
        "received_events_url": "https://api.github.com/users/yarnpkg/received_events",
        "type": "Organization",
        "site_admin": false
      },
      "repo": {
        "id": 49970642,
        "node_id": "MDEwOlJlcG9zaXRvcnk0OTk3MDY0Mg==",
        "name": "yarn",
        "full_name": "yarnpkg/yarn",
        "private": false,
        "owner": {
          "login": "yarnpkg",
          "id": 22247014,
          "node_id": "MDEyOk9yZ2FuaXphdGlvbjIyMjQ3MDE0",
          "avatar_url": "https://avatars.githubusercontent.com/u/22247014?v=4",
          "gravatar_id": "",
          "url": "https://api.github.com/users/yarnpkg",
          "html_url": "https://github.com/yarnpkg",
          "followers_url": "https://api.github.com/users/yarnpkg/followers",
          "following_url": "https://api.github.com/users/yarnpkg/following{/other_user}",
          "gists_url": "https://api.github.com/users/yarnpkg/gists{/gist_id}",
          "starred_url": "https://api.github.com/users/yarnpkg/starred{/owner}{/repo}",
          "subscriptions_url": "https://api.github.com/users/yarnpkg/subscriptions",
          "organizations_url": "https://api.github.com/users/yarnpkg/orgs",
          "repos_url": "https://api.github.com/users/yarnpkg/repos",
          "events_url": "https://api.github.com/users/yarnpkg/events{/privacy}",
          "received_events_url": "https://api.github.com/users/yarnpkg/received_events",
          "type": "Organization",
          "site_admin": false
        },
        "html_url": "https://github.com/yarnpkg/yarn",
        "description": "The 1.x line is frozen - features and bugfixes now happen on https://github.com/yarnpkg/berry",
        "fork": false,
        "url": "https://api.github.com/repos/yarnpkg/yarn",
        "forks_url": "https://api.github.com/repos/yarnpkg/yarn/forks",
        "keys_url": "https://api.github.com/repos/yarnpkg/yarn/keys{/key_id}",
        "collaborators_url": "https://api.github.com/repos/yarnpkg/yarn/collaborators{/collaborator}",
        "teams_url": "https://api.github.com/repos/yarnpkg/yarn/teams",
        "hooks_url": "https://api.github.com/repos/yarnpkg/yarn/hooks",
        "issue_events_url": "https://api.github.com/repos/yarnpkg/yarn/issues/events{/number}",
        "events_url": "https://api.github.com/repos/yarnpkg/yarn/events",
        "assignees_url": "https://api.github.com/repos/yarnpkg/yarn/assignees{/user}",
        "branches_url": "https://api.github.com/repos/yarnpkg/yarn/branches{/branch}",
        "tags_url": "https://api.github.com/repos/yarnpkg/yarn/tags",
        "blobs_url": "https://api.github.com/repos/yarnpkg/yarn/git/blobs{/sha}",
        "git_tags_url": "https://api.github.com/repos/yarnpkg/yarn/git/tags{/sha}",
        "git_refs_url": "https://api.github.com/repos/yarnpkg/yarn/git/refs{/sha}",
        "trees_url": "https://api.github.com/repos/yarnpkg/yarn/git/trees{/sha}",
        "statuses_url": "https://api.github.com/repos/yarnpkg/yarn/statuses/{sha}",
        "languages_url": "https://api.github.com/repos/yarnpkg/yarn/languages",
        "stargazers_url": "https://api.github.com/repos/yarnpkg/yarn/stargazers",
        "contributors_url": "https://api.github.com/repos/yarnpkg/yarn/contributors",
        "subscribers_url": "https://api.github.com/repos/yarnpkg/yarn/subscribers",
        "subscription_url": "https://api.github.com/repos/yarnpkg/yarn/subscription",
        "commits_url": "https://api.github.com/repos/yarnpkg/yarn/commits{/sha}",
        "git_commits_url": "https://api.github.com/repos/yarnpkg/yarn/git/commits{/sha}",
        "comments_url": "https://api.github.com/repos/yarnpkg/yarn/comments{/number}",
        "issue_comment_url": "https://api.github.com/repos/yarnpkg/yarn/issues/comments{/number}",
        "contents_url": "https://api.github.com/repos/yarnpkg/yarn/contents/{+path}",
        "compare_url": "https://api.github.com/repos/yarnpkg/yarn/compare/{base}...{head}",
        "merges_url": "https://api.github.com/repos/yarnpkg/yarn/merges",
        "archive_url": "https://api.github.com/repos/yarnpkg/yarn/{archive_format}{/ref}",
        "downloads_url": "https://api.github.com/repos/yarnpkg/yarn/downloads",
        "issues_url": "https://api.github.com/repos/yarnpkg/yarn/issues{/number}",
        "pulls_url": "https://api.github.com/repos/yarnpkg/yarn/pulls{/number}",
        "milestones_url": "https://api.github.com/repos/yarnpkg/yarn/milestones{/number}",
        "notifications_url": "https://api.github.com/repos/yarnpkg/yarn/notifications{?since,all,participating}",
        "labels_url": "https://api.github.com/repos/yarnpkg/yarn/labels{/name}",
        "releases_url": "https://api.github.com/repos/yarnpkg/yarn/releases{/id}",
        "deployments_url": "https://api.github.com/repos/yarnpkg/yarn/deployments",
        "created_at": "2016-01-19T17:39:16Z",
        "updated_at": "2023-12-19T10:07:58Z",
        "pushed_at": "2023-11-14T19:04:20Z",
        "git_url": "git://github.com/yarnpkg/yarn.git",
        "ssh_url": "[email protected]:yarnpkg/yarn.git",
        "clone_url": "https://github.com/yarnpkg/yarn.git",
        "svn_url": "https://github.com/yarnpkg/yarn",
        "homepage": "https://classic.yarnpkg.com",
        "size": 119486,
        "stargazers_count": 41274,
        "watchers_count": 41274,
        "language": "JavaScript",
        "has_issues": true,
        "has_projects": true,
        "has_downloads": true,
        "has_wiki": false,
        "has_pages": false,
        "has_discussions": false,
        "forks_count": 2919,
        "mirror_url": null,
        "archived": false,
        "disabled": false,
        "open_issues_count": 1990,
        "license": {
          "key": "other",
          "name": "Other",
          "spdx_id": "NOASSERTION",
          "url": null,
          "node_id": "MDc6TGljZW5zZTA="
        },
        "allow_forking": true,
        "is_template": false,
        "web_commit_signoff_required": false,
        "topics": [
          "javascript",
          "npm",
          "package-manager",
          "yarn"
        ],
        "visibility": "public",
        "forks": 2919,
        "open_issues": 1990,
        "watchers": 41274,
        "default_branch": "master"
      }
    },
    "_links": {
      "self": {
        "href": "https://api.github.com/repos/yarnpkg/yarn/pulls/1477"
      },
      "html": {
        "href": "https://github.com/yarnpkg/yarn/pull/1477"
      },
      "issue": {
        "href": "https://api.github.com/repos/yarnpkg/yarn/issues/1477"
      },
      "comments": {
        "href": "https://api.github.com/repos/yarnpkg/yarn/issues/1477/comments"
      },
      "review_comments": {
        "href": "https://api.github.com/repos/yarnpkg/yarn/pulls/1477/comments"
      },
      "review_comment": {
        "href": "https://api.github.com/repos/yarnpkg/yarn/pulls/comments{/number}"
      },
      "commits": {
        "href": "https://api.github.com/repos/yarnpkg/yarn/pulls/1477/commits"
      },
      "statuses": {
        "href": "https://api.github.com/repos/yarnpkg/yarn/statuses/5a0e147f4b54573dc529a48a3545a7bf38ce6043"
      }
    },
    "author_association": "NONE",
    "auto_merge": null,
    "active_lock_reason": null
  }

Support v7 of octokit/webhooks schema

I've noticed that the generation step has stopped working since octokit/webhooks v7 was released. https://github.com/octokit/webhooks/releases/tag/v7.0.0

Downgrading to the latest v6 version fixes the generation temporarily, but I suppose that supporting v7 is the best way forward.

diff --git a/pyproject.toml b/pyproject.toml
index 190b460..684a886 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -124,8 +124,12 @@ output_dir = "githubkit/rest/"
 "/components/schemas/team-organization/properties/company" = { type = ["string", "null"] }
 "/components/schemas/team-organization/properties/email" = { type = ["string", "null"] }

 [tool.codegen.webhook]
-schema_source = "https://unpkg.com/@octokit/webhooks-schemas/schema.json"
+schema_source = "https://unpkg.com/@octokit/[email protected]/schema.json"
 output = "githubkit/webhooks/models.py"
 types_output = "githubkit/webhooks/types.py"

This is the log from the failure:

2023-06-07 13:15:57,263 INFO Start getting Webhook source...
2023-06-07 13:15:57,362 INFO HTTP Request: GET https://unpkg.com/@octokit/webhooks-schemas/schema.json "HTTP/1.1 302 Found"
2023-06-07 13:15:57,403 INFO HTTP Request: GET https://unpkg.com/@octokit/[email protected]/schema.json "HTTP/1.1 200 OK"
2023-06-07 13:15:57,422 INFO Getting schema from https://unpkg.com/@octokit/webhooks-schemas/schema.json succeeded!
2023-06-07 13:15:57,422 INFO Start parsing Webhook spec...
Traceback (most recent call last):
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/model_schema.py", line 218, in _add_if_no_conflict
    prop = _merge_property(
           ^^^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/model_schema.py", line 184, in _merge_property
    schema = _merge_schema(
             ^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/model_schema.py", line 172, in _merge_schema
    raise RuntimeError(f"Cannot merge schema for {name}: {first!r}; {second!r}")
RuntimeError: Cannot merge schema for resolution: UnionSchema(title=None, description='**Required when the `state` is `resolved`.** The reason for resolving the alert.', default=None, examples=None, schemas=[NoneSchema(title=None, description='**Required when the `state` is `resolved`.** The reason for resolving the alert.', default=None, examples=None), EnumSchema(title=None, description='**Required when the `state` is `resolved`.** The reason for resolving the alert.', default=None, examples=None, values=['false_positive', 'wont_fix', 'revoked', 'used_in_tests'])], discriminator=None); EnumSchema(title=None, description=None, default=None, examples=None, values=['false_positive', 'wontfix', 'revoked', 'used_in_tests'])

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/Users/gustav/src/githubkit/codegen/__main__.py", line 9, in <module>
    build()
  File "/Users/gustav/src/githubkit/codegen/__init__.py", line 156, in build
    parsed_data = parse_webhook_schema(source, config.webhook, config)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/__init__.py", line 86, in parse_webhook_schema
    root_schema = parse_schema(source, "webhook_schema")
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/__init__.py", line 56, in parse_schema
    schema = build_union_schema(source, class_name, base_source)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/union_schema.py", line 84, in build_union_schema
    _build_sub_schema(
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/union_schema.py", line 52, in _build_sub_schema
    schema = parse_schema(
             ^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/__init__.py", line 56, in parse_schema
    schema = build_union_schema(source, class_name, base_source)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/union_schema.py", line 84, in build_union_schema
    _build_sub_schema(
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/union_schema.py", line 52, in _build_sub_schema
    schema = parse_schema(
             ^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/__init__.py", line 70, in parse_schema
    schema = build_model_schema(source, class_name, base_source)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/model_schema.py", line 299, in build_model_schema
    schema.properties = _process_properties(source, class_name, base_source)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/model_schema.py", line 264, in _process_properties
    prop_schema = parse_schema(
                  ^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/__init__.py", line 70, in parse_schema
    schema = build_model_schema(source, class_name, base_source)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/model_schema.py", line 299, in build_model_schema
    schema.properties = _process_properties(source, class_name, base_source)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/model_schema.py", line 245, in _process_properties
    _add_if_no_conflict(prop)
  File "/Users/gustav/src/githubkit/codegen/parser/schemas/model_schema.py", line 225, in _add_if_no_conflict
    raise RuntimeError(
RuntimeError: Error while creating model SecretScanningAlertResolvedPropAlert from https://unpkg.com/@octokit/webhooks-schemas/schema.json#/definitions/secret_scanning_alert$resolved/properties/alert, duplicated property resolution

How to serialize webhook events?

Hi,

I want to serialize webhook events (workflow_run in particular).

I do

    event_name = request.headers['X-GitHub-Event']
    event: WebhookEvent = parse(event_name, request.data)
    parse_obj_without_name(event.json())

to get the JSON representation, and then try to read that back in with parse_obj, and then I get validation errors from pydantic.

 webserver | [2023-06-30T18:32:53.750+0200] {app.py:1744} ERROR - Exception on /github_hook [POST]
 webserver | Traceback (most recent call last):
 webserver | File "/opt/homebrew/lib/python3.10/site-packages/flask/app.py", line 2529, in wsgi_app
 webserver | response = self.full_dispatch_request()
 webserver | File "/opt/homebrew/lib/python3.10/site-packages/flask/app.py", line 1825, in full_dispatch_request
 webserver | rv = self.handle_user_exception(e)
 webserver | File "/opt/homebrew/lib/python3.10/site-packages/flask/app.py", line 1823, in full_dispatch_request
 webserver | rv = self.dispatch_request()
 webserver | File "/opt/homebrew/lib/python3.10/site-packages/flask/app.py", line 1799, in dispatch_request
 webserver | return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
 webserver | File "/Users/criemen/repos/codeql-halo/plugins/github_webhook_receiver.py", line 121, in github_hook
 webserver | parse_obj_without_name(event.json())
 webserver | File "/opt/homebrew/lib/python3.10/site-packages/githubkit/webhooks/parse.py", line 21, in parse_obj_without_name
 webserver | return parse_obj_as(WebhookEvent, payload)
 webserver | File "pydantic/tools.py", line 38, in pydantic.tools.parse_obj_as
 webserver | File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
 webserver | pydantic.error_wrappers.ValidationError: 62 validation errors for ParsingModel[Union[Annotated[Union[githubkit.webhooks.models.BranchProtectionRuleCreated, githubkit.webhooks.models.BranchProtectionRuleDeleted, githubkit.webhooks.models.BranchProtectionRuleEdited], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.CheckRunCompleted, githubkit.webhooks.models.CheckRunCreated, githubkit.webhooks.models.CheckRunRequestedAction, githubkit.webhooks.models.CheckRunRerequested], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.CheckSuiteCompleted, githubkit.webhooks.models.CheckSuiteRequested, githubkit.webhooks.models.CheckSuiteRerequested], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.CodeScanningAlertAppearedInBranch, githubkit.webhooks.models.CodeScanningAlertClosedByUser, githubkit.webhooks.models.CodeScanningAlertCreated, githubkit.webhooks.models.CodeScanningAlertFixed, githubkit.webhooks.models.CodeScanningAlertReopened, githubkit.webhooks.models.CodeScanningAlertReopenedByUser], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], CommitCommentCreated, CreateEvent, DeleteEvent, Annotated[Union[githubkit.webhooks.models.DependabotAlertCreated, githubkit.webhooks.models.DependabotAlertDismissed, githubkit.webhooks.models.DependabotAlertFixed, githubkit.webhooks.models.DependabotAlertReintroduced, githubkit.webhooks.models.DependabotAlertReopened], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.DeployKeyCreated, githubkit.webhooks.models.DeployKeyDeleted], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], DeploymentCreated, DeploymentProtectionRuleRequested, DeploymentStatusCreated, Annotated[Union[githubkit.webhooks.models.DiscussionAnswered, githubkit.webhooks.models.DiscussionCategoryChanged, githubkit.webhooks.models.DiscussionCreated, githubkit.webhooks.models.DiscussionDeleted, githubkit.webhooks.models.DiscussionEdited, githubkit.webhooks.models.DiscussionLabeled, githubkit.webhooks.models.DiscussionLocked, githubkit.webhooks.models.DiscussionPinned, githubkit.webhooks.models.DiscussionTransferred, githubkit.webhooks.models.DiscussionUnanswered, githubkit.webhooks.models.DiscussionUnlabeled, githubkit.webhooks.models.DiscussionUnlocked, githubkit.webhooks.models.DiscussionUnpinned], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.DiscussionCommentCreated, githubkit.webhooks.models.DiscussionCommentDeleted, githubkit.webhooks.models.DiscussionCommentEdited], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], ForkEvent, GithubAppAuthorizationRevoked, GollumEvent, Annotated[Union[githubkit.webhooks.models.InstallationCreated, githubkit.webhooks.models.InstallationDeleted, githubkit.webhooks.models.InstallationNewPermissionsAccepted, githubkit.webhooks.models.InstallationSuspend, githubkit.webhooks.models.InstallationUnsuspend], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.InstallationRepositoriesAdded, githubkit.webhooks.models.InstallationRepositoriesRemoved], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], InstallationTargetRenamed, Annotated[Union[githubkit.webhooks.models.IssueCommentCreated, githubkit.webhooks.models.IssueCommentDeleted, githubkit.webhooks.models.IssueCommentEdited], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.IssuesAssigned, githubkit.webhooks.models.IssuesClosed, githubkit.webhooks.models.IssuesDeleted, githubkit.webhooks.models.IssuesDemilestoned, githubkit.webhooks.models.IssuesEdited, githubkit.webhooks.models.IssuesLabeled, githubkit.webhooks.models.IssuesLocked, githubkit.webhooks.models.IssuesMilestoned, githubkit.webhooks.models.IssuesOpened, githubkit.webhooks.models.IssuesPinned, githubkit.webhooks.models.IssuesReopened, githubkit.webhooks.models.IssuesTransferred, githubkit.webhooks.models.IssuesUnassigned, githubkit.webhooks.models.IssuesUnlabeled, githubkit.webhooks.models.IssuesUnlocked, githubkit.webhooks.models.IssuesUnpinned], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.LabelCreated, githubkit.webhooks.models.LabelDeleted, githubkit.webhooks.models.LabelEdited], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.MarketplacePurchaseCancelled, githubkit.webhooks.models.MarketplacePurchaseChanged, githubkit.webhooks.models.MarketplacePurchasePendingChange, githubkit.webhooks.models.MarketplacePurchasePendingChangeCancelled, githubkit.webhooks.models.MarketplacePurchasePurchased], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.MemberAdded, githubkit.webhooks.models.MemberEdited, githubkit.webhooks.models.MemberRemoved], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.MembershipAdded, githubkit.webhooks.models.MembershipRemoved], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], MergeGroupChecksRequested, MetaDeleted, Annotated[Union[githubkit.webhooks.models.MilestoneClosed, githubkit.webhooks.models.MilestoneCreated, githubkit.webhooks.models.MilestoneDeleted, githubkit.webhooks.models.MilestoneEdited, githubkit.webhooks.models.MilestoneOpened], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.OrgBlockBlocked, githubkit.webhooks.models.OrgBlockUnblocked], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.OrganizationDeleted, githubkit.webhooks.models.OrganizationMemberAdded, githubkit.webhooks.models.OrganizationMemberInvited, githubkit.webhooks.models.OrganizationMemberRemoved, githubkit.webhooks.models.OrganizationRenamed], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.PackagePublished, githubkit.webhooks.models.PackageUpdated], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], PageBuildEvent, PingEvent, Annotated[Union[githubkit.webhooks.models.ProjectClosed, githubkit.webhooks.models.ProjectCreated, githubkit.webhooks.models.ProjectDeleted, githubkit.webhooks.models.ProjectEdited, githubkit.webhooks.models.ProjectReopened], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.ProjectCardConverted, githubkit.webhooks.models.ProjectCardCreated, githubkit.webhooks.models.ProjectCardDeleted, githubkit.webhooks.models.ProjectCardEdited, githubkit.webhooks.models.ProjectCardMoved], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.ProjectColumnCreated, githubkit.webhooks.models.ProjectColumnDeleted, githubkit.webhooks.models.ProjectColumnEdited, githubkit.webhooks.models.ProjectColumnMoved], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.ProjectsV2ItemArchived, githubkit.webhooks.models.ProjectsV2ItemConverted, githubkit.webhooks.models.ProjectsV2ItemCreated, githubkit.webhooks.models.ProjectsV2ItemDeleted, githubkit.webhooks.models.ProjectsV2ItemEdited, githubkit.webhooks.models.ProjectsV2ItemReordered, githubkit.webhooks.models.ProjectsV2ItemRestored], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], PublicEvent, Annotated[Union[githubkit.webhooks.models.PullRequestAssigned, githubkit.webhooks.models.PullRequestAutoMergeDisabled, githubkit.webhooks.models.PullRequestAutoMergeEnabled, githubkit.webhooks.models.PullRequestClosed, githubkit.webhooks.models.PullRequestConvertedToDraft, githubkit.webhooks.models.PullRequestDemilestoned, githubkit.webhooks.models.PullRequestDequeued, githubkit.webhooks.models.PullRequestEdited, githubkit.webhooks.models.PullRequestEnqueued, githubkit.webhooks.models.PullRequestLabeled, githubkit.webhooks.models.PullRequestLocked, githubkit.webhooks.models.PullRequestMilestoned, githubkit.webhooks.models.PullRequestOpened, githubkit.webhooks.models.PullRequestReadyForReview, githubkit.webhooks.models.PullRequestReopened, githubkit.webhooks.models.PullRequestReviewRequestRemovedOneof0, githubkit.webhooks.models.PullRequestReviewRequestRemovedOneof1, githubkit.webhooks.models.PullRequestReviewRequestedOneof0, githubkit.webhooks.models.PullRequestReviewRequestedOneof1, githubkit.webhooks.models.PullRequestSynchronize, githubkit.webhooks.models.PullRequestUnassigned, githubkit.webhooks.models.PullRequestUnlabeled, githubkit.webhooks.models.PullRequestUnlocked], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.PullRequestReviewDismissed, githubkit.webhooks.models.PullRequestReviewEdited, githubkit.webhooks.models.PullRequestReviewSubmitted], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.PullRequestReviewCommentCreated, githubkit.webhooks.models.PullRequestReviewCommentDeleted, githubkit.webhooks.models.PullRequestReviewCommentEdited], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.PullRequestReviewThreadResolved, githubkit.webhooks.models.PullRequestReviewThreadUnresolved], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], PushEvent, Annotated[Union[githubkit.webhooks.models.RegistryPackagePublished, githubkit.webhooks.models.RegistryPackageUpdated], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.ReleaseCreated, githubkit.webhooks.models.ReleaseDeleted, githubkit.webhooks.models.ReleaseEdited, githubkit.webhooks.models.ReleasePrereleased, githubkit.webhooks.models.ReleasePublished, githubkit.webhooks.models.ReleaseReleased, githubkit.webhooks.models.ReleaseUnpublished], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.RepositoryArchived, githubkit.webhooks.models.RepositoryCreated, githubkit.webhooks.models.RepositoryDeleted, githubkit.webhooks.models.RepositoryEdited, githubkit.webhooks.models.RepositoryPrivatized, githubkit.webhooks.models.RepositoryPublicized, githubkit.webhooks.models.RepositoryRenamed, githubkit.webhooks.models.RepositoryTransferred, githubkit.webhooks.models.RepositoryUnarchived], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], RepositoryDispatchEvent, RepositoryImportEvent, Annotated[Union[githubkit.webhooks.models.RepositoryVulnerabilityAlertCreate, githubkit.webhooks.models.RepositoryVulnerabilityAlertDismiss, githubkit.webhooks.models.RepositoryVulnerabilityAlertReopen, githubkit.webhooks.models.RepositoryVulnerabilityAlertResolve], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.SecretScanningAlertCreated, githubkit.webhooks.models.SecretScanningAlertReopened, githubkit.webhooks.models.SecretScanningAlertResolved, githubkit.webhooks.models.SecretScanningAlertRevoked], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], SecretScanningAlertLocationCreated, Annotated[Union[githubkit.webhooks.models.SecurityAdvisoryPerformed, githubkit.webhooks.models.SecurityAdvisoryPublished, githubkit.webhooks.models.SecurityAdvisoryUpdated, githubkit.webhooks.models.SecurityAdvisoryWithdrawn], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.SponsorshipCancelled, githubkit.webhooks.models.SponsorshipCreated, githubkit.webhooks.models.SponsorshipEdited, githubkit.webhooks.models.SponsorshipPendingCancellation, githubkit.webhooks.models.SponsorshipPendingTierChange, githubkit.webhooks.models.SponsorshipTierChanged], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.StarCreated, githubkit.webhooks.models.StarDeleted], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], StatusEvent, Annotated[Union[githubkit.webhooks.models.TeamAddedToRepository, githubkit.webhooks.models.TeamCreated, githubkit.webhooks.models.TeamDeleted, githubkit.webhooks.models.TeamEdited, githubkit.webhooks.models.TeamRemovedFromRepository], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], TeamAddEvent, WatchStarted, WorkflowDispatchEvent, Annotated[Union[githubkit.webhooks.models.WorkflowJobCompleted, githubkit.webhooks.models.WorkflowJobInProgress, githubkit.webhooks.models.WorkflowJobQueued, githubkit.webhooks.models.WorkflowJobWaiting], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})], Annotated[Union[githubkit.webhooks.models.WorkflowRunCompleted, githubkit.webhooks.models.WorkflowRunInProgress, githubkit.webhooks.models.WorkflowRunRequested], FieldInfo(default=PydanticUndefined, discriminator='action', extra={})]]]

Is this use-case supported?

Feature: Add a link to the Github docs for each REST endpoint

I believe that Github provides a link to the Github docs for each REST endpoint in the api definition json so it would be possible to add a link to the docs for each Python endpoint function
For example

  async def async_get_branch(
      self,
      owner: str,
      repo: str,
      branch: str,
      *,
      headers: Optional[Dict[str, str]] = None,
  ) -> Response[BranchWithProtection]:
      """see more `https://docs.github.com/en/rest/branches/branches?apiVersion=2022-11-28#get-a-branch`"""
      from ..models import BasicError, BranchWithProtection

      url = f"/repos/{owner}/{repo}/branches/{branch}"

      headers = {"X-GitHub-Api-Version": self._REST_API_VERSION, **(headers or {})}

      return await self._github.arequest(
          "GET",
          url,
          headers=exclude_unset(headers),
          response_model=BranchWithProtection,
          error_models={
              "404": BasicError,
          },
      )

Bug: `pull request simple` field `head label` schema incorrect

from githubkit import GitHub, TokenAuthStrategy

g = GitHub(TokenAuthStrategy(<token>))

params = {'page': 430, 'per_page': 100, 'sort': 'asc', 'state': 'all'}
prs = g.rest.pulls.list(owner='home-assistant', repo='core', **params)

prs.parsed_data

Gives

Traceback (most recent call last):
  File "C:\Users\username\project\test_bug.py", line 9, in <module>
    for pr in prs.parsed_data:
              ^^^^^^^^^^^^^^^
  File "C:\Users\username\project\.venv\Lib\site-packages\githubkit\response.py", line 50, in parsed_data
    return parse_raw_as(self._data_model, self._response.content)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\username\project\.venv\Lib\site-packages\pydantic\tools.py", line 82, in parse_raw_as
    return parse_obj_as(type_, obj, type_name=type_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\username\project\.venv\Lib\site-packages\pydantic\tools.py", line 38, in parse_obj_as
    return model_type(__root__=obj).__root__
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\username\project\.venv\Lib\site-packages\pydantic\main.py", line 341, in __init__
    raise validation_error
pydantic.error_wrappers.ValidationError: 1 validation error for ParsingModel[List[githubkit.rest.models.PullRequestSimple]]
__root__ -> 56 -> head -> label
  none is not an allowed value (type=type_error.none.not_allowed)

Bug: action type error for webhook event of type `repository_dispatch`

backtrace

Traceback (most recent call last):
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/flask/app.py", line 2529, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/flask/app.py", line 1825, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/flask/app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/webhook_receiver/server.py", line 208, in github_hook
    event: WebhookEvent = parse(event_name, request.data)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/githubkit/versions/v2022_11_28/webhooks/_namespace.py", line 741, in parse
    return type_validate_json(Event, payload)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/criemen/repos/codeql-halo/.venv/lib/python3.11/site-packages/githubkit/compat.py", line 100, in type_validate_json
    return parse_raw_as(type_, data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "pydantic/tools.py", line 82, in pydantic.tools.parse_raw_as
  File "pydantic/tools.py", line 38, in pydantic.tools.parse_obj_as
  File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for ParsingModel[WebhookRepositoryDispatchSample]
__root__ -> action
  unexpected value; permitted: 'sample.collected' (type=value_error.const; given=halo_execute_dag_codeql_release_process; permitted=('sample.collected',))

The problem seems to stem from

action: Literal["sample.collected"]
being a literal, instead of allowing any strings.
The value comes from the docs, so presumably this is an error in the schema provided by GitHub.

Unexpected behavior when updating organization settings

I tried to use the following snippet to update a specific value in my org:

utils.print_debug("updating settings via rest API")
print(f"updating org {data}")
response = self._client.rest.orgs.update(org_id, **data)
utils.print_trace(f"{response.raw_request.url} {response.raw_request.headers} {response.raw_request.content}")

specifically, I wanted to update the web_commit_signoff_required parameter. However, when running the code, it looks like the request contains more parameters that will be updated, and this is also what I experience when looking at the GitHub Web UI. These additional properties got updated as well.

[DEBUG] updating settings via rest API
updating org {'web_commit_signoff_required': True}
[TRACE] https://api.github.com/orgs/OtterdogTest Headers({'host': 'api.github.com', 'accept-encoding': 'gzip, deflate', 'connection': 'keep-alive', 'user-agent': 'GitHubKit/Python', 'accept': 'application/vnd.github+json', 'x-github-api-version': '2022-11-28', 'content-length': '284', 'content-type': 'application/json', 'authorization': '[secure]'}) b'{"default_repository_permission": "read", "members_can_create_repositories": true, "members_can_create_pages": true, "members_can_create_public_pages": true, "members_can_create_private_pages": true, "members_can_fork_private_repositories": false, "web_commit_signoff_required": true}'

Now, I tried to understand from the code what could go possibly wrong but failed to do so. Do you have any insights what could happen here?

Feature: Support custom headers

Hey!

Many GitHub endpoints for events, including list_public_events, list_events_for_timeline, list_events, list_events_for_authenticated_user supports ETag and If-None-Match headers (see docs).

I think that it would be convenient if githubkit would support setting the If-None-Match header, and correct parsing of 304 Not Modified responses, as this is currently not possible with the generated methods.

Rate limiting mechanism

I decided to speed up my algorithm by sending multiple requests at the same time.
It works fine until it doesn't because of the rate limits

It would be nice if rate limits could be taken into account when sending lots of asynchronous requests.
Currently I am encountering secondary rate limits.
The returned response has status 403 and has a 'retry-after' header, maybe it can be used to temporarily stop sending async requests? and continue after the time has passed?

There is also the primary rate limit. Could the headers mentioned be used to coordinate sending the async requests?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.