Skip to content

Commit

Permalink
Simplify _typing and rename to _typing_compat; add more module-level …
Browse files Browse the repository at this point in the history
…instrumentation test cases
  • Loading branch information
Sachaa-Thanasius committed Sep 20, 2024
1 parent 116c0fc commit 1608a5e
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 87 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repos:
- id: check-yaml

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.5
rev: v0.6.6
hooks:
- id: ruff
args: [--fix]
Expand Down
17 changes: 6 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ path = "src/defer_imports/__init__.py"
benchmark = ["slothy"]
test = ["pytest", "pytest-rerunfailures"]
cov = ["defer-imports[test]", "coverage", "covdefaults"]
dev = ["defer-imports[benchmark,cov]", "pre-commit"]
dev = ["defer-imports[benchmark,cov]", "pre-commit", "typing-extensions"]

[project.urls]
Documentation = "https://github.com/Sachaa-Thanasius/defer-imports#readme"
Expand Down Expand Up @@ -81,7 +81,7 @@ defer_imports = ["src"]
plugins = ["covdefaults"]
source = ["defer_imports", "tests"]
omit = [
"src/defer_imports/_typing.py", # Has a module-level __getattr__ that isn't invoked at runtime.
"src/defer_imports/_typing_compat.py", # Has a module-level __getattr__ that isn't invoked at runtime.
]

[tool.coverage.report]
Expand Down Expand Up @@ -154,14 +154,13 @@ extend-ignore = [
"ISC002",

# ---- Project-specific rules
# -- Readability
"RET505", # Returns in both parts of if-else are fine.
"SIM108", # if-else instead of a ternary is fine.
]
unfixable = [
"ERA", # Prevent unlikely erroneous deletion.
]
typing-modules = ["defer_imports._typing"]
typing-modules = ["defer_imports._typing_compat"]

