Skip to content

Commit

Permalink
Use global to update _typing globals.
Browse files Browse the repository at this point in the history
- That's what it's meant to help with. Doesn't hurt that this removes reliance on the mutability of globals()'s return value.
  • Loading branch information
Sachaa-Thanasius committed Sep 9, 2024
1 parent 4804ea9 commit 393ca36
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 21 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ keep-runtime-typing = true
"A002", # Allow some shadowing of builtins by parameter names.
]
"src/defer_imports/_typing.py" = [
"F822", # __all__ has names that are only provided by module-level __getattr__.
"F822", # __all__ has names that are only provided by module-level __getattr__.
"PLW0603", # "global" is used to update variables at global scope.
]

# ---- Test code
Expand Down
50 changes: 30 additions & 20 deletions src/defer_imports/_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,74 +48,84 @@ def final(f: object) -> object:


def __getattr__(name: str) -> object: # noqa: PLR0911, PLR0912
# Let's cache the return values in the global namespace to avoid subsequent calls to __getattr__ if possible.
# Let's cache the return values in the global namespace to avoid repeat calls.

# ---- Pure imports
if name in {"Generator", "Iterable", "MutableMapping", "Sequence"}:
import collections.abc
global Generator, Iterable, MutableMapping, Sequence

globals()[name] = res = getattr(collections.abc, name)
return res
from collections.abc import Generator, Iterable, MutableMapping, Sequence

return globals()[name]

if name in {"Any", "Final", "Optional", "Union"}:
import typing
global Any, Final, Optional, Union

from typing import Any, Final, Optional, Union

globals()[name] = res = getattr(typing, name)
return res
return globals()[name]

if name in {"CodeType", "ModuleType"}:
import types
global CodeType, ModuleType

globals()[name] = res = getattr(types, name)
return res
from types import CodeType, ModuleType

return globals()[name]

# ---- Imports with fallbacks
if name == "ReadableBuffer":
global ReadableBuffer

if sys.version_info >= (3, 12):
from collections.abc import Buffer as ReadableBuffer
else:
from typing import Union

ReadableBuffer = Union[bytes, bytearray, memoryview]

globals()[name] = ReadableBuffer
return ReadableBuffer
return globals()[name]

if name == "Self":
global Self

if sys.version_info >= (3, 11):
from typing import Self
else:

class Self:
"""Placeholder for typing.Self."""

globals()[name] = Self
return Self
return globals()[name]

if name == "TypeAlias":
global TypeAlias

if sys.version_info >= (3, 10):
from typing import TypeAlias
else:

class TypeAlias:
"""Placeholder for typing.TypeAlias."""

globals()[name] = TypeAlias
return TypeAlias
return globals()[name]

# ---- Composed types/values with imports involved
if name == "StrPath":
global StrPath

import os
from typing import Union

globals()[name] = StrPath = Union[str, os.PathLike[str]]
return StrPath
StrPath = Union[str, os.PathLike[str]]
return globals()[name]

if name == "T":
global T

from typing import TypeVar

globals()[name] = T = TypeVar("T") # pyright: ignore [reportGeneralTypeIssues]
return T
T = TypeVar("T")
return globals()[name]

msg = f"module {__name__!r} has no attribute {name!r}"
raise AttributeError(msg)
Expand Down

0 comments on commit 393ca36

Please sign in to comment.