Repository: PyGithub/PyGithub

Issue:
Bug in repr(PublicKey)
hi there.

there is a bug in "repo.get_public_key".

i found this bug by trying to use "repo.create_secret", which failed because of this issue

```
repo.get_public_key()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/Users/sschultchen/PycharmProjects/IAC_Main/venv/lib/python3.9/site-packages/github/PublicKey.py", line 55, in __repr__
    return self.get__repr__({"key_id": self._key_id.value, "key": self._key.value})
  File "/Users/sschultchen/PycharmProjects/IAC_Main/venv/lib/python3.9/site-packages/github/GithubObject.py", line 62, in value
    raise GithubException.BadAttributeException(
github.GithubException.BadAttributeException: (1, <class 'str'>, None)
```

i guess the reason for this might be that the pygithub implementation assumes that "key_id" is a string, but it actually is a integer.

at least, the github api gives me an integer, and not a string for this attribute.


Discussion:
Just testing this myself on the public GitHub, I get back key_id as a string?
hmm, i tested this against a github enterprise instance. i can check again tomorrow, and maybe attach the result from the get request.
Sounds great, thanks.
here is the output of 'repo._requester.requestJsonAndCheck("GET", f"{repo.url}/actions/secrets/public-key")'

there you can see that "key_id" is an integer.

i also checked with github.com, and there key_id is indeed a string

```
[
    {
        "access-control-allow-origin": "*",
        "access-control-expose-headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset",
        "cache-control": "private, max-age=60, s-maxage=60",
        "content-encoding": "gzip",
        "content-security-policy": "default-src 'none'",
        "content-type": "application/json; charset=utf-8",
        "date": "Fri, 19 Mar 2021 07:13:08 GMT",
        "etag": "W/\"db09a86f9a30ab1d2f7e0239a47718a3\"",
        "referrer-policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
        "server": "GitHub.com",
        "status": "200 OK",
        "strict-transport-security": "max-age=31536000; includeSubdomains",
        "transfer-encoding": "chunked",
        "vary": "Accept, Authorization, Cookie, X-GitHub-OTP",
        "x-accepted-oauth-scopes": "",
        "x-content-type-options": "nosniff",
        "x-frame-options": "deny",
        "x-github-enterprise-version": "2.22.6",
        "x-github-media-type": "github.v3; format=json",
        "x-github-request-id": "627be4e4-56d1-42da-88cf-1c09a2dde2d3",
        "x-oauth-scopes": "admin:gpg_key, admin:org, admin:org_hook, admin:pre_receive_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
        "x-ratelimit-limit": "5000",
        "x-ratelimit-remaining": "4997",
        "x-ratelimit-reset": "1616141558",
        "x-runtime-rack": "0.035888",
        "x-xss-protection": "1; mode=block"
    },
    {
        "key": "<secret_removed>",
        "key_id": 1
    }
]
```
as a quickfix, i replaces the "repo.get_public_key" method with this:

```
    def get_public_key(self):
        """
        :calls: `GET /repos/:owner/:repo/actions/secrets/public-key <https://docs.github.com/en/rest/reference/actions#get-a-repository-public-key>`_
        :rtype: :class:`github.PublicKey.PublicKey`
        """
        headers, data = self._requester.requestJsonAndCheck(
            "GET", f"{self.url}/actions/secrets/public-key"
        )
        data['key_id'] = str(data['key_id'])
        return github.PublicKey.PublicKey(
            self._requester, headers, data, completed=True
        )
```

the relevant change here is this:

```
data['key_id'] = str(data['key_id'])
```
Ah, I'm sorry, I missed your helpful debugging.

That is a workaround, not a bad one, but I think the correct thing to do in this case is say key_id can be int or str and deal correctly in either case.
whatever you see fit. i would offer to do it myself, but i have no clue how a proper implementation would look like to match the style of this lib.

Resolving patch:
diff --git a/github/PublicKey.py b/github/PublicKey.py
--- a/github/PublicKey.py
+++ b/github/PublicKey.py
@@ -65,7 +65,7 @@ def key(self):
     @property
     def key_id(self):
         """