[tool.ruff.lint.isort]
lines-after-imports = 2
Expand All @@ -173,15 +172,11 @@ keep-runtime-typing = true
[tool.ruff.lint.per-file-ignores]
# ---- Package code
"src/defer_imports/__init__.py" = [
# Ruff doesn't understand wildcard imports that export names from a package.
"F403",
"F405",
"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__.
"src/defer_imports/_typing_compat.py*" = [
"PLW0603", # "global" is used to update variables at global scope.
"ERA001", # Annoying false positive on comments.
"ERA001", # Annoying false positive on some comments.
]

# ---- Test code
Expand All @@ -200,7 +195,7 @@ keep-runtime-typing = true
"benchmark/**/*.py" = [
"T201", # Printing is fine.
"F401", # Unused imports are fine; we're testing import speed.
"ERA001", # Allow commented code.
"ERA001", # Plenty of imports are commented out with explanations next to them.
]


Expand Down
17 changes: 11 additions & 6 deletions src/defer_imports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
from itertools import islice, takewhile
from threading import RLock

from . import _typing as _tp
from . import _typing_compat as _tp


__version__ = "0.0.3dev0"
__version__ = "0.0.3.dev0"

__all__ = (
# -- Compile-time hook
Expand All @@ -42,6 +42,9 @@

# ============================================================================
# region -------- Vendored helpers --------
#
# Helper functions vendored from CPython in some way to avoid actually
# importing them.
# ============================================================================


Expand Down Expand Up @@ -1057,6 +1060,8 @@ def __exit__(self, *exc_info: object) -> None:
#
# Helpers for using defer_imports in various consoles, such as the built-in
# CPython REPL and IPython.
#
# TODO: Add tests for these.
# ============================================================================


Expand Down Expand Up @@ -1100,7 +1105,7 @@ def instrument_ipython() -> None:
_delayed_console_names = frozenset({"code", "codeop", "_DeferredCompile", "DeferredInteractiveConsole", "interact"})


def __getattr__(name: str) -> _tp.Any:
def __getattr__(name: str) -> _tp.Any: # pragma: no cover
# Shim to delay executing expensive console-related functionality until requested.

if name in _delayed_console_names:
Expand Down Expand Up @@ -1156,8 +1161,8 @@ def interact(readfunc: _tp.Optional[_tp.AcceptsInput] = None) -> None:
Parameters
----------
readfunc: \_tp.Optional[\_tp.AcceptsInput], optional
An input function to replace InteractiveConsole.raw_input(). If not given, we default to trying to
import readline to enable GNU readline if available.
An input function to replace InteractiveConsole.raw_input(). If not given, default to trying to import
readline to enable GNU readline if available.
"""

console = DeferredInteractiveConsole()
Expand All @@ -1179,7 +1184,7 @@ def interact(readfunc: _tp.Optional[_tp.AcceptsInput] = None) -> None:
_initial_global_names = tuple(globals())


def __dir__() -> list[str]:
def __dir__() -> list[str]: # pragma: no cover
return list(_delayed_console_names.union(_initial_global_names, __all__))


Expand Down
50 changes: 0 additions & 50 deletions src/defer_imports/_typing.pyi

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
#
# SPDX-License-Identifier: MIT

# pyright: reportUnsupportedDunderAll=none

"""A __getattr__-based lazy import shim for typing- and annotation-related symbols."""

from __future__ import annotations
Expand Down Expand Up @@ -42,24 +40,29 @@
"final",
)

TYPE_CHECKING = False

if TYPE_CHECKING:
from typing import final
else:

def final(f: object) -> object:
"""Decorator to indicate final methods and final classes.
def final(f: object) -> object:
"""Decorator to indicate final methods and final classes.
Slightly modified version of typing.final to avoid importing from typing at runtime.
"""
Slightly modified version of typing.final to avoid importing from typing at runtime.
"""

try:
f.__final__ = True # pyright: ignore # Runtime attribute assignment
except (AttributeError, TypeError): # pragma: no cover
# Skip the attribute silently if it is not writable.
# AttributeError: if the object has __slots__ or a read-only property
# TypeError: if it's a builtin class
pass
return f
try:
f.__final__ = True # pyright: ignore # Runtime attribute assignment
except (AttributeError, TypeError): # pragma: no cover
# Skip the attribute silently if it is not writable.
# AttributeError: if the object has __slots__ or a read-only property
# TypeError: if it's a builtin class
pass
return f


def __getattr__(name: str) -> object: # noqa: PLR0911, PLR0912
def __getattr__(name: str) -> object: # noqa: PLR0911, PLR0912, PLR0915
# ---- Pure imports
if name in {"Callable", "Generator", "Iterable", "MutableMapping", "Sequence"}:
global Callable, Generator, Iterable, MutableMapping, Sequence
Expand Down Expand Up @@ -95,6 +98,8 @@ def __getattr__(name: str) -> object: # noqa: PLR0911, PLR0912

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

Expand All @@ -107,6 +112,8 @@ def __getattr__(name: str) -> object: # noqa: PLR0911, PLR0912

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

class Self:
Expand All @@ -119,6 +126,8 @@ class Self:

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

class TypeAlias:
Expand Down Expand Up @@ -153,8 +162,8 @@ def __call__(self, prompt: str = "") -> str: ...

global PathEntryFinderProtocol

# Copied from _typeshed.importlib.
class PathEntryFinderProtocol(Protocol):
# Copied from _typeshed.importlib.
def find_spec(self, fullname: str, target: ModuleType | None = ..., /) -> ModuleSpec | None: ...

return globals()[name]
Expand All @@ -167,4 +176,4 @@ def find_spec(self, fullname: str, target: ModuleType | None = ..., /) -> Module


def __dir__() -> list[str]:
return [*_initial_global_names, *__all__]
return list(dict.fromkeys(_initial_global_names + __all__))
Loading

0 comments on commit 1608a5e

Please sign in to comment.