Metadata-Version: 2.3
Name: autolazy
Version: 1.1.6
Summary: Utilities for building lazily imported Python packages
Keywords: lazy,import,lazy-loader,package
Author: Nekch0
Author-email: Nekch0 <ptdmc.akdmc@gmail.com>
License: MIT License
         
         Copyright (c) 2026 Nekch0
         
         Permission is hereby granted, free of charge, to any person obtaining a copy
         of this software and associated documentation files (the "Software"), to deal
         in the Software without restriction, including without limitation the rights
         to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         copies of the Software, and to permit persons to whom the Software is
         furnished to do so, subject to the following conditions:
         
         The above copyright notice and this permission notice shall be included in all
         copies or substantial portions of the Software.
         
         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         SOFTWARE.
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Dist: lazy-loader>=0.4,<1
Requires-Python: >=3.12
Project-URL: Homepage, https://github.com/Nekch0/autolazy
Project-URL: Repository, https://github.com/Nekch0/autolazy
Project-URL: Bug Tracker, https://github.com/Nekch0/autolazy/issues
Project-URL: Changelog, https://github.com/Nekch0/autolazy/blob/main/CHANGELOG.md
Description-Content-Type: text/markdown

# autolazy

Utilities for building lazily imported Python packages.

`autolazy` provides helper functions that make it easy to set up
[`lazy_loader`](https://github.com/scientific-python/lazy_loader)-based packages.
It automatically discovers each submodule's public API by parsing `__all__`
declarations — no imports, no side effects, just AST analysis — and wires
everything up in a single call.

## Installation

```bash
pip install autolazy
```

Requires Python 3.12+.

## Quick start

Suppose you have a package like this:

```
mypkg/
├── __init__.py
├── audio.py       # __all__ = ["AudioLoader", "AudioWriter"]
└── vision.py      # __all__ = ["ImageLoader"]
```

Replace the usual eager imports in `__init__.py` with:

```python
from autolazy import lazy_attach

__getattr__, __dir__, __all__ = lazy_attach(
    package_name=__name__,
    init_file_path=__file__,
    submod_attrs=["audio", "vision"],
)
```

Now symbols are imported on first access:

```python
import mypkg

mypkg.AudioLoader   # imports audio.py only at this point
mypkg.ImageLoader   # imports vision.py only at this point
```

Submodules themselves can also be exposed lazily:

```python
__getattr__, __dir__, __all__ = lazy_attach(
    package_name=__name__,
    init_file_path=__file__,
    submodules=["utils"],       # exposed as mypkg.utils
    submod_attrs=["audio"],     # symbols exposed at mypkg level
)
```

## API

### `lazy_attach`

```python
lazy_attach(
    package_name: str,
    init_file_path: str,
    submodules: list[str] | None = None,
    submod_attrs: list[str] | None = None,
) -> tuple[__getattr__, __dir__, __all__]
```

Scans each name in `submod_attrs`, extracts its `__all__` via AST parsing,
and passes the result to `lazy_loader.attach()`.

| Parameter | Description |
|---|---|
| `package_name` | The package name — pass `__name__` |
| `init_file_path` | Path to `__init__.py` — pass `__file__` |
| `submodules` | Submodules exposed as `pkg.submod` (not flattened) |
| `submod_attrs` | Submodules whose public symbols are flattened into the package namespace |

If a name in `submod_attrs` has no `__all__` (or the file is missing), it
falls back to being treated as a plain submodule and a `UserWarning` is
emitted for missing files.

Dotted names (e.g. `"sub.module"`) are resolved relative to the package
directory, so nested packages work out of the box.

---

### `parse_all`

```python
parse_all(path: Path) -> list[str]
```

Parses a Python source file and returns the names declared in `__all__`,
without importing the module. Supports all common patterns:

```python
__all__ = ["a", "b"]       # assignment
__all__ += ["c"]           # augmented assignment
__all__.append("d")        # append
__all__.extend(["e", "f"]) # extend
```

Non-string elements in `__all__` are silently ignored. If multiple
assignments to `__all__` exist, the last one wins (matching Python semantics).

---

### `sync_type_checking`

Since `lazy_attach` builds the API at runtime, static type checkers like
Pylance/pyright can't see `mypkg.AudioLoader`. `sync_type_checking` generates an
`if TYPE_CHECKING:` block of literal imports (never executed, so lazy loading is
preserved) that keeps them in sync.

The easiest way is the CLI — it reads the arguments from your existing
`lazy_attach(...)` call, so there's nothing to configure:

```bash
python -m autolazy mypkg/__init__.py
```

It inserts (and, on re-runs, refreshes in place) a marker-delimited block:

```python
# >>> autolazy: type-checking imports (auto-generated) >>>
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .audio import AudioLoader, AudioWriter
    from .vision import ImageLoader

    __all__ = ["AudioLoader", "AudioWriter", "ImageLoader"]
# <<< autolazy: type-checking imports <<<
```

The literal `__all__` marks the imports as re-exports, so ruff and pyright stay
quiet with no `as` aliases or `# noqa`. It sits under `if TYPE_CHECKING:`, so it
never runs — your runtime `__all__` stays the one `lazy_attach` builds.
Imports and `__all__` assignments that would exceed 88 characters are wrapped
with one name per line. Imports follow Ruff/isort's default `order-by-type`
natural ordering, while `__all__` retains the public API's declaration order.
Writing preserves the existing file's LF or CRLF line endings, including on
Windows.

The same is available programmatically as `sync_type_checking(init_file_path,
submodules=..., submod_attrs=...)` (pass `write=False` to get the block as a
string). See the [guide](docs/guide.md#static-typing-pylance-support) for details.

## How it compares to manual `lazy_loader` usage

Without `autolazy` you must maintain `submod_attrs` by hand:

```python
# manual — must be kept in sync with each module's __all__
__getattr__, __dir__, __all__ = lazy.attach(
    __name__,
    submod_attrs={
        "audio": ["AudioLoader", "AudioWriter"],
        "vision": ["ImageLoader"],
    },
)
```

With `autolazy`:

```python
# automatic — __all__ is read from each file at import time
__getattr__, __dir__, __all__ = lazy_attach(
    package_name=__name__,
    init_file_path=__file__,
    submod_attrs=["audio", "vision"],
)
```

## Requirements

- Python >= 3.12
- [`lazy-loader`](https://pypi.org/project/lazy-loader/) >= 0.4

## License

MIT — see [LICENSE](LICENSE).
