Skip to content

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

Open
wolph wants to merge 4 commits into
developfrom
lazy-imports
Open

Defer asyncio and typing_extensions imports (lazy package init)#46
wolph wants to merge 4 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
`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 no longer imports asyncio/aio 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 imports typing_extensions eagerly only on Python < 3.11 (to preserve
the 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): import
drops ~43ms -> ~22ms, with asyncio and typing_extensions no longer loaded.
Behaviour and public API are unchanged; full test suite passes.
wolph added 3 commits June 24, 2026 17:42
…tests

- time.py: cast aio.acount to Callable[[], AsyncIterable[_T]] (real type
  object instead of a string forward-ref) so static analysis/refactor works
- types.py: revert typing_extensions deferral; restore runtime
  'from typing_extensions import *' so backported runtime behaviour (e.g.
  TypeVar(default=...)) is preserved on Python 3.9-3.12. Bare
  'import python_utils' still avoids typing_extensions (types loads lazily)
- __init__.py: __dir__ now includes lazy submodules (containers, exceptions)
  so dir(python_utils) and import_global() see them again
- test_lazy_imports.py: drop the removed types.__getattr__ test; add a
  clean-subprocess test proving bare import pulls in neither asyncio nor
  typing_extensions, plus __getattr__ caching and __dir__ coverage
Alias the internal 'importlib'/'typing' imports to '_importlib'/'_typing' so
they no longer leak as public attributes of 'python_utils' (they appeared in
dir() and as python_utils.importlib / python_utils.typing under the lazy
__init__, which develop did not expose). Add an explicit '# pragma: no cover'
to the TYPE_CHECKING block since coverage's auto-exclusion only matches the
unaliased 'typing.TYPE_CHECKING' spelling.

Verified via a full public-API manifest diff against develop: the only
remaining differences are the two inherent consequences of deferring asyncio
(aio_timeout_generator's default iterable is now None instead of aio.acount,
and python_utils.time no longer re-exposes the 'aio'/'asyncio' modules).
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.

2 participants