https
GET
api.github.com
None
/repos/PyGithub/PyGithub/commits/f5f9756a1dd52a53820cc54927abb34725377987?per_page=3&page=1
{'Authorization': 'token private_token_removed', 'User-Agent': 'PyGithub/Python'}
None
200
[('Date', 'Wed, 08 Oct 2025 10:12:04 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With'), ('ETag', 'W/"0a3f82885dbe27f5c6e3c4f875494fd94ab07ab93f9b4dd36e8208c228010b42"'), ('Last-Modified', 'Tue, 02 Sep 2025 17:30:48 GMT'), ('github-authentication-token-expiration', '2025-11-13 21:31:56 +0100'), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('x-accepted-github-permissions', 'contents=read'), ('x-github-api-version-selected', '2022-11-28'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4970'), ('X-RateLimit-Reset', '1759920713'), ('X-RateLimit-Used', '30'), ('X-RateLimit-Resource', 'core'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('Transfer-Encoding', 'chunked'), ('Server', 'github.com'), ('X-GitHub-Request-Id', 'FBE7:31BC9:172DF10:15C2035:68E638F3')]
{"sha":"f5f9756a1dd52a53820cc54927abb34725377987","node_id":"C_kwDOADYVqtoAKGY1Zjk3NTZhMWRkNTJhNTM4MjBjYzU0OTI3YWJiMzQ3MjUzNzc5ODc","commit":{"author":{"name":"Enrico Minack","email":"github@enrico.minack.dev","date":"2025-09-02T17:30:48Z"},"committer":{"name":"GitHub","email":"noreply@github.com","date":"2025-09-02T17:30:48Z"},"message":"Use default type if known type is not supported (#3365)\n\nFixes #3364.","tree":{"sha":"d51a7e258bf5adfbdff698e2be0251e0ec7f13bf","url":"https://api.github.com/repos/PyGithub/PyGithub/git/trees/d51a7e258bf5adfbdff698e2be0251e0ec7f13bf"},"url":"https://api.github.com/repos/PyGithub/PyGithub/git/commits/f5f9756a1dd52a53820cc54927abb34725377987","comment_count":0,"verification":{"verified":true,"reason":"valid","signature":"-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJotynICRC1aQ7uu5UhlAAAWnYQAIZmJFTyICIsHim25qzt/tuy\nBanCChisBqeYqSYuDkWfbWfQvnZH+AGfEA3tuDwOTijgPav1Mbg9n698Q3YjKh9R\nIJ7eWyTF6isCCjoS5T4XGCyshWV8XvlsmLmQDrCLlsn4HKCE+cAsydbqNpzatsiN\n9pdsyk/yWGkVluchyz0X/PB10f9TzauiBrH+g96LxiS1KftsSy5Cdz4gZ5BEN07s\nV9gkS8gjttk0YvZVQNzKIcgfphDh/YbfThknGxlMUyveJsGaOHnagpbuKP6nIXWD\n5MakLMf1K7AmuUhGce/cEWI637eo5XXDqYn/1EKTZulJAhbVA6h/hzc94Oi96bnQ\nyIDSjrAH25aJSkjsHGmJEkUbb2gnEEoMtN1tZ4vQk2Ki8CnORDQh4PIJtIRNhF8w\nXTerRqd3CrSyyGa00cNKohU536a4xgIPZYkFD6x21DtB+iZI9twpOzuc3OGSMf/J\nhM6Oihc0NsQ8qtTzUP7tD7QoT0HjuT7A5lJJqpU2fzN8TN3+PC5nikLx2xpkmy5I\nWwYf0LyzlcSepZLws4urI6ITlIEoEVTqXA6dr6FSxISqzonZ4HPoM42Kve7UWhXt\nuXqP0NI054zali3z9HnmLzuuxLdOLJJ9evKZ8oxCJXoUEVj42EeGN4vqQd81t+iw\nnQAit7b3UnxRgcbAksP1\n=5+n7\n-----END PGP SIGNATURE-----\n","payload":"tree d51a7e258bf5adfbdff698e2be0251e0ec7f13bf\nparent 3ccecbb99d0d0f661f19684c8882d52b60450e64\nauthor Enrico Minack <github@enrico.minack.dev> 1756834248 +0200\ncommitter GitHub <noreply@github.com> 1756834248 +0200\n\nUse default type if known type is not supported (#3365)\n\nFixes #3364.","verified_at":"2025-09-02T17:30:48Z"}},"url":"https://api.github.com/repos/PyGithub/PyGithub/commits/f5f9756a1dd52a53820cc54927abb34725377987","html_url":"https://github.com/PyGithub/PyGithub/commit/f5f9756a1dd52a53820cc54927abb34725377987","comments_url":"https://api.github.com/repos/PyGithub/PyGithub/commits/f5f9756a1dd52a53820cc54927abb34725377987/comments","author":{"login":"EnricoMi","id":44700269,"node_id":"MDQ6VXNlcjQ0NzAwMjY5","avatar_url":"https://avatars.githubusercontent.com/u/44700269?v=4","gravatar_id":"","url":"https://api.github.com/users/EnricoMi","html_url":"https://github.com/EnricoMi","followers_url":"https://api.github.com/users/EnricoMi/followers","following_url":"https://api.github.com/users/EnricoMi/following{/other_user}","gists_url":"https://api.github.com/users/EnricoMi/gists{/gist_id}","starred_url":"https://api.github.com/users/EnricoMi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/EnricoMi/subscriptions","organizations_url":"https://api.github.com/users/EnricoMi/orgs","repos_url":"https://api.github.com/users/EnricoMi/repos","events_url":"https://api.github.com/users/EnricoMi/events{/privacy}","received_events_url":"https://api.github.com/users/EnricoMi/received_events","type":"User","user_view_type":"public","site_admin":false},"committer":{"login":"web-flow","id":19864447,"node_id":"MDQ6VXNlcjE5ODY0NDQ3","avatar_url":"https://avatars.githubusercontent.com/u/19864447?v=4","gravatar_id":"","url":"https://api.github.com/users/web-flow","html_url":"https://github.com/web-flow","followers_url":"https://api.github.com/users/web-flow/followers","following_url":"https://api.github.com/users/web-flow/following{/other_user}","gists_url":"https://api.github.com/users/web-flow/gists{/gist_id}","starred_url":"https://api.github.com/users/web-flow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/web-flow/subscriptions","organizations_url":"https://api.github.com/users/web-flow/orgs","repos_url":"https://api.github.com/users/web-flow/repos","events_url":"https://api.github.com/users/web-flow/events{/privacy}","received_events_url":"https://api.github.com/users/web-flow/received_events","type":"User","user_view_type":"public","site_admin":false},"parents":[{"sha":"3ccecbb99d0d0f661f19684c8882d52b60450e64","url":"https://api.github.com/repos/PyGithub/PyGithub/commits/3ccecbb99d0d0f661f19684c8882d52b60450e64","html_url":"https://github.com/PyGithub/PyGithub/commit/3ccecbb99d0d0f661f19684c8882d52b60450e64"}],"stats":{"total":123,"additions":119,"deletions":4},"files":[{"sha":"1e442612c22db72febe809d7afeccb56264026f4","filename":"github/GithubApp.py","status":"modified","additions":1,"deletions":1,"changes":2,"blob_url":"https://github.com/PyGithub/PyGithub/blob/f5f9756a1dd52a53820cc54927abb34725377987/github%2FGithubApp.py","raw_url":"https://github.com/PyGithub/PyGithub/raw/f5f9756a1dd52a53820cc54927abb34725377987/github%2FGithubApp.py","contents_url":"https://api.github.com/repos/PyGithub/PyGithub/contents/github%2FGithubApp.py?ref=f5f9756a1dd52a53820cc54927abb34725377987","patch":"@@ -211,7 +211,7 @@ def _useAttributes(self, attributes: dict[str, Any]) -> None:\n         if \"owner\" in attributes:  # pragma no branch\n             self._owner = self._makeUnionClassAttributeFromTypeKey(\n                 \"type\",\n-                \"Enterprise\",\n+                \"User\",\n                 attributes[\"owner\"],\n                 (github.NamedUser.NamedUser, \"User\"),\n                 (github.Organization.Organization, \"Organization\"),"},{"sha":"cd33008621af2ac0bedf189cf77ff6ab0250ceec","filename":"github/GithubObject.py","status":"modified","additions":15,"deletions":3,"changes":18,"blob_url":"https://github.com/PyGithub/PyGithub/blob/f5f9756a1dd52a53820cc54927abb34725377987/github%2FGithubObject.py","raw_url":"https://github.com/PyGithub/PyGithub/raw/f5f9756a1dd52a53820cc54927abb34725377987/github%2FGithubObject.py","contents_url":"https://api.github.com/repos/PyGithub/PyGithub/contents/github%2FGithubObject.py?ref=f5f9756a1dd52a53820cc54927abb34725377987","patch":"@@ -370,13 +370,23 @@ def _makeClassAttribute(self, klass: type[T_gh], value: Any) -> Attribute[T_gh]:\n         )\n \n     def _makeUnionClassAttributeFromTypeName(\n-        self, type_name: str | None, value: Any, *class_and_names: tuple[type[T_gh], str]\n+        self, type_name: str | None, fallback_type: str | None, value: Any, *class_and_names: tuple[type[T_gh], str]\n     ) -> Attribute[T_gh]:\n         if value is None or type_name is None:\n             return _ValuedAttribute(None)  # type: ignore\n+        fallback_class = None\n         for klass, name in class_and_names:\n             if type_name == name:\n                 return self._makeClassAttribute(klass, value)\n+            if fallback_type == name:\n+                fallback_class = klass\n+        if fallback_type is not None:\n+            if fallback_class is None:\n+                # this is misconfiguration in PyGithub code, not a user's fault\n+                raise ValueError(\n+                    f\"Fallback type {fallback_type} is not among classes and names: {[name for klass, name in class_and_names]}\"\n+                )\n+            return self._makeClassAttribute(fallback_class, value)\n         return _BadAttribute(value, type)  # type: ignore\n \n     def _makeUnionClassAttributeFromTypeKey(\n@@ -388,7 +398,9 @@ def _makeUnionClassAttributeFromTypeKey(\n     ) -> Attribute[T_gh]:\n         if value is None or not isinstance(value, dict):\n             return _ValuedAttribute(None)  # type: ignore\n-        return self._makeUnionClassAttributeFromTypeName(value.get(type_key, default_type), value, *class_and_names)\n+        return self._makeUnionClassAttributeFromTypeName(\n+            value.get(type_key, default_type), default_type, value, *class_and_names\n+        )\n \n     def _makeUnionClassAttributeFromTypeKeyAndValueKey(\n         self,\n@@ -401,7 +413,7 @@ def _makeUnionClassAttributeFromTypeKeyAndValueKey(\n         if value is None or not isinstance(value, dict):\n             return _ValuedAttribute(None)  # type: ignore\n         return self._makeUnionClassAttributeFromTypeName(\n-            value.get(type_key, default_type), value.get(value_key), *class_and_names\n+            value.get(type_key, default_type), default_type, value.get(value_key), *class_and_names\n         )\n \n     @staticmethod"},{"sha":"173f14bad62d8eafa2ee04287e8ef278c9106d4a","filename":"tests/GithubObject.py","status":"modified","additions":103,"deletions":0,"changes":103,"blob_url":"https://github.com/PyGithub/PyGithub/blob/f5f9756a1dd52a53820cc54927abb34725377987/tests%2FGithubObject.py","raw_url":"https://github.com/PyGithub/PyGithub/raw/f5f9756a1dd52a53820cc54927abb34725377987/tests%2FGithubObject.py","contents_url":"https://api.github.com/repos/PyGithub/PyGithub/contents/tests%2FGithubObject.py?ref=f5f9756a1dd52a53820cc54927abb34725377987","patch":"@@ -24,15 +24,21 @@\n #                                                                              #\n ################################################################################\n \n+from __future__ import annotations\n+\n import unittest\n from datetime import datetime, timedelta, timezone\n+from typing import Any\n+from unittest import mock\n \n import github.Repository\n import github.RepositoryDiscussion\n \n from . import Framework\n \n gho = Framework.github.GithubObject\n+ghusr = Framework.github.NamedUser\n+ghorg = Framework.github.Organization\n \n \n class GithubObject(unittest.TestCase):\n@@ -52,6 +58,95 @@ def testApiType(self):\n         self.assertEqual(github.RepositoryDiscussion.RepositoryDiscussion.is_rest(), False)\n         self.assertEqual(github.RepositoryDiscussion.RepositoryDiscussion.is_graphql(), True)\n \n+    def testMakeUnionClassAttributeFromTypeName(self):\n+        req = mock.Mock(is_not_lazy=False)\n+        obj = TestingClass(req, {}, {})\n+\n+        data = {\"login\": \"login\"}\n+        class_and_names = [(ghusr.NamedUser, \"User\"), (ghorg.Organization, \"Organization\")]\n+\n+        def make(type_name: str | None, fallback_type: str | None = \"User\"):\n+            return obj._makeUnionClassAttributeFromTypeName(type_name, fallback_type, data, *class_and_names)\n+\n+        none = make(None)\n+        usr = make(\"User\")\n+        org = make(\"Organization\")\n+        unknown = make(\"Unknown\")\n+        bad = make(\"Unknown\", None)\n+\n+        self.assertIsInstance(none, gho._ValuedAttribute)\n+        self.assertIsInstance(usr, gho._ValuedAttribute)\n+        self.assertIsInstance(org, gho._ValuedAttribute)\n+        self.assertIsInstance(unknown, gho._ValuedAttribute)\n+        self.assertIsInstance(bad, gho._BadAttribute)\n+\n+        self.assertIsNone(none.value)\n+        self.assertIsInstance(usr.value, ghusr.NamedUser)\n+        self.assertIsInstance(org.value, ghorg.Organization)\n+        self.assertIsInstance(unknown.value, ghusr.NamedUser)\n+\n+        self.assertEqual(str(usr.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(org.value), 'Organization(login=\"login\")')\n+        self.assertEqual(str(unknown.value), 'NamedUser(login=\"login\")')\n+\n+    def testMakeUnionClassAttributeFromTypeKey(self):\n+        req = mock.Mock(is_not_lazy=False)\n+        obj = TestingClass(req, {}, {})\n+\n+        class_and_names = [(ghusr.NamedUser, \"User\"), (ghorg.Organization, \"Organization\")]\n+\n+        def make(data: dict[str, Any]):\n+            return obj._makeUnionClassAttributeFromTypeKey(\"type\", \"User\", data, *class_and_names)\n+\n+        default = make({\"login\": \"login\"})\n+        usr = make({\"login\": \"login\", \"type\": \"User\"})\n+        org = make({\"login\": \"login\", \"type\": \"Organization\"})\n+        unknown = make({\"login\": \"login\", \"type\": \"Unknown\"})\n+\n+        self.assertIsInstance(default, gho._ValuedAttribute)\n+        self.assertIsInstance(usr, gho._ValuedAttribute)\n+        self.assertIsInstance(org, gho._ValuedAttribute)\n+        self.assertIsInstance(unknown, gho._ValuedAttribute)\n+\n+        self.assertIsInstance(default.value, ghusr.NamedUser)\n+        self.assertIsInstance(usr.value, ghusr.NamedUser)\n+        self.assertIsInstance(org.value, ghorg.Organization)\n+        self.assertIsInstance(unknown.value, ghusr.NamedUser)\n+\n+        self.assertEqual(str(default.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(usr.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(org.value), 'Organization(login=\"login\")')\n+        self.assertEqual(str(unknown.value), 'NamedUser(login=\"login\")')\n+\n+    def testMakeUnionClassAttributeFromTypeKeyAndValueKey(self):\n+        req = mock.Mock(is_not_lazy=False)\n+        obj = TestingClass(req, {}, {})\n+\n+        class_and_names = [(ghusr.NamedUser, \"User\"), (ghorg.Organization, \"Organization\")]\n+\n+        def make(data: dict[str, Any]):\n+            return obj._makeUnionClassAttributeFromTypeKeyAndValueKey(\"type\", \"data\", \"User\", data, *class_and_names)\n+\n+        default = make({\"data\": {\"login\": \"login\"}})\n+        usr = make({\"data\": {\"login\": \"login\"}, \"type\": \"User\"})\n+        org = make({\"data\": {\"login\": \"login\"}, \"type\": \"Organization\"})\n+        unknown = make({\"data\": {\"login\": \"login\"}, \"type\": \"Unknown\"})\n+\n+        self.assertIsInstance(default, gho._ValuedAttribute)\n+        self.assertIsInstance(usr, gho._ValuedAttribute)\n+        self.assertIsInstance(org, gho._ValuedAttribute)\n+        self.assertIsInstance(unknown, gho._ValuedAttribute)\n+\n+        self.assertIsInstance(default.value, ghusr.NamedUser)\n+        self.assertIsInstance(usr.value, ghusr.NamedUser)\n+        self.assertIsInstance(org.value, ghorg.Organization)\n+        self.assertIsInstance(unknown.value, ghusr.NamedUser)\n+\n+        self.assertEqual(str(default.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(usr.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(org.value), 'Organization(login=\"login\")')\n+        self.assertEqual(str(unknown.value), 'NamedUser(login=\"login\")')\n+\n     def testMakeDatetimeAttribute(self):\n         for value, expected in [\n             (None, None),\n@@ -145,3 +240,11 @@ def testMakeTimetsampAttributeBadValues(self):\n             self.assertEqual(value, e.exception.actual_value)\n             self.assertEqual(int, e.exception.expected_type)\n             self.assertIsNone(e.exception.transformation_exception)\n+\n+\n+class TestingClass(gho.NonCompletableGithubObject):\n+    def _initAttributes(self) -> None:\n+        pass\n+\n+    def _useAttributes(self, attributes: Any) -> None:\n+        pass"}]}

https
GET
api.github.com
None
/repos/PyGithub/PyGithub/commits/f5f9756a1dd52a53820cc54927abb34725377987
{'Authorization': 'token private_token_removed', 'User-Agent': 'PyGithub/Python'}
None
200
[('Date', 'Wed, 08 Oct 2025 10:12:04 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With'), ('ETag', 'W/"0a3f82885dbe27f5c6e3c4f875494fd94ab07ab93f9b4dd36e8208c228010b42"'), ('Last-Modified', 'Tue, 02 Sep 2025 17:30:48 GMT'), ('github-authentication-token-expiration', '2025-11-13 21:31:56 +0100'), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('x-accepted-github-permissions', 'contents=read'), ('x-github-api-version-selected', '2022-11-28'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4969'), ('X-RateLimit-Reset', '1759920713'), ('X-RateLimit-Used', '31'), ('X-RateLimit-Resource', 'core'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('Transfer-Encoding', 'chunked'), ('Server', 'github.com'), ('X-GitHub-Request-Id', 'F175:1CFE8:1769A81:15F46DD:68E638F4')]
{"sha":"f5f9756a1dd52a53820cc54927abb34725377987","node_id":"C_kwDOADYVqtoAKGY1Zjk3NTZhMWRkNTJhNTM4MjBjYzU0OTI3YWJiMzQ3MjUzNzc5ODc","commit":{"author":{"name":"Enrico Minack","email":"github@enrico.minack.dev","date":"2025-09-02T17:30:48Z"},"committer":{"name":"GitHub","email":"noreply@github.com","date":"2025-09-02T17:30:48Z"},"message":"Use default type if known type is not supported (#3365)\n\nFixes #3364.","tree":{"sha":"d51a7e258bf5adfbdff698e2be0251e0ec7f13bf","url":"https://api.github.com/repos/PyGithub/PyGithub/git/trees/d51a7e258bf5adfbdff698e2be0251e0ec7f13bf"},"url":"https://api.github.com/repos/PyGithub/PyGithub/git/commits/f5f9756a1dd52a53820cc54927abb34725377987","comment_count":0,"verification":{"verified":true,"reason":"valid","signature":"-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJotynICRC1aQ7uu5UhlAAAWnYQAIZmJFTyICIsHim25qzt/tuy\nBanCChisBqeYqSYuDkWfbWfQvnZH+AGfEA3tuDwOTijgPav1Mbg9n698Q3YjKh9R\nIJ7eWyTF6isCCjoS5T4XGCyshWV8XvlsmLmQDrCLlsn4HKCE+cAsydbqNpzatsiN\n9pdsyk/yWGkVluchyz0X/PB10f9TzauiBrH+g96LxiS1KftsSy5Cdz4gZ5BEN07s\nV9gkS8gjttk0YvZVQNzKIcgfphDh/YbfThknGxlMUyveJsGaOHnagpbuKP6nIXWD\n5MakLMf1K7AmuUhGce/cEWI637eo5XXDqYn/1EKTZulJAhbVA6h/hzc94Oi96bnQ\nyIDSjrAH25aJSkjsHGmJEkUbb2gnEEoMtN1tZ4vQk2Ki8CnORDQh4PIJtIRNhF8w\nXTerRqd3CrSyyGa00cNKohU536a4xgIPZYkFD6x21DtB+iZI9twpOzuc3OGSMf/J\nhM6Oihc0NsQ8qtTzUP7tD7QoT0HjuT7A5lJJqpU2fzN8TN3+PC5nikLx2xpkmy5I\nWwYf0LyzlcSepZLws4urI6ITlIEoEVTqXA6dr6FSxISqzonZ4HPoM42Kve7UWhXt\nuXqP0NI054zali3z9HnmLzuuxLdOLJJ9evKZ8oxCJXoUEVj42EeGN4vqQd81t+iw\nnQAit7b3UnxRgcbAksP1\n=5+n7\n-----END PGP SIGNATURE-----\n","payload":"tree d51a7e258bf5adfbdff698e2be0251e0ec7f13bf\nparent 3ccecbb99d0d0f661f19684c8882d52b60450e64\nauthor Enrico Minack <github@enrico.minack.dev> 1756834248 +0200\ncommitter GitHub <noreply@github.com> 1756834248 +0200\n\nUse default type if known type is not supported (#3365)\n\nFixes #3364.","verified_at":"2025-09-02T17:30:48Z"}},"url":"https://api.github.com/repos/PyGithub/PyGithub/commits/f5f9756a1dd52a53820cc54927abb34725377987","html_url":"https://github.com/PyGithub/PyGithub/commit/f5f9756a1dd52a53820cc54927abb34725377987","comments_url":"https://api.github.com/repos/PyGithub/PyGithub/commits/f5f9756a1dd52a53820cc54927abb34725377987/comments","author":{"login":"EnricoMi","id":44700269,"node_id":"MDQ6VXNlcjQ0NzAwMjY5","avatar_url":"https://avatars.githubusercontent.com/u/44700269?v=4","gravatar_id":"","url":"https://api.github.com/users/EnricoMi","html_url":"https://github.com/EnricoMi","followers_url":"https://api.github.com/users/EnricoMi/followers","following_url":"https://api.github.com/users/EnricoMi/following{/other_user}","gists_url":"https://api.github.com/users/EnricoMi/gists{/gist_id}","starred_url":"https://api.github.com/users/EnricoMi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/EnricoMi/subscriptions","organizations_url":"https://api.github.com/users/EnricoMi/orgs","repos_url":"https://api.github.com/users/EnricoMi/repos","events_url":"https://api.github.com/users/EnricoMi/events{/privacy}","received_events_url":"https://api.github.com/users/EnricoMi/received_events","type":"User","user_view_type":"public","site_admin":false},"committer":{"login":"web-flow","id":19864447,"node_id":"MDQ6VXNlcjE5ODY0NDQ3","avatar_url":"https://avatars.githubusercontent.com/u/19864447?v=4","gravatar_id":"","url":"https://api.github.com/users/web-flow","html_url":"https://github.com/web-flow","followers_url":"https://api.github.com/users/web-flow/followers","following_url":"https://api.github.com/users/web-flow/following{/other_user}","gists_url":"https://api.github.com/users/web-flow/gists{/gist_id}","starred_url":"https://api.github.com/users/web-flow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/web-flow/subscriptions","organizations_url":"https://api.github.com/users/web-flow/orgs","repos_url":"https://api.github.com/users/web-flow/repos","events_url":"https://api.github.com/users/web-flow/events{/privacy}","received_events_url":"https://api.github.com/users/web-flow/received_events","type":"User","user_view_type":"public","site_admin":false},"parents":[{"sha":"3ccecbb99d0d0f661f19684c8882d52b60450e64","url":"https://api.github.com/repos/PyGithub/PyGithub/commits/3ccecbb99d0d0f661f19684c8882d52b60450e64","html_url":"https://github.com/PyGithub/PyGithub/commit/3ccecbb99d0d0f661f19684c8882d52b60450e64"}],"stats":{"total":123,"additions":119,"deletions":4},"files":[{"sha":"1e442612c22db72febe809d7afeccb56264026f4","filename":"github/GithubApp.py","status":"modified","additions":1,"deletions":1,"changes":2,"blob_url":"https://github.com/PyGithub/PyGithub/blob/f5f9756a1dd52a53820cc54927abb34725377987/github%2FGithubApp.py","raw_url":"https://github.com/PyGithub/PyGithub/raw/f5f9756a1dd52a53820cc54927abb34725377987/github%2FGithubApp.py","contents_url":"https://api.github.com/repos/PyGithub/PyGithub/contents/github%2FGithubApp.py?ref=f5f9756a1dd52a53820cc54927abb34725377987","patch":"@@ -211,7 +211,7 @@ def _useAttributes(self, attributes: dict[str, Any]) -> None:\n         if \"owner\" in attributes:  # pragma no branch\n             self._owner = self._makeUnionClassAttributeFromTypeKey(\n                 \"type\",\n-                \"Enterprise\",\n+                \"User\",\n                 attributes[\"owner\"],\n                 (github.NamedUser.NamedUser, \"User\"),\n                 (github.Organization.Organization, \"Organization\"),"},{"sha":"cd33008621af2ac0bedf189cf77ff6ab0250ceec","filename":"github/GithubObject.py","status":"modified","additions":15,"deletions":3,"changes":18,"blob_url":"https://github.com/PyGithub/PyGithub/blob/f5f9756a1dd52a53820cc54927abb34725377987/github%2FGithubObject.py","raw_url":"https://github.com/PyGithub/PyGithub/raw/f5f9756a1dd52a53820cc54927abb34725377987/github%2FGithubObject.py","contents_url":"https://api.github.com/repos/PyGithub/PyGithub/contents/github%2FGithubObject.py?ref=f5f9756a1dd52a53820cc54927abb34725377987","patch":"@@ -370,13 +370,23 @@ def _makeClassAttribute(self, klass: type[T_gh], value: Any) -> Attribute[T_gh]:\n         )\n \n     def _makeUnionClassAttributeFromTypeName(\n-        self, type_name: str | None, value: Any, *class_and_names: tuple[type[T_gh], str]\n+        self, type_name: str | None, fallback_type: str | None, value: Any, *class_and_names: tuple[type[T_gh], str]\n     ) -> Attribute[T_gh]:\n         if value is None or type_name is None:\n             return _ValuedAttribute(None)  # type: ignore\n+        fallback_class = None\n         for klass, name in class_and_names:\n             if type_name == name:\n                 return self._makeClassAttribute(klass, value)\n+            if fallback_type == name:\n+                fallback_class = klass\n+        if fallback_type is not None:\n+            if fallback_class is None:\n+                # this is misconfiguration in PyGithub code, not a user's fault\n+                raise ValueError(\n+                    f\"Fallback type {fallback_type} is not among classes and names: {[name for klass, name in class_and_names]}\"\n+                )\n+            return self._makeClassAttribute(fallback_class, value)\n         return _BadAttribute(value, type)  # type: ignore\n \n     def _makeUnionClassAttributeFromTypeKey(\n@@ -388,7 +398,9 @@ def _makeUnionClassAttributeFromTypeKey(\n     ) -> Attribute[T_gh]:\n         if value is None or not isinstance(value, dict):\n             return _ValuedAttribute(None)  # type: ignore\n-        return self._makeUnionClassAttributeFromTypeName(value.get(type_key, default_type), value, *class_and_names)\n+        return self._makeUnionClassAttributeFromTypeName(\n+            value.get(type_key, default_type), default_type, value, *class_and_names\n+        )\n \n     def _makeUnionClassAttributeFromTypeKeyAndValueKey(\n         self,\n@@ -401,7 +413,7 @@ def _makeUnionClassAttributeFromTypeKeyAndValueKey(\n         if value is None or not isinstance(value, dict):\n             return _ValuedAttribute(None)  # type: ignore\n         return self._makeUnionClassAttributeFromTypeName(\n-            value.get(type_key, default_type), value.get(value_key), *class_and_names\n+            value.get(type_key, default_type), default_type, value.get(value_key), *class_and_names\n         )\n \n     @staticmethod"},{"sha":"173f14bad62d8eafa2ee04287e8ef278c9106d4a","filename":"tests/GithubObject.py","status":"modified","additions":103,"deletions":0,"changes":103,"blob_url":"https://github.com/PyGithub/PyGithub/blob/f5f9756a1dd52a53820cc54927abb34725377987/tests%2FGithubObject.py","raw_url":"https://github.com/PyGithub/PyGithub/raw/f5f9756a1dd52a53820cc54927abb34725377987/tests%2FGithubObject.py","contents_url":"https://api.github.com/repos/PyGithub/PyGithub/contents/tests%2FGithubObject.py?ref=f5f9756a1dd52a53820cc54927abb34725377987","patch":"@@ -24,15 +24,21 @@\n #                                                                              #\n ################################################################################\n \n+from __future__ import annotations\n+\n import unittest\n from datetime import datetime, timedelta, timezone\n+from typing import Any\n+from unittest import mock\n \n import github.Repository\n import github.RepositoryDiscussion\n \n from . import Framework\n \n gho = Framework.github.GithubObject\n+ghusr = Framework.github.NamedUser\n+ghorg = Framework.github.Organization\n \n \n class GithubObject(unittest.TestCase):\n@@ -52,6 +58,95 @@ def testApiType(self):\n         self.assertEqual(github.RepositoryDiscussion.RepositoryDiscussion.is_rest(), False)\n         self.assertEqual(github.RepositoryDiscussion.RepositoryDiscussion.is_graphql(), True)\n \n+    def testMakeUnionClassAttributeFromTypeName(self):\n+        req = mock.Mock(is_not_lazy=False)\n+        obj = TestingClass(req, {}, {})\n+\n+        data = {\"login\": \"login\"}\n+        class_and_names = [(ghusr.NamedUser, \"User\"), (ghorg.Organization, \"Organization\")]\n+\n+        def make(type_name: str | None, fallback_type: str | None = \"User\"):\n+            return obj._makeUnionClassAttributeFromTypeName(type_name, fallback_type, data, *class_and_names)\n+\n+        none = make(None)\n+        usr = make(\"User\")\n+        org = make(\"Organization\")\n+        unknown = make(\"Unknown\")\n+        bad = make(\"Unknown\", None)\n+\n+        self.assertIsInstance(none, gho._ValuedAttribute)\n+        self.assertIsInstance(usr, gho._ValuedAttribute)\n+        self.assertIsInstance(org, gho._ValuedAttribute)\n+        self.assertIsInstance(unknown, gho._ValuedAttribute)\n+        self.assertIsInstance(bad, gho._BadAttribute)\n+\n+        self.assertIsNone(none.value)\n+        self.assertIsInstance(usr.value, ghusr.NamedUser)\n+        self.assertIsInstance(org.value, ghorg.Organization)\n+        self.assertIsInstance(unknown.value, ghusr.NamedUser)\n+\n+        self.assertEqual(str(usr.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(org.value), 'Organization(login=\"login\")')\n+        self.assertEqual(str(unknown.value), 'NamedUser(login=\"login\")')\n+\n+    def testMakeUnionClassAttributeFromTypeKey(self):\n+        req = mock.Mock(is_not_lazy=False)\n+        obj = TestingClass(req, {}, {})\n+\n+        class_and_names = [(ghusr.NamedUser, \"User\"), (ghorg.Organization, \"Organization\")]\n+\n+        def make(data: dict[str, Any]):\n+            return obj._makeUnionClassAttributeFromTypeKey(\"type\", \"User\", data, *class_and_names)\n+\n+        default = make({\"login\": \"login\"})\n+        usr = make({\"login\": \"login\", \"type\": \"User\"})\n+        org = make({\"login\": \"login\", \"type\": \"Organization\"})\n+        unknown = make({\"login\": \"login\", \"type\": \"Unknown\"})\n+\n+        self.assertIsInstance(default, gho._ValuedAttribute)\n+        self.assertIsInstance(usr, gho._ValuedAttribute)\n+        self.assertIsInstance(org, gho._ValuedAttribute)\n+        self.assertIsInstance(unknown, gho._ValuedAttribute)\n+\n+        self.assertIsInstance(default.value, ghusr.NamedUser)\n+        self.assertIsInstance(usr.value, ghusr.NamedUser)\n+        self.assertIsInstance(org.value, ghorg.Organization)\n+        self.assertIsInstance(unknown.value, ghusr.NamedUser)\n+\n+        self.assertEqual(str(default.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(usr.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(org.value), 'Organization(login=\"login\")')\n+        self.assertEqual(str(unknown.value), 'NamedUser(login=\"login\")')\n+\n+    def testMakeUnionClassAttributeFromTypeKeyAndValueKey(self):\n+        req = mock.Mock(is_not_lazy=False)\n+        obj = TestingClass(req, {}, {})\n+\n+        class_and_names = [(ghusr.NamedUser, \"User\"), (ghorg.Organization, \"Organization\")]\n+\n+        def make(data: dict[str, Any]):\n+            return obj._makeUnionClassAttributeFromTypeKeyAndValueKey(\"type\", \"data\", \"User\", data, *class_and_names)\n+\n+        default = make({\"data\": {\"login\": \"login\"}})\n+        usr = make({\"data\": {\"login\": \"login\"}, \"type\": \"User\"})\n+        org = make({\"data\": {\"login\": \"login\"}, \"type\": \"Organization\"})\n+        unknown = make({\"data\": {\"login\": \"login\"}, \"type\": \"Unknown\"})\n+\n+        self.assertIsInstance(default, gho._ValuedAttribute)\n+        self.assertIsInstance(usr, gho._ValuedAttribute)\n+        self.assertIsInstance(org, gho._ValuedAttribute)\n+        self.assertIsInstance(unknown, gho._ValuedAttribute)\n+\n+        self.assertIsInstance(default.value, ghusr.NamedUser)\n+        self.assertIsInstance(usr.value, ghusr.NamedUser)\n+        self.assertIsInstance(org.value, ghorg.Organization)\n+        self.assertIsInstance(unknown.value, ghusr.NamedUser)\n+\n+        self.assertEqual(str(default.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(usr.value), 'NamedUser(login=\"login\")')\n+        self.assertEqual(str(org.value), 'Organization(login=\"login\")')\n+        self.assertEqual(str(unknown.value), 'NamedUser(login=\"login\")')\n+\n     def testMakeDatetimeAttribute(self):\n         for value, expected in [\n             (None, None),\n@@ -145,3 +240,11 @@ def testMakeTimetsampAttributeBadValues(self):\n             self.assertEqual(value, e.exception.actual_value)\n             self.assertEqual(int, e.exception.expected_type)\n             self.assertIsNone(e.exception.transformation_exception)\n+\n+\n+class TestingClass(gho.NonCompletableGithubObject):\n+    def _initAttributes(self) -> None:\n+        pass\n+\n+    def _useAttributes(self, attributes: Any) -> None:\n+        pass"}]}