-        :type: string
+        :type: string or int
         """
         self._completeIfNotSet(self._key_id)
         return self._key_id.value
@@ -78,7 +78,10 @@ def _useAttributes(self, attributes):
         if "key" in attributes:  # pragma no branch
             self._key = self._makeStringAttribute(attributes["key"])
         if "key_id" in attributes:  # pragma no branch
-            self._key_id = self._makeStringAttribute(attributes["key_id"])
+            if type(attributes["key_id"]) == str:
+                self._key_id = self._makeStringAttribute(attributes["key_id"])
+            else:
+                self._key_id = self._makeIntAttribute(attributes["key_id"])
 
     def encrypt(self, unencrypted_value):
         return encrypt(self._key.value, unencrypted_value)


Test patch:
diff --git a/tests/PublicKey.py b/tests/PublicKey.py
--- a/tests/PublicKey.py
+++ b/tests/PublicKey.py
@@ -31,11 +31,8 @@
 
 
 class PublicKey(Framework.TestCase):
-    def setUp(self):
-        super().setUp()
-        self.public_key = self.g.get_user().get_repo("PyGithub").get_public_key()
-
     def testAttributes(self):
+        self.public_key = self.g.get_user().get_repo("PyGithub").get_public_key()
         self.assertEqual(
             self.public_key.key, "u5e1Z25+z8pmgVVt5Pd8k0z/sKpVL1MXYtRAecE4vm8="
         )
@@ -44,3 +41,14 @@ def testAttributes(self):
             repr(self.public_key),
             'PublicKey(key_id="568250167242549743", key="u5e1Z25+z8pmgVVt5Pd8k0z/sKpVL1MXYtRAecE4vm8=")',
         )
+
+    def testAttributes_with_int_key_id(self):
+        self.public_key = self.g.get_user().get_repo("PyGithub").get_public_key()
+        self.assertEqual(
+            self.public_key.key, "u5e1Z25+z8pmgVVt5Pd8k0z/sKpVL1MXYtRAecE4vm8="
+        )
+        self.assertEqual(self.public_key.key_id, 568250167242549743)
+        self.assertEqual(
+            repr(self.public_key),
+            'PublicKey(key_id=568250167242549743, key="u5e1Z25+z8pmgVVt5Pd8k0z/sKpVL1MXYtRAecE4vm8=")',
+        )
diff --git a/tests/ReplayData/PublicKey.setUp.txt b/tests/ReplayData/PublicKey.testAttributes.txt
similarity index 100%
rename from tests/ReplayData/PublicKey.setUp.txt
rename to tests/ReplayData/PublicKey.testAttributes.txt
diff --git a/tests/ReplayData/PublicKey.testAttributes_with_int_key_id.txt b/tests/ReplayData/PublicKey.testAttributes_with_int_key_id.txt
new file mode 100644
--- /dev/null
+++ b/tests/ReplayData/PublicKey.testAttributes_with_int_key_id.txt
@@ -0,0 +1,32 @@
+https
+GET
+api.github.com
+None
+/user
+{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
+None
+200
+[('status', '200 OK'), ('x-ratelimit-remaining', '4980'), ('content-length', '801'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"fc8367028bd9046f0b52929ea8657756"'), ('date', 'Thu, 10 May 2012 19:03:17 GMT'), ('content-type', 'application/json; charset=utf-8')]
+{"type":"User","owned_private_repos":5,"public_repos":10,"html_url":"https://github.com/jacquev6","blog":"http://vincent-jacques.net","collaborators":0,"following":24,"company":"Criteo","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","public_gists":1,"followers":13,"url":"https://api.github.com/users/jacquev6","private_gists":5,"hireable":false,"login":"jacquev6","email":"vincent@vincent-jacques.net","disk_usage":16676,"plan":{"private_repos":5,"collaborators":1,"space":614400,"name":"micro"},"created_at":"2010-07-09T06:10:06Z","name":"Vincent Jacques","bio":"","id":327146,"total_private_repos":5,"location":"Paris, France"}
+
+https
+GET
+api.github.com
+None
+/repos/jacquev6/PyGithub
+{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
+None
+200
+[('status', '200 OK'), ('x-ratelimit-remaining', '4979'), ('content-length', '1097'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"b297a1eb78f994e828d8b625dae93910"'), ('date', 'Thu, 10 May 2012 19:03:18 GMT'), ('content-type', 'application/json; charset=utf-8')]
+{"homepage":"http://vincent-jacques.net/PyGithub","clone_url":"https://github.com/jacquev6/PyGithub.git","html_url":"https://github.com/jacquev6/PyGithub","url":"https://api.github.com/repos/jacquev6/PyGithub","has_downloads":true,"watchers":13,"permissions":{"admin":true,"pull":true,"push":true},"mirror_url":null,"git_url":"git://github.com/jacquev6/PyGithub.git","has_wiki":false,"has_issues":true,"fork":false,"forks":2,"language":"Python","size":196,"description":"Python library implementing the full Github API v3","private":false,"created_at":"2012-02-25T12:53:47Z","open_issues":15,"svn_url":"https://github.com/jacquev6/PyGithub","owner":{"url":"https://api.github.com/users/jacquev6","avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","login":"jacquev6","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","id":327146},"name":"PyGithub","pushed_at":"2012-05-10T18:49:21Z","id":3544490,"ssh_url":"git@github.com:jacquev6/PyGithub.git","updated_at":"2012-05-10T18:49:21Z"}
+
+https
+GET
+api.github.com
+None
+/repos/jacquev6/PyGithub/actions/secrets/public-key
+{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
+None
+200
+[('status', '200 OK'), ('x-ratelimit-remaining', '4978'), ('content-length', '485'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"1dd282b50e691f8f162ef9355dad8771"'), ('date', 'Thu, 10 May 2012 19:03:19 GMT'), ('content-type', 'application/json; charset=utf-8')]
+{"key": "u5e1Z25+z8pmgVVt5Pd8k0z/sKpVL1MXYtRAecE4vm8=", "key_id": 568250167242549743}