Skip to content

Commit 3da47c2

Browse files
committed
Hide del util from type checkers
Even though current type checkers don't seem to need it. As noted in dffa930, it appears neither mypy nor pyright currently needs `del util` to be guarded by `if not TYPE_CHECKING:` -- they currently treat util as bound even without it, even with `del util` present. It is not obvious that this is the best type checker behavior or that type checkers will continue to behave this way. (In addition, it may help humans for all statements whose effects are intended not to be considered for purposes of static typing to be guarded by `if not TYPE_CHECKING:`.) So this guards the deletion of util the same as the binding of __getattr__. This also moves, clarifies, and expands the commented explanations of what is going on.
1 parent 5bef7ed commit 3da47c2

File tree

1 file changed

+32
-22
lines changed

1 file changed

+32
-22
lines changed

Diff for: git/__init__.py

+32-22
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@
169169
IndexEntry,
170170
IndexFile,
171171
StageType,
172+
# NOTE: This tells type checkers what util resolves to. We delete it, and it is
173+
# really resolved by __getattr__, which warns. See below on what to use instead.
172174
util,
173175
)
174176
from git.util import ( # @NoMove
@@ -183,27 +185,6 @@
183185
raise ImportError("%s: %s" % (_exc.__class__.__name__, _exc)) from _exc
184186

185187

186-
# NOTE: The expression `git.util` evaluates to git.index.util and `from git import util`
187-
# imports git.index.util, NOT git.util. It may not be feasible to change this until the
188-
# next major version, to avoid breaking code inadvertently relying on it.
189-
#
190-
# - If git.index.util *is* what you want, use or import from that, to avoid confusion.
191-
#
192-
# - To use the "real" git.util module, write `from git.util import ...`, or if necessary
193-
# access it as `sys.modules["git.util"]`.
194-
#
195-
# (This differs from other indirect-submodule imports that are unambiguously non-public
196-
# and subject to immediate removal. Here, the public git.util module, though different,
197-
# makes less discoverable that the expression `git.util` refers to a non-public
198-
# attribute of the git module.)
199-
#
200-
# This had come about by a wildcard import. Now that all intended imports are explicit,
201-
# the intuitive but potentially incompatible binding occurs due to the usual rules for
202-
# Python submodule bindings. So for now we delete that and let __getattr__ handle it.
203-
#
204-
del util
205-
206-
207188
def _warned_import(message: str, fullname: str) -> "ModuleType":
208189
import importlib
209190

@@ -242,7 +223,36 @@ def _getattr(name: str) -> Any:
242223
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
243224

244225

245-
if not TYPE_CHECKING: # Preserve static checking for undefined/misspelled attributes.
226+
if not TYPE_CHECKING:
227+
# NOTE: The expression `git.util` gives git.index.util and `from git import util`
228+
# imports git.index.util, NOT git.util. It may not be feasible to change this until
229+
# the next major version, to avoid breaking code inadvertently relying on it.
230+
#
231+
# - If git.index.util *is* what you want, use (or import from) that, to avoid
232+
# confusion.
233+
#
234+
# - To use the "real" git.util module, write `from git.util import ...`, or if
235+
# necessary access it as `sys.modules["git.util"]`.
236+
#
237+
# Note also that `import git.util` technically imports the "real" git.util... but
238+
# the *expression* `git.util` after doing so is still git.index.util!
239+
#
240+
# (This situation differs from that of other indirect-submodule imports that are
241+
# unambiguously non-public and subject to immediate removal. Here, the public
242+
# git.util module, though different, makes less discoverable that the expression
243+
# `git.util` refers to a non-public attribute of the git module.)
244+
#
245+
# This had originally come about by a wildcard import. Now that all intended imports
246+
# are explicit, the intuitive but potentially incompatible binding occurs due to the
247+
# usual rules for Python submodule bindings. So for now we replace that binding with
248+
# git.index.util, delete that, and let __getattr__ handle it and issue a warning.
249+
#
250+
# For the same runtime behavior, it would be enough to forgo importing util, and
251+
# delete util as created naturally; __getattr__ would behave the same. But type
252+
# checkers would not know what util refers to when accessed as an attribute of git.
253+
del util
254+
255+
# This is "hidden" to preserve static checking for undefined/misspelled attributes.
246256
__getattr__ = _getattr
247257

248258
# { Initialize git executable path

0 commit comments

Comments
 (0)