Skip to content

Conversation

1ucian0
Copy link

@1ucian0 1ucian0 commented Aug 31, 2025

Fixes #988

Alternative to #1161

Version Information

Please provide:

  • The version of Python you're using: 3.11

  • The version of pip you used to install github3.py: 25.2

  • The version of:

    • github3.py: 4.0.1
    • requests: 2.32.5
    • uritemplate: 4.2.0
    • python-dateutil: 2.9.0.post0

Minimum Reproducible Example

from github3 import GitHub

gh = GitHub()
repo = gh.repository('qiskit-community','qiskit-qec')
print(next(repo.releases()))

Exception information

Traceback (most recent call last):
  File "<redacted>/github3.py/b.py", line 5, in <module>
    print(next(repo.releases()))
          ^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/src/github3/structs.py", line 142, in __next__
    return next(self.__i__)
           ^^^^^^^^^^^^^^^^
  File "<redacted>/src/github3/structs.py", line 131, in __iter__
    yield cls(i)
          ^^^^^^
  File "<redacted>/src/github3/models.py", line 49, in __init__
    self._update_attributes(json)
  File "<redacted>/src/github3/repos/release.py", line 103, in _update_attributes
    self.author = users.ShortUser(release["author"], self)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/src/github3/models.py", line 49, in __init__
    self._update_attributes(json)
  File "<redacted>/src/github3/users.py", line 311, in _update_attributes
    self.avatar_url = user["avatar_url"]
                      ~~~~^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not subscriptable

What is exceptional about what you're seeing versus what you expected to see.

Deleted users break the lib. From the conversation #1161 (comment) , it seems that github3 should create a GhostUser when github3.users.User is called with None.

Some things to consider:

  • I think hardcoding the ghost user JSON is better than fetching it, because it never changes.
  • I didnt know how to create cassettes, so I did not write an integration test. I think it would be great to have one. Pointers?
  • I ran into a typo in the docs. Unrelated.

Copy link
Owner

@sigmavirus24 sigmavirus24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that an integration test with betamax is necessary, but if you were to copy

def test_user(self):
for example, an integration test could look like

    def test_not_found_user_returns_default_ghost(self):
        """Test the ability to retrieve a User."""
        cassette_name = self.cassette_name("not_found_user_returns_default_ghost")
        with self.recorder.use_cassette(cassette_name):
            ghost = self.gh.user("sigmavirus124")

        assert isinstance(ghost, github3.users.User)
        try:
            assert repr(ghost)
        except UnicodeEncodeError:
            self.fail(
                "Regression caught. See PR #52. Names must be utf-8"
                " encoded"
            )

Then run the test and check-in the resulting JSON file.

Comment on lines 313 to 355
json = {
"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{/own"
"er}{/repo}",
"subscriptions_url": "https://api.github.com/users/ghost/subscr"
"iptions",
"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{/priva"
"cy}",
"received_events_url": "https://api.github.com/users/ghost/rece"
"ived_events",
"type": "User",
"user_view_type": "public",
"site_admin": False,
"name": "Deleted user",
"company": None,
"blog": "",
"location": "Nothing to see here, move along.",
"email": None,
"hireable": None,
"bio": "Hi, I'm @ghost! I take the place of user accounts that "
"have been deleted.\n:ghost:\n",
"twitter_username": None,
"public_repos": 0,
"public_gists": 0,
"followers": 11584,
"following": 0,
"created_at": "2008-05-13T06:14:25Z",
"updated_at": "2018-04-10T17:22:33Z",
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather pull this into a module constant, e.g,

_ghost_json: typing.Final[typing.Dict[str, typing.Any]] = {
 # ..
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out 6c3b789 to see if I interpreted you correctly. Or did you mean in src/github3/__init__.py ?


def setUp(self):
"""Use None to create a ghost user."""
self.session = self.create_session_mock()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.session = self.create_session_mock()
super().setup()

There's also after_setup() for overriding things:

def after_setup(self):
"""No-op method to avoid people having to override setUp."""
pass

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took the after_setup approach in 615e464

@sigmavirus24
Copy link
Owner

Also looks like PyCQA/doc8#162 still isn't fixed.

@1ucian0
Copy link
Author

1ucian0 commented Sep 5, 2025

I'm not sure that an integration test with betamax is necessary,

The thing is... this only happens, as far as I can tell, in very corner cases with releases. For example, gh.issue("sigmavirus24", "github3.py", 6) works well. but gh.repository("qiskit-community", "qiskit-qec").release(63525446) does not.

but if you were to copy ...

thanks! I added tests.integration.test_github.TestGitHub.test_release_by_ghostuser .

If you think this is an overkill, we can remove it.

Copy link
Owner

@sigmavirus24 sigmavirus24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last comment I'd request you address, otherwise this looks really great

@1ucian0
Copy link
Author

1ucian0 commented Sep 13, 2025

Let me know if I should do something else. btw, thanks for your support so far :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TypeError: 'NoneType' object is not subscriptable
2 participants