Skip to content

Incorrect return type hint in SymbolicReference.reference leads to mypy errors #2066

@dvanderveer

Description

@dvanderveer

Since GitPython 3.1.45 was released, we've had mypy reporting pre-checkin errors for code that has been stable and functional for years:

error: "SymbolicReference" has no attribute "checkout"  [attr-defined]
error: "SymbolicReference" has no attribute "tracking_branch"  [attr-defined]
error: "SymbolicReference" has no attribute "tracking_branch"  [attr-defined]

The code failing mypy checks is doing things like getting the active branch, so we can switch back to it after doing other stuff:

    @contextmanager
    def checkout(self, branch: Branch) -> typing.Generator[None]:
        new_branch = self.api.create_head(branch.name)
        current = self.api.head.ref
        try:
            new_branch.checkout()
            yield
        finally:
            current.checkout()

... or ensuring that the active branch has a remote tracking branch set:

    tracking_branch = repo.head.reference.tracking_branch()
    if tracking_branch is None:
        raise GitConfigException(
            'Error: Upstream branch not set; use "git branch '
            '--set-upstream-to=<remote/branch>" to fix.'
        )
    if tracking_branch.remote_name == ".":
        raise GitConfigException(
            'Error: Upstream branch is local rather than remote; use "git '
            'branch --set-upstream-to=<remote/branch>" to fix.'
        )

The immediate cause of these new mypy errors appears to be this commit, which added a new type hint for the return value of SymbolicReference.reference:

    @property
    def reference(self) -> "SymbolicReference":
        return self._get_reference()

In manual testing I've found that the reference method doesn't actually return a SymbolicReference object when the the symbolic reference is HEAD. Instead, a Head object is returned:

>>> import git
>>> git.__version__
'3.1.45'
>>> testrepo = git.repo.Repo.init(".")
>>> type(testrepo.head)
<class 'git.refs.head.HEAD'>
>>> type(testrepo.head.ref)
<class 'git.refs.head.Head'>
>>> testrepo.head.ref.tracking_branch()
<git.RemoteReference "refs/remotes/./master">
>>> testrepo.head.ref.checkout()
<git.Head "refs/heads/bar">

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions