inito 0.0.1b0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- inito-0.0.1b0/.github/workflows/ci.yml +65 -0
- inito-0.0.1b0/.github/workflows/release.yml +46 -0
- inito-0.0.1b0/.gitignore +25 -0
- inito-0.0.1b0/.pre-commit-config.yaml +14 -0
- inito-0.0.1b0/CHANGELOG.md +43 -0
- inito-0.0.1b0/CLAUDE.md +105 -0
- inito-0.0.1b0/CONTRIBUTING.md +64 -0
- inito-0.0.1b0/LICENSE +21 -0
- inito-0.0.1b0/PKG-INFO +161 -0
- inito-0.0.1b0/README.md +121 -0
- inito-0.0.1b0/TASKS.md +280 -0
- inito-0.0.1b0/benchmarks/conftest.py +156 -0
- inito-0.0.1b0/benchmarks/import_time.py +50 -0
- inito-0.0.1b0/benchmarks/memory_profile.py +82 -0
- inito-0.0.1b0/benchmarks/pyperf_suite.py +67 -0
- inito-0.0.1b0/benchmarks/test_attribute_access_benchmark.py +8 -0
- inito-0.0.1b0/benchmarks/test_builder_benchmark.py +11 -0
- inito-0.0.1b0/benchmarks/test_construction_benchmark.py +6 -0
- inito-0.0.1b0/benchmarks/test_decoration_benchmark.py +6 -0
- inito-0.0.1b0/benchmarks/test_equality_benchmark.py +8 -0
- inito-0.0.1b0/benchmarks/test_hash_benchmark.py +7 -0
- inito-0.0.1b0/benchmarks/test_repr_benchmark.py +7 -0
- inito-0.0.1b0/docs/api.md +144 -0
- inito-0.0.1b0/docs/conf.py +41 -0
- inito-0.0.1b0/docs/examples.md +71 -0
- inito-0.0.1b0/docs/faq.md +58 -0
- inito-0.0.1b0/docs/index.md +36 -0
- inito-0.0.1b0/docs/installation.md +31 -0
- inito-0.0.1b0/docs/migration.md +72 -0
- inito-0.0.1b0/docs/performance.md +113 -0
- inito-0.0.1b0/docs/quickstart.md +97 -0
- inito-0.0.1b0/docs/troubleshooting.md +86 -0
- inito-0.0.1b0/examples/all_args_constructor_basic.py +18 -0
- inito-0.0.1b0/examples/builder_basic.py +72 -0
- inito-0.0.1b0/examples/data_basic.py +35 -0
- inito-0.0.1b0/examples/equals_and_hash_code_basic.py +20 -0
- inito-0.0.1b0/examples/getter_basic.py +20 -0
- inito-0.0.1b0/examples/no_args_constructor_basic.py +18 -0
- inito-0.0.1b0/examples/required_args_constructor_basic.py +18 -0
- inito-0.0.1b0/examples/setter_basic.py +20 -0
- inito-0.0.1b0/examples/to_string_basic.py +19 -0
- inito-0.0.1b0/pyproject.toml +109 -0
- inito-0.0.1b0/scripts/check_all.sh +7 -0
- inito-0.0.1b0/src/inito/__init__.py +62 -0
- inito-0.0.1b0/src/inito/builders/__init__.py +5 -0
- inito-0.0.1b0/src/inito/builders/builder_generator.py +140 -0
- inito-0.0.1b0/src/inito/core/__init__.py +5 -0
- inito-0.0.1b0/src/inito/core/attach.py +39 -0
- inito-0.0.1b0/src/inito/decorators/__init__.py +57 -0
- inito-0.0.1b0/src/inito/decorators/all_args_constructor.py +25 -0
- inito-0.0.1b0/src/inito/decorators/builder.py +25 -0
- inito-0.0.1b0/src/inito/decorators/data.py +44 -0
- inito-0.0.1b0/src/inito/decorators/equals_and_hash_code.py +26 -0
- inito-0.0.1b0/src/inito/decorators/getter.py +25 -0
- inito-0.0.1b0/src/inito/decorators/no_args_constructor.py +29 -0
- inito-0.0.1b0/src/inito/decorators/required_args_constructor.py +30 -0
- inito-0.0.1b0/src/inito/decorators/setter.py +25 -0
- inito-0.0.1b0/src/inito/decorators/to_string.py +25 -0
- inito-0.0.1b0/src/inito/exceptions/__init__.py +23 -0
- inito-0.0.1b0/src/inito/exceptions/errors.py +35 -0
- inito-0.0.1b0/src/inito/generators/__init__.py +26 -0
- inito-0.0.1b0/src/inito/generators/accessors.py +38 -0
- inito-0.0.1b0/src/inito/generators/base.py +52 -0
- inito-0.0.1b0/src/inito/generators/constructor.py +164 -0
- inito-0.0.1b0/src/inito/generators/equality.py +56 -0
- inito-0.0.1b0/src/inito/generators/registry.py +41 -0
- inito-0.0.1b0/src/inito/generators/repr_.py +23 -0
- inito-0.0.1b0/src/inito/metadata/__init__.py +13 -0
- inito-0.0.1b0/src/inito/metadata/class_metadata.py +31 -0
- inito-0.0.1b0/src/inito/metadata/extractor.py +73 -0
- inito-0.0.1b0/src/inito/metadata/field.py +29 -0
- inito-0.0.1b0/src/inito/py.typed +0 -0
- inito-0.0.1b0/src/inito/reflection/__init__.py +1 -0
- inito-0.0.1b0/src/inito/reflection/introspection.py +32 -0
- inito-0.0.1b0/src/inito/typing/__init__.py +5 -0
- inito-0.0.1b0/src/inito/utils/__init__.py +1 -0
- inito-0.0.1b0/src/inito/utils/codegen.py +33 -0
- inito-0.0.1b0/src/inito/utils/decorator_factory.py +51 -0
- inito-0.0.1b0/tests/builders/__init__.py +0 -0
- inito-0.0.1b0/tests/builders/test_builder_generator.py +136 -0
- inito-0.0.1b0/tests/core/__init__.py +0 -0
- inito-0.0.1b0/tests/core/test_attach.py +65 -0
- inito-0.0.1b0/tests/decorators/__init__.py +0 -0
- inito-0.0.1b0/tests/decorators/test_all_args_constructor.py +36 -0
- inito-0.0.1b0/tests/decorators/test_builder.py +81 -0
- inito-0.0.1b0/tests/decorators/test_data.py +103 -0
- inito-0.0.1b0/tests/decorators/test_equals_and_hash_code.py +56 -0
- inito-0.0.1b0/tests/decorators/test_getter.py +46 -0
- inito-0.0.1b0/tests/decorators/test_no_args_constructor.py +33 -0
- inito-0.0.1b0/tests/decorators/test_required_args_constructor.py +25 -0
- inito-0.0.1b0/tests/decorators/test_setter.py +47 -0
- inito-0.0.1b0/tests/decorators/test_to_string.py +49 -0
- inito-0.0.1b0/tests/exceptions/__init__.py +0 -0
- inito-0.0.1b0/tests/exceptions/test_errors.py +41 -0
- inito-0.0.1b0/tests/generators/__init__.py +0 -0
- inito-0.0.1b0/tests/generators/test_accessors_generator.py +48 -0
- inito-0.0.1b0/tests/generators/test_base.py +32 -0
- inito-0.0.1b0/tests/generators/test_constructor_generator.py +61 -0
- inito-0.0.1b0/tests/generators/test_equality_generator.py +46 -0
- inito-0.0.1b0/tests/generators/test_no_args_constructor_generator.py +58 -0
- inito-0.0.1b0/tests/generators/test_registry.py +24 -0
- inito-0.0.1b0/tests/generators/test_repr_generator.py +29 -0
- inito-0.0.1b0/tests/generators/test_required_args_constructor_generator.py +75 -0
- inito-0.0.1b0/tests/integration/__init__.py +0 -0
- inito-0.0.1b0/tests/integration/test_composition.py +79 -0
- inito-0.0.1b0/tests/integration/test_edge_cases.py +99 -0
- inito-0.0.1b0/tests/integration/test_frozen.py +58 -0
- inito-0.0.1b0/tests/integration/test_generics.py +28 -0
- inito-0.0.1b0/tests/integration/test_invalid_usage.py +54 -0
- inito-0.0.1b0/tests/metadata/__init__.py +0 -0
- inito-0.0.1b0/tests/metadata/test_class_metadata.py +27 -0
- inito-0.0.1b0/tests/metadata/test_extractor.py +85 -0
- inito-0.0.1b0/tests/metadata/test_field_metadata.py +31 -0
- inito-0.0.1b0/tests/reflection/__init__.py +0 -0
- inito-0.0.1b0/tests/reflection/test_introspection.py +54 -0
- inito-0.0.1b0/tests/utils/__init__.py +0 -0
- inito-0.0.1b0/tests/utils/test_codegen.py +34 -0
- inito-0.0.1b0/tests/utils/test_decorator_factory.py +66 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
quality:
|
|
14
|
+
name: lint, format, typecheck, test (py${{ matrix.python-version }})
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v7
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
- name: Install project (dev extras)
|
|
27
|
+
run: |
|
|
28
|
+
uv venv --python ${{ matrix.python-version }}
|
|
29
|
+
uv pip install -e ".[dev]"
|
|
30
|
+
- name: Ruff check
|
|
31
|
+
run: uv run ruff check .
|
|
32
|
+
- name: Ruff format check
|
|
33
|
+
run: uv run ruff format --check .
|
|
34
|
+
- name: Mypy
|
|
35
|
+
run: uv run mypy src
|
|
36
|
+
- name: Pytest with coverage
|
|
37
|
+
run: uv run pytest --cov-report=html
|
|
38
|
+
- name: Build docs (warnings-as-errors)
|
|
39
|
+
if: matrix.python-version == '3.13'
|
|
40
|
+
run: uv run sphinx-build -b html docs docs/_build -W
|
|
41
|
+
- name: Upload coverage artifact
|
|
42
|
+
if: matrix.python-version == '3.13'
|
|
43
|
+
uses: actions/upload-artifact@v7
|
|
44
|
+
with:
|
|
45
|
+
name: coverage-html
|
|
46
|
+
path: htmlcov
|
|
47
|
+
|
|
48
|
+
build:
|
|
49
|
+
name: build sdist and wheel
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
needs: quality
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v7
|
|
54
|
+
- name: Install uv
|
|
55
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
56
|
+
with:
|
|
57
|
+
python-version: "3.13"
|
|
58
|
+
- name: Build
|
|
59
|
+
run: uv build
|
|
60
|
+
- name: Check metadata with twine
|
|
61
|
+
run: uvx twine check dist/*
|
|
62
|
+
- uses: actions/upload-artifact@v7
|
|
63
|
+
with:
|
|
64
|
+
name: dist
|
|
65
|
+
path: dist
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: release-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: false
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Build distribution
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.13"
|
|
22
|
+
- name: Build
|
|
23
|
+
run: uv build
|
|
24
|
+
- name: Check metadata with twine
|
|
25
|
+
run: uvx twine check dist/*
|
|
26
|
+
- uses: actions/upload-artifact@v7
|
|
27
|
+
with:
|
|
28
|
+
name: release-dist
|
|
29
|
+
path: dist
|
|
30
|
+
|
|
31
|
+
publish:
|
|
32
|
+
name: Publish to PyPI
|
|
33
|
+
needs: build
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
environment:
|
|
36
|
+
name: pypi
|
|
37
|
+
url: https://pypi.org/project/inito/
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write # required for PyPI trusted publishing (OIDC) - no API token needed
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/download-artifact@v8
|
|
42
|
+
with:
|
|
43
|
+
name: release-dist
|
|
44
|
+
path: dist
|
|
45
|
+
- name: Publish to PyPI
|
|
46
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
inito-0.0.1b0/.gitignore
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
local_dev/
|
|
2
|
+
|
|
3
|
+
# Python
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.pyc
|
|
6
|
+
*.pyo
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.venv/
|
|
9
|
+
.venv*/
|
|
10
|
+
venv/
|
|
11
|
+
|
|
12
|
+
# Build artifacts
|
|
13
|
+
dist/
|
|
14
|
+
build/
|
|
15
|
+
|
|
16
|
+
# Tooling caches
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# Docs
|
|
24
|
+
site/
|
|
25
|
+
docs/_build/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.6.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
9
|
+
rev: v1.11.2
|
|
10
|
+
hooks:
|
|
11
|
+
- id: mypy
|
|
12
|
+
additional_dependencies: []
|
|
13
|
+
args: [src]
|
|
14
|
+
pass_filenames: false
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.0.1-beta] - 2026-07-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Core metadata/reflection/code-generation engine: decoration-time-only
|
|
14
|
+
field metadata extraction, an `exec()`-based method-generation utility,
|
|
15
|
+
a shared dual-mode (`@dec`/`@dec(...)`) decorator factory, and a
|
|
16
|
+
generator registry for capability-based reuse across decorators.
|
|
17
|
+
- `@Data`: constructor, `__repr__`, `__eq__`, `__hash__`, and
|
|
18
|
+
`get_`/`set_` accessors for every declared field.
|
|
19
|
+
- `@Getter`, `@Setter`: accessors only.
|
|
20
|
+
- `@NoArgsConstructor`: no-argument constructor using field defaults.
|
|
21
|
+
- `@AllArgsConstructor`, `@RequiredArgsConstructor`: constructor-only
|
|
22
|
+
decorators accepting every field or only required fields, respectively.
|
|
23
|
+
- `@Builder`/`builder`: a fluent, chainable builder (`.builder()...build()`)
|
|
24
|
+
with `to_builder()` support (`to_builder=True`), `setter_prefix`, and
|
|
25
|
+
`build_method_name` options. Works standalone on plain classes and
|
|
26
|
+
composes with `@dataclass`.
|
|
27
|
+
- `@ToString`: `__repr__` only.
|
|
28
|
+
- `@EqualsAndHashCode`: `__eq__`/`__hash__` only.
|
|
29
|
+
- Full test suite (176 tests, 100% line+branch coverage), a
|
|
30
|
+
pytest-benchmark/pyperf/tracemalloc/import-time benchmark suite, and
|
|
31
|
+
Sphinx + Furo documentation (installation, quickstart, API reference,
|
|
32
|
+
examples, migration guide, performance report, FAQ, troubleshooting).
|
|
33
|
+
- GitHub Actions CI (lint/format/typecheck/test/build across Python
|
|
34
|
+
3.9-3.13) and a tag-triggered PyPI trusted-publishing release workflow.
|
|
35
|
+
|
|
36
|
+
### Known limitations
|
|
37
|
+
- Static type checkers (mypy/pyright) don't see generated members without
|
|
38
|
+
a dedicated plugin (tracked for a future release).
|
|
39
|
+
- Stacking any constructor-generating decorator with
|
|
40
|
+
`@dataclass(frozen=True)` raises `FrozenInstanceError` (expected, not a
|
|
41
|
+
bug — see the README/troubleshooting docs).
|
|
42
|
+
- Self-referential forward references (e.g. `next: Node`) aren't supported,
|
|
43
|
+
since annotations resolve eagerly at decoration time.
|
inito-0.0.1b0/CLAUDE.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for working on `inito` in this repository.
|
|
4
|
+
|
|
5
|
+
## Project summary
|
|
6
|
+
|
|
7
|
+
`inito` is a Lombok-inspired, zero-runtime-dependency Python library. It
|
|
8
|
+
eliminates boilerplate (constructors, `repr`, equality/hashing, accessors,
|
|
9
|
+
builders) via decorators that generate real methods once, at class-decoration
|
|
10
|
+
time, then attach them directly to the class.
|
|
11
|
+
|
|
12
|
+
## Authoritative specs
|
|
13
|
+
|
|
14
|
+
`local_dev/inito.md` (product/technical spec) and `local_dev/engineering.md`
|
|
15
|
+
(coding standards) are the governing documents for this project. Both are
|
|
16
|
+
gitignored and dev-machine-only — if they're missing on a fresh clone, ask
|
|
17
|
+
the user for them before doing feature work. Read them before implementing
|
|
18
|
+
anything not already covered here.
|
|
19
|
+
|
|
20
|
+
## The non-negotiable performance rule
|
|
21
|
+
|
|
22
|
+
All reflection/annotation inspection happens **exactly once, at decoration
|
|
23
|
+
time**. Never inspect annotations or defaults at instance-construction or
|
|
24
|
+
method-call time. Generated methods are real Python functions built from
|
|
25
|
+
source text via `exec()` and attached directly to the class — never
|
|
26
|
+
`__getattr__`/`__getattribute__` overrides, proxies, descriptors, or
|
|
27
|
+
monkeypatching after class creation.
|
|
28
|
+
|
|
29
|
+
Two sanctioned mutation points, and only two:
|
|
30
|
+
- `src/inito/utils/codegen.py` (`build_function`) — the only `exec()` call site.
|
|
31
|
+
- `src/inito/core/attach.py` (`attach_method` / `attach_capability`) — the
|
|
32
|
+
only place a class is mutated after code generation.
|
|
33
|
+
|
|
34
|
+
## Naming convention
|
|
35
|
+
|
|
36
|
+
- Canonical decorator names are PascalCase, matching Lombok and `inito.md`'s
|
|
37
|
+
Initial Features list: `Data`, `Getter`, `Setter`, `Builder`, `ToString`,
|
|
38
|
+
`EqualsAndHashCode`, `NoArgsConstructor`, `AllArgsConstructor`,
|
|
39
|
+
`RequiredArgsConstructor`.
|
|
40
|
+
- Each is built by `make_decorator` (a factory function, not a class) so it
|
|
41
|
+
supports both `@Data` and `@Data(frozen=True)`.
|
|
42
|
+
- Lowercase aliases are exported bound to the same object (`data = Data`,
|
|
43
|
+
`builder = Builder`, ...) to satisfy the `from inito import builder`
|
|
44
|
+
examples from the spec, while keeping one canonical name.
|
|
45
|
+
- Internal generator classes always use the `*Generator` suffix
|
|
46
|
+
(`ConstructorGenerator`, `ReprGenerator`, ...) and are never exported at the
|
|
47
|
+
top level.
|
|
48
|
+
|
|
49
|
+
## Architecture map
|
|
50
|
+
|
|
51
|
+
- `decorators/` — public decorators (`@Data`, ...); each is a thin
|
|
52
|
+
~10–35 line module wiring generators together via `make_decorator`.
|
|
53
|
+
- `generators/` — one atomic capability per generator (`constructor`, `repr`,
|
|
54
|
+
`eq`+`hash`, `getter`+`setter`), registered under a capability name in
|
|
55
|
+
`generators/registry.py`.
|
|
56
|
+
- `builders/` — the `@Builder` generator (not yet implemented — see Phase 8).
|
|
57
|
+
- `reflection/` — low-level MRO/annotation-walking helpers used once by
|
|
58
|
+
`MetadataExtractor`.
|
|
59
|
+
- `typing/` — reserved for Protocol/generic typing support (currently empty).
|
|
60
|
+
- `metadata/` — `FieldMetadata`/`ClassMetadata` value types and
|
|
61
|
+
`MetadataExtractor`, which builds and caches metadata once per class.
|
|
62
|
+
- `utils/` — `codegen.build_function` and
|
|
63
|
+
`decorator_factory.make_decorator`, the two cross-cutting utilities every
|
|
64
|
+
decorator/generator depends on.
|
|
65
|
+
- `core/` — `attach.py`, the sole class-mutation choke point.
|
|
66
|
+
- `exceptions/` — the `InitoError` hierarchy.
|
|
67
|
+
|
|
68
|
+
**How to add a new decorator:** implement a generator (matching the
|
|
69
|
+
`MethodGenerator` or `MultiMethodGenerator` protocol in `generators/base.py`),
|
|
70
|
+
register it under a new capability name in `generators/__init__.py`, then
|
|
71
|
+
write a small decorator module that resolves that capability via
|
|
72
|
+
`core.attach.attach_capability` and wraps it with `make_decorator`. Never
|
|
73
|
+
modify an existing generator to add a new decorator.
|
|
74
|
+
|
|
75
|
+
## Commands
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv pip install -e ".[dev]" # or: pip install -e ".[dev]"
|
|
79
|
+
pytest # run tests + coverage
|
|
80
|
+
pytest tests/decorators/test_data.py -v # run a single test file
|
|
81
|
+
pytest --cov-report=html # generate an HTML coverage report
|
|
82
|
+
ruff check . # lint
|
|
83
|
+
ruff format . # format
|
|
84
|
+
ruff format --check . # format check (CI mode)
|
|
85
|
+
mypy src # typecheck
|
|
86
|
+
uv build # build sdist + wheel
|
|
87
|
+
./scripts/check_all.sh # run all of the above
|
|
88
|
+
pytest benchmarks/ --benchmark-only # run the pytest-benchmark suite
|
|
89
|
+
python benchmarks/pyperf_suite.py # process-isolated pyperf construction comparison
|
|
90
|
+
python benchmarks/memory_profile.py # per-instance memory footprint comparison
|
|
91
|
+
python benchmarks/import_time.py # cold-import overhead comparison
|
|
92
|
+
sphinx-build -b html docs docs/_build -W # build docs, warnings-as-errors
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Testing conventions
|
|
96
|
+
|
|
97
|
+
`tests/` mirrors `src/inito/`'s package structure. Test files are named
|
|
98
|
+
`test_<module>.py`. Prefer shared fixtures in `conftest.py` over redefining
|
|
99
|
+
sample classes per test file (DRY).
|
|
100
|
+
|
|
101
|
+
## Progress tracking
|
|
102
|
+
|
|
103
|
+
`TASKS.md` is the single source of truth for what's implemented versus
|
|
104
|
+
planned across sessions. Always update its checkboxes as work completes —
|
|
105
|
+
a future session should be able to resume by finding the first unchecked box.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
uv pip install -e ".[dev]"
|
|
7
|
+
# or: pip install -e ".[dev]"
|
|
8
|
+
pre-commit install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ruff check . # lint
|
|
15
|
+
ruff format . # format
|
|
16
|
+
mypy src # typecheck
|
|
17
|
+
pytest # test + coverage
|
|
18
|
+
./scripts/check_all.sh # all of the above
|
|
19
|
+
sphinx-build -b html docs docs/_build -W # build docs, warnings-as-errors
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Standards
|
|
23
|
+
|
|
24
|
+
Follow `local_dev/engineering.md`'s SOLID/DRY/cognitive-complexity/error-handling
|
|
25
|
+
rules and `local_dev/inito.md`'s performance rules (reflection only at
|
|
26
|
+
decoration time, generated methods attached once, zero runtime dependencies).
|
|
27
|
+
See [CLAUDE.md](./CLAUDE.md) for the architecture map and naming conventions.
|
|
28
|
+
|
|
29
|
+
## Progress tracking
|
|
30
|
+
|
|
31
|
+
[TASKS.md](./TASKS.md) is the single source of truth for what's implemented
|
|
32
|
+
versus planned. Update its checkboxes as work lands.
|
|
33
|
+
|
|
34
|
+
## Versioning
|
|
35
|
+
|
|
36
|
+
Semantic versioning. Version is single-sourced from `src/inito/__init__.py`'s
|
|
37
|
+
`__version__` — hatchling reads it via a regex, so bumping it there is the
|
|
38
|
+
only place that needs to change.
|
|
39
|
+
|
|
40
|
+
## Release process
|
|
41
|
+
|
|
42
|
+
1. Bump `__version__` in `src/inito/__init__.py`, following semver.
|
|
43
|
+
2. Update `CHANGELOG.md`: move the `[Unreleased]` entries under a new
|
|
44
|
+
`[X.Y.Z] - YYYY-MM-DD` heading.
|
|
45
|
+
3. Commit, then tag: `git tag vX.Y.Z && git push origin vX.Y.Z`.
|
|
46
|
+
4. Pushing a `v*` tag triggers `.github/workflows/release.yml`, which builds
|
|
47
|
+
the sdist/wheel, runs `twine check`, and publishes to PyPI via trusted
|
|
48
|
+
publishing (OIDC) — no API token needed in CI.
|
|
49
|
+
|
|
50
|
+
**One-time setup required before the first release** (not something CI or
|
|
51
|
+
this repo's config can do on its own):
|
|
52
|
+
|
|
53
|
+
- On [PyPI](https://pypi.org/manage/account/publishing/), register a
|
|
54
|
+
"trusted publisher" for the `inito` project pointing at this repo, the
|
|
55
|
+
`release.yml` workflow filename, and a `pypi` environment name (this can
|
|
56
|
+
be done *before* the first release — PyPI supports pending publishers for
|
|
57
|
+
projects that don't exist yet).
|
|
58
|
+
- In the GitHub repo settings, create an environment named `pypi` (matches
|
|
59
|
+
`release.yml`'s `environment: name: pypi`) — optionally with protection
|
|
60
|
+
rules (e.g. required reviewers) before it can publish.
|
|
61
|
+
|
|
62
|
+
Without that one-time PyPI-side setup, `release.yml`'s publish job will
|
|
63
|
+
fail with an OIDC/trusted-publisher authentication error — that's expected
|
|
64
|
+
until the trusted publisher is registered.
|
inito-0.0.1b0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Swetank Subham
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
inito-0.0.1b0/PKG-INFO
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: inito
|
|
3
|
+
Version: 0.0.1b0
|
|
4
|
+
Summary: A Lombok-inspired boilerplate-elimination library for Python.
|
|
5
|
+
Project-URL: Homepage, https://github.com/swtnk/inito
|
|
6
|
+
Project-URL: Repository, https://github.com/swtnk/inito
|
|
7
|
+
Project-URL: Documentation, https://github.com/swtnk/inito#readme
|
|
8
|
+
Project-URL: Changelog, https://github.com/swtnk/inito/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/swtnk/inito/issues
|
|
10
|
+
Author-email: Swetank Subham <swetanksubham.r@gmail.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: boilerplate,builder,codegen,dataclass,lombok
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: attrs>=23; extra == 'dev'
|
|
29
|
+
Requires-Dist: furo>=2024.1; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy<2,>=1.11; extra == 'dev'
|
|
31
|
+
Requires-Dist: myst-parser>=3; extra == 'dev'
|
|
32
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
33
|
+
Requires-Dist: pyperf>=2.6; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest-benchmark>=4; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
38
|
+
Requires-Dist: sphinx>=7; extra == 'dev'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# inito
|
|
42
|
+
|
|
43
|
+
A Lombok-inspired boilerplate-elimination library for Python. `inito`
|
|
44
|
+
generates constructors, `repr`, equality/hashing, accessors, and fluent
|
|
45
|
+
builders once at class-decoration time — never at instance construction or
|
|
46
|
+
attribute-access time — so the generated classes perform like handwritten
|
|
47
|
+
ones. Zero runtime dependencies.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install inito
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
or
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
uv add inito
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick start
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from inito import Data
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@Data
|
|
68
|
+
class User:
|
|
69
|
+
name: str
|
|
70
|
+
age: int = 0
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
user = User("Ada", age=30)
|
|
74
|
+
print(user) # User(name='Ada', age=30)
|
|
75
|
+
print(user.get_name()) # Ada
|
|
76
|
+
user.set_age(31)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`@Data` also accepts options:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from inito import Data
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@Data(frozen=True)
|
|
86
|
+
class Point:
|
|
87
|
+
x: int
|
|
88
|
+
y: int
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`@builder` generates a fluent, chainable builder, and composes with
|
|
92
|
+
`@dataclass`:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from dataclasses import dataclass
|
|
96
|
+
from inito import builder
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@builder(to_builder=True)
|
|
100
|
+
@dataclass
|
|
101
|
+
class Request:
|
|
102
|
+
prompt: str
|
|
103
|
+
temperature: float = 0.7
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
request = Request.builder().prompt("hello").build()
|
|
107
|
+
revised = request.to_builder().temperature(0.9).build()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Status
|
|
111
|
+
|
|
112
|
+
Implemented today: `@Data` (constructor, `__repr__`, `__eq__`, `__hash__`,
|
|
113
|
+
getters, setters), `@Getter` (getters only), `@Setter` (setters only),
|
|
114
|
+
`@NoArgsConstructor` (no-argument constructor using field defaults),
|
|
115
|
+
`@AllArgsConstructor` (constructor only, every field),
|
|
116
|
+
`@RequiredArgsConstructor` (constructor only accepting required fields),
|
|
117
|
+
`@Builder`/`builder` (fluent builder, `to_builder=True` support),
|
|
118
|
+
`@ToString` (`__repr__` only — pairs well with `@Builder` for a readable
|
|
119
|
+
repr without pulling in `@Data`'s constructor/eq/hash/accessors), and
|
|
120
|
+
`@EqualsAndHashCode` (`__eq__`/`__hash__` only).
|
|
121
|
+
|
|
122
|
+
All of `inito.md`'s Initial Features (v1) are now implemented. See
|
|
123
|
+
[docs/performance.md](./docs/performance.md) for benchmarks against
|
|
124
|
+
handwritten classes, `dataclasses`, and `attrs`. See [TASKS.md](./TASKS.md)
|
|
125
|
+
for what's left: docs, CI hardening, and release.
|
|
126
|
+
|
|
127
|
+
### Known limitation: static type checkers don't see generated members yet
|
|
128
|
+
|
|
129
|
+
Every generated member (`get_x`, `set_x`, `.builder()`, `.to_builder()`, the
|
|
130
|
+
generated constructor's parameters, ...) is attached to your class via
|
|
131
|
+
`setattr` at decoration time — real attributes at runtime, but invisible to
|
|
132
|
+
`mypy`/`pyright` today, since neither tool has a plugin for inito yet. Your
|
|
133
|
+
code will run correctly; `mypy --strict`/`pyright` will flag those accesses
|
|
134
|
+
as unknown attributes in the meantime. `attrs` and Pydantic hit the same
|
|
135
|
+
problem and solved it with dedicated mypy plugins — that's tracked as a
|
|
136
|
+
future initiative (see `TASKS.md` Phase 17), not required for this release.
|
|
137
|
+
|
|
138
|
+
### Known limitations: frozen dataclasses and self-referential fields
|
|
139
|
+
|
|
140
|
+
Stacking any inito constructor-generating decorator (`@Data`, `@Builder`,
|
|
141
|
+
`@AllArgsConstructor`, ...) with `@dataclass(frozen=True)` — in either
|
|
142
|
+
order — raises `dataclasses.FrozenInstanceError`. This is expected, not a
|
|
143
|
+
bug: the generated `__init__`/`build()` assign fields with plain
|
|
144
|
+
`self.x = value`, which correctly respects the frozen class's blocking
|
|
145
|
+
`__setattr__` rather than silently bypassing the immutability you asked
|
|
146
|
+
for. If you want inito's own frozen-style behavior, use `@Data(frozen=True)`
|
|
147
|
+
(which just omits setters) instead of also stacking `@dataclass(frozen=True)`.
|
|
148
|
+
|
|
149
|
+
Self-referential type hints (e.g. a linked-list `next: Node`) also aren't
|
|
150
|
+
supported: inito resolves annotations eagerly, once, at decoration time —
|
|
151
|
+
before the class's own name is bound in its module's globals — so a forward
|
|
152
|
+
reference to the class currently being decorated can't resolve. Forward
|
|
153
|
+
references to any other, already-defined class work normally.
|
|
154
|
+
|
|
155
|
+
## Contributing
|
|
156
|
+
|
|
157
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT — see [LICENSE](./LICENSE).
|
inito-0.0.1b0/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# inito
|
|
2
|
+
|
|
3
|
+
A Lombok-inspired boilerplate-elimination library for Python. `inito`
|
|
4
|
+
generates constructors, `repr`, equality/hashing, accessors, and fluent
|
|
5
|
+
builders once at class-decoration time — never at instance construction or
|
|
6
|
+
attribute-access time — so the generated classes perform like handwritten
|
|
7
|
+
ones. Zero runtime dependencies.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install inito
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
or
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv add inito
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from inito import Data
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@Data
|
|
28
|
+
class User:
|
|
29
|
+
name: str
|
|
30
|
+
age: int = 0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
user = User("Ada", age=30)
|
|
34
|
+
print(user) # User(name='Ada', age=30)
|
|
35
|
+
print(user.get_name()) # Ada
|
|
36
|
+
user.set_age(31)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`@Data` also accepts options:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from inito import Data
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@Data(frozen=True)
|
|
46
|
+
class Point:
|
|
47
|
+
x: int
|
|
48
|
+
y: int
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`@builder` generates a fluent, chainable builder, and composes with
|
|
52
|
+
`@dataclass`:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from dataclasses import dataclass
|
|
56
|
+
from inito import builder
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@builder(to_builder=True)
|
|
60
|
+
@dataclass
|
|
61
|
+
class Request:
|
|
62
|
+
prompt: str
|
|
63
|
+
temperature: float = 0.7
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
request = Request.builder().prompt("hello").build()
|
|
67
|
+
revised = request.to_builder().temperature(0.9).build()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Status
|
|
71
|
+
|
|
72
|
+
Implemented today: `@Data` (constructor, `__repr__`, `__eq__`, `__hash__`,
|
|
73
|
+
getters, setters), `@Getter` (getters only), `@Setter` (setters only),
|
|
74
|
+
`@NoArgsConstructor` (no-argument constructor using field defaults),
|
|
75
|
+
`@AllArgsConstructor` (constructor only, every field),
|
|
76
|
+
`@RequiredArgsConstructor` (constructor only accepting required fields),
|
|
77
|
+
`@Builder`/`builder` (fluent builder, `to_builder=True` support),
|
|
78
|
+
`@ToString` (`__repr__` only — pairs well with `@Builder` for a readable
|
|
79
|
+
repr without pulling in `@Data`'s constructor/eq/hash/accessors), and
|
|
80
|
+
`@EqualsAndHashCode` (`__eq__`/`__hash__` only).
|
|
81
|
+
|
|
82
|
+
All of `inito.md`'s Initial Features (v1) are now implemented. See
|
|
83
|
+
[docs/performance.md](./docs/performance.md) for benchmarks against
|
|
84
|
+
handwritten classes, `dataclasses`, and `attrs`. See [TASKS.md](./TASKS.md)
|
|
85
|
+
for what's left: docs, CI hardening, and release.
|
|
86
|
+
|
|
87
|
+
### Known limitation: static type checkers don't see generated members yet
|
|
88
|
+
|
|
89
|
+
Every generated member (`get_x`, `set_x`, `.builder()`, `.to_builder()`, the
|
|
90
|
+
generated constructor's parameters, ...) is attached to your class via
|
|
91
|
+
`setattr` at decoration time — real attributes at runtime, but invisible to
|
|
92
|
+
`mypy`/`pyright` today, since neither tool has a plugin for inito yet. Your
|
|
93
|
+
code will run correctly; `mypy --strict`/`pyright` will flag those accesses
|
|
94
|
+
as unknown attributes in the meantime. `attrs` and Pydantic hit the same
|
|
95
|
+
problem and solved it with dedicated mypy plugins — that's tracked as a
|
|
96
|
+
future initiative (see `TASKS.md` Phase 17), not required for this release.
|
|
97
|
+
|
|
98
|
+
### Known limitations: frozen dataclasses and self-referential fields
|
|
99
|
+
|
|
100
|
+
Stacking any inito constructor-generating decorator (`@Data`, `@Builder`,
|
|
101
|
+
`@AllArgsConstructor`, ...) with `@dataclass(frozen=True)` — in either
|
|
102
|
+
order — raises `dataclasses.FrozenInstanceError`. This is expected, not a
|
|
103
|
+
bug: the generated `__init__`/`build()` assign fields with plain
|
|
104
|
+
`self.x = value`, which correctly respects the frozen class's blocking
|
|
105
|
+
`__setattr__` rather than silently bypassing the immutability you asked
|
|
106
|
+
for. If you want inito's own frozen-style behavior, use `@Data(frozen=True)`
|
|
107
|
+
(which just omits setters) instead of also stacking `@dataclass(frozen=True)`.
|
|
108
|
+
|
|
109
|
+
Self-referential type hints (e.g. a linked-list `next: Node`) also aren't
|
|
110
|
+
supported: inito resolves annotations eagerly, once, at decoration time —
|
|
111
|
+
before the class's own name is bound in its module's globals — so a forward
|
|
112
|
+
reference to the class currently being decorated can't resolve. Forward
|
|
113
|
+
references to any other, already-defined class work normally.
|
|
114
|
+
|
|
115
|
+
## Contributing
|
|
116
|
+
|
|
117
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT — see [LICENSE](./LICENSE).
|