Skip to content

Defer asyncio and typing_extensions imports (lazy package init)#46

Merged
wolph merged 2 commits into
developfrom
lazy-imports
Jun 30, 2026
Merged

Defer asyncio and typing_extensions imports (lazy package init)#46
wolph merged 2 commits into
developfrom
lazy-imports

Conversation

@wolph

@wolph wolph commented Jun 24, 2026

Copy link
Copy Markdown
Owner

import python_utils now imports nothing eagerly — the package __init__ uses PEP 562 __getattr__ to load submodules and their exported names on first access (with a TYPE_CHECKING block so static typing is unchanged). This avoids pulling in asyncio for consumers that only need the synchronous utilities.

  • time.py: asyncio/aio no longer imported at module scope (moved into the two async generators; the aio.acount default is resolved lazily), so from python_utils.time import format_time stays asyncio-free.
  • types.py: typing_extensions is imported eagerly only on Python < 3.11 (to preserve backport overrides); on 3.11+ stdlib typing already provides the names used, and any remaining typing_extensions-only name is served lazily via __getattr__.

Net effect for a typical consumer (measured via python-progressbar): cold import ~43ms → ~22ms, with asyncio and typing_extensions no longer loaded. Behaviour and public API unchanged; full test suite passes.

Note: the docs_and_lint job currently also flags pre-existing mypy/pyright issues in untouched files (logger.py wraps_classmethod typing, terminal.py stale # type: ignore codes, loguru/setuptools stubs) — these are equally red on develop (tool/stub drift) and are not introduced by this PR.

Copilot AI review requested due to automatic review settings June 24, 2026 02:12

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces lazy loading to the python_utils package to avoid eagerly importing heavy dependencies like asyncio and typing_extensions when only synchronous utilities are needed. This is achieved by utilizing PEP 562 (__getattr__ and __dir__) in python_utils/__init__.py and python_utils/types.py, and by moving asyncio imports inside the relevant asynchronous functions in python_utils/time.py. Feedback is provided to improve a type cast in python_utils/time.py by using actual type objects instead of string literals and accurately typing aio.acount as a callable.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread python_utils/time.py

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR updates python_utils to avoid eagerly importing heavier dependencies (notably asyncio and, on 3.11+, typing_extensions) by switching the package __init__ to PEP 562 lazy attribute loading and moving asyncio imports into async-only call paths.

Changes:

  • Implement PEP 562 lazy exports in python_utils/__init__.py (__getattr__, __dir__) so import python_utils doesn’t import submodules immediately.
  • Defer asyncio (and aio) imports in python_utils/time.py to async functions only; make the aio_timeout_generator default iterable resolved at runtime.
  • Version-gate / lazily resolve typing_extensions names in python_utils/types.py on Python 3.11+.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
python_utils/__init__.py Replaces eager re-exports with lazy attribute-based loading via PEP 562.
python_utils/time.py Moves asyncio/aio imports into async functions and resolves default iterable lazily.
python_utils/types.py Avoids eager typing_extensions import on 3.11+ and adds __getattr__ fallback.
python_utils/converters.py Minor docstring capitalization tweak for remap.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread python_utils/types.py Outdated
Comment thread python_utils/time.py
Comment thread python_utils/__init__.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 952d55844b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread python_utils/types.py Outdated
Comment thread python_utils/__init__.py Outdated
@wolph wolph force-pushed the lazy-imports branch 3 times, most recently from 35a86ff to 5ffc03e Compare June 28, 2026 02:01
@wolph wolph changed the base branch from develop to feature/modernize-strict June 28, 2026 02:01
Comment thread _python_utils_tests/test_lazy_imports.py Fixed
Comment thread _python_utils_tests/test_lazy_imports.py Fixed
Defer all submodule/function imports via PEP 562 (`__getattr__`/`__dir__`) so
`import python_utils` no longer eagerly imports every submodule, and move
`asyncio` (and `python_utils.aio`) imports inside `python_utils.time`'s async
helpers. Consumers that only need the synchronous utilities no longer pay the
cost of importing `asyncio`.

- __init__.py: PEP 562 lazy loading; `__version__` kept eager (cheap
  metadata). Public `__all__` is unchanged and `dir()` still lists the lazy
  `containers`/`exceptions` submodules so `import_global()` keeps working.
- time.py: `aio_timeout_generator(iterable=...)` now defaults to `None` and
  resolves to `aio.acount` lazily (the default cannot reference `aio.acount`
  without importing asyncio at module load); `asyncio` is imported inside the
  two async helpers.
- tests: regression tests proving a bare `import python_utils` and
  `import python_utils.time` pull in neither `asyncio` nor `typing_extensions`.

Public API verified against the 4.0.0 base via an export/signature diff: the
only differences are the two inherent consequences of deferring asyncio --
`aio_timeout_generator`'s default iterable is `None` instead of `aio.acount`,
and `python_utils.time` no longer re-exposes the `aio`/`asyncio` modules.
@wolph wolph changed the base branch from feature/modernize-strict to develop June 28, 2026 09:28
Comment thread _python_utils_tests/test_aliases.py Fixed
Comment thread python_utils/containers.py Fixed
@wolph wolph force-pushed the lazy-imports branch 2 times, most recently from c76b511 to 9d9f76d Compare June 29, 2026 12:55
Builds on the lazy package init: removes typing_extensions from the submodule
import path and defers __version__.

- Add stdlib-only python_utils/_aliases.py holding the lightweight type aliases;
  python_utils.types re-exports them (explicit `X as X` idiom) and keeps its
  eager typing_extensions facade + runtime overrides unchanged.
- Rewire time/converters/formatters/generators/import_ off python_utils.types
  onto _aliases, so importing them no longer pulls typing_extensions.
- Move logger's only typing_extensions use (Logged.__new__ -> Self) into
  TYPE_CHECKING (Self is a new addition; runtime introspection of __new__'s
  return type is not a compat obligation).
- Make python_utils.__version__ lazy via the PEP 562 __getattr__ so bare
  `import python_utils` no longer calls importlib.metadata.version()
  (~89 -> ~8 modules added to sys.modules).
- Add a deterministic import-footprint regression gate + get_type_hints smoke.

Public API verified byte-identical to develop via an export/signature manifest
diff, except: (a) aio_timeout_generator's default iterable (from the lazy
asyncio change) and (b) converters' raw __annotations__ strings now name the
bare aliases (get_type_hints resolves identically). `from __future__ import
annotations` is limited to where required (logger, containers) so
inspect.signature keeps returning evaluated type objects elsewhere. The
undocumented python_utils.<submodule>.types re-export attribute is removed.
@wolph wolph merged commit d8eb734 into develop Jun 30, 2026
16 checks passed
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.

3 participants