pycodify 0.1.2__tar.gz → 0.2.0__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.
Files changed (37) hide show
  1. pycodify-0.2.0/.readthedocs.yaml +17 -0
  2. {pycodify-0.1.2 → pycodify-0.2.0}/PKG-INFO +16 -5
  3. {pycodify-0.1.2 → pycodify-0.2.0}/README.md +14 -3
  4. pycodify-0.2.0/docs/api.rst +41 -0
  5. pycodify-0.2.0/docs/architecture.rst +124 -0
  6. {pycodify-0.1.2 → pycodify-0.2.0}/docs/conf.py +6 -6
  7. pycodify-0.2.0/docs/index.rst +38 -0
  8. pycodify-0.2.0/docs/usage.rst +201 -0
  9. {pycodify-0.1.2 → pycodify-0.2.0}/pyproject.toml +3 -3
  10. {pycodify-0.1.2 → pycodify-0.2.0}/src/pycodify/__init__.py +13 -4
  11. pycodify-0.2.0/src/pycodify/core.py +210 -0
  12. {pycodify-0.1.2 → pycodify-0.2.0}/src/pycodify/formatters.py +113 -98
  13. pycodify-0.2.0/src/pycodify/imports.py +64 -0
  14. pycodify-0.2.0/src/pycodify/paths.py +148 -0
  15. {pycodify-0.1.2 → pycodify-0.2.0}/tests/test_core.py +36 -3
  16. pycodify-0.2.0/tests/test_imports.py +53 -0
  17. pycodify-0.2.0/tests/test_paths.py +115 -0
  18. pycodify-0.2.0/tests/test_round_trip.py +147 -0
  19. pycodify-0.1.2/docs/api.rst +0 -106
  20. pycodify-0.1.2/docs/architecture.rst +0 -89
  21. pycodify-0.1.2/docs/index.rst +0 -72
  22. pycodify-0.1.2/docs/usage.rst +0 -129
  23. pycodify-0.1.2/src/pycodify/core.py +0 -145
  24. pycodify-0.1.2/src/pycodify/imports.py +0 -36
  25. {pycodify-0.1.2 → pycodify-0.2.0}/.github/workflows/ci.yml +0 -0
  26. {pycodify-0.1.2 → pycodify-0.2.0}/.github/workflows/publish.yml +0 -0
  27. {pycodify-0.1.2 → pycodify-0.2.0}/.gitignore +0 -0
  28. {pycodify-0.1.2 → pycodify-0.2.0}/CONTRIBUTING.md +0 -0
  29. {pycodify-0.1.2 → pycodify-0.2.0}/LICENSE +0 -0
  30. {pycodify-0.1.2 → pycodify-0.2.0}/docs/Makefile +0 -0
  31. {pycodify-0.1.2 → pycodify-0.2.0}/paper.bib +0 -0
  32. {pycodify-0.1.2 → pycodify-0.2.0}/paper.md +0 -0
  33. {pycodify-0.1.2 → pycodify-0.2.0}/scripts/release.py +0 -0
  34. {pycodify-0.1.2 → pycodify-0.2.0}/scripts/update_and_release.py +0 -0
  35. {pycodify-0.1.2 → pycodify-0.2.0}/scripts/update_version.py +0 -0
  36. {pycodify-0.1.2 → pycodify-0.2.0}/scripts/verify_release_ready.py +0 -0
  37. {pycodify-0.1.2 → pycodify-0.2.0}/tests/__init__.py +0 -0
@@ -0,0 +1,17 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-22.04
5
+ tools:
6
+ python: "3.12"
7
+
8
+ sphinx:
9
+ configuration: docs/conf.py
10
+ fail_on_warning: true
11
+
12
+ python:
13
+ install:
14
+ - method: pip
15
+ path: .
16
+ extra_requirements:
17
+ - docs
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: pycodify
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: Python source code as a serialization format with automatic import resolution
5
5
  Project-URL: Homepage, https://github.com/OpenHCSDev/pycodify
6
6
  Project-URL: Documentation, https://pycodify.readthedocs.io
@@ -49,7 +49,7 @@ class Config:
49
49
  value: int = 42
50
50
 
51
51
  config = Config(name="production", value=100)
52
- code = generate_python_source(Assignment("config", config), clean_mode=True)
52
+ code = generate_python_source(Assignment("config", config))
53
53
  print(code)
54
54
  # Output:
55
55
  # from __main__ import Config
@@ -62,9 +62,12 @@ print(code)
62
62
  |--------|:--------:|:-----------:|:--------:|:---------------:|:-------------:|
63
63
  | pickle | ✗ | ✗ | ✗ | ✓ | ✗ |
64
64
  | JSON/YAML | ✓ | ✓ | ✓ | ✗ | ✓ |
65
- | Python source | ✓ | ✓ | ✓ | ✓ | ✓ |
65
+ | Python source | ✓ | ✓ | ✓ | ✓ | Depends on imported APIs |
66
66
 
67
- Binary formats like `pickle` cannot be diffed, inspected, or edited. Text formats like JSON lose type information. Python source code has all desired properties: it is diffable, inspectable, editable, type-preserving, and cross-version stable.
67
+ Binary formats like `pickle` cannot be diffed, inspected, or edited. Text
68
+ formats like JSON lose type information. Python source is diffable, inspectable,
69
+ editable, and type-preserving, but replay still depends on the imported APIs and
70
+ constructor signatures remaining compatible.
68
71
 
69
72
  ## Features
70
73
 
@@ -76,6 +79,14 @@ Binary formats like `pickle` cannot be diffed, inspected, or edited. Text format
76
79
 
77
80
  ## Documentation
78
81
 
82
+ Explicit mode is the default (``clean_mode=False``), preserving every field for
83
+ reproducibility. Pass ``clean_mode=True`` only when concise, default-eliding
84
+ source is desired.
85
+
86
+ Generated output is executable Python, not a sandboxed data format. Review it
87
+ before execution and never call `exec` on source generated from untrusted
88
+ objects, custom formatters, headers, or edited files.
89
+
79
90
  Full documentation available at [pycodify.readthedocs.io](https://pycodify.readthedocs.io)
80
91
 
81
92
  ## Installation
@@ -14,7 +14,7 @@ class Config:
14
14
  value: int = 42
15
15
 
16
16
  config = Config(name="production", value=100)
17
- code = generate_python_source(Assignment("config", config), clean_mode=True)
17
+ code = generate_python_source(Assignment("config", config))
18
18
  print(code)
19
19
  # Output:
20
20
  # from __main__ import Config
@@ -27,9 +27,12 @@ print(code)
27
27
  |--------|:--------:|:-----------:|:--------:|:---------------:|:-------------:|
28
28
  | pickle | ✗ | ✗ | ✗ | ✓ | ✗ |
29
29
  | JSON/YAML | ✓ | ✓ | ✓ | ✗ | ✓ |
30
- | Python source | ✓ | ✓ | ✓ | ✓ | ✓ |
30
+ | Python source | ✓ | ✓ | ✓ | ✓ | Depends on imported APIs |
31
31
 
32
- Binary formats like `pickle` cannot be diffed, inspected, or edited. Text formats like JSON lose type information. Python source code has all desired properties: it is diffable, inspectable, editable, type-preserving, and cross-version stable.
32
+ Binary formats like `pickle` cannot be diffed, inspected, or edited. Text
33
+ formats like JSON lose type information. Python source is diffable, inspectable,
34
+ editable, and type-preserving, but replay still depends on the imported APIs and
35
+ constructor signatures remaining compatible.
33
36
 
34
37
  ## Features
35
38
 
@@ -41,6 +44,14 @@ Binary formats like `pickle` cannot be diffed, inspected, or edited. Text format
41
44
 
42
45
  ## Documentation
43
46
 
47
+ Explicit mode is the default (``clean_mode=False``), preserving every field for
48
+ reproducibility. Pass ``clean_mode=True`` only when concise, default-eliding
49
+ source is desired.
50
+
51
+ Generated output is executable Python, not a sandboxed data format. Review it
52
+ before execution and never call `exec` on source generated from untrusted
53
+ objects, custom formatters, headers, or edited files.
54
+
44
55
  Full documentation available at [pycodify.readthedocs.io](https://pycodify.readthedocs.io)
45
56
 
46
57
  ## Installation
@@ -0,0 +1,41 @@
1
+ API orientation
2
+ ===============
3
+
4
+ ``Assignment`` and ``CodeBlock``
5
+ Structured top-level source items.
6
+
7
+ ``generate_python_source(obj, header="", clean_mode=False)``
8
+ Generate complete executable source and required imports, with shared and
9
+ repeated absolute paths bound once at the top.
10
+
11
+ ``to_source(value, context=None)``
12
+ Format one value into a ``SourceFragment``.
13
+
14
+ ``SourceFormatter``
15
+ Nominal extension point. Subclasses implement ``can_format`` and ``format``;
16
+ application-specific formatters register without changing pycodify core.
17
+
18
+ ``resolve_imports``
19
+ Resolve import requirements and aliases for colliding names.
20
+
21
+ ``FormatContext`` and ``SourceFragment``
22
+ Immutable formatting state and output records.
23
+
24
+ ``imported_name(module, qualname, context)``
25
+ Spell an importable object by its qualified name, honouring collision aliases.
26
+
27
+ ``PythonSourceLiteral``
28
+ Base for values that spell their own source through ``source_fragment``.
29
+
30
+ ``PathRendering``, ``PathCollector``, ``PathFactoring``, ``PathConstructor``
31
+ How a render pass spells paths: plainly, while recording, or through the
32
+ document's path bindings.
33
+
34
+ The canonical import surface is ``pycodify.__all__``.
35
+
36
+ Public API
37
+ ----------
38
+
39
+ .. automodule:: pycodify
40
+ :members:
41
+ :member-order: bysource
@@ -0,0 +1,124 @@
1
+ Architecture
2
+ ============
3
+
4
+ Two-Pass Algorithm
5
+ -------------------
6
+
7
+ Generating executable source requires knowing import aliases before emitting code. But aliases depend on detecting collisions, which requires visiting all types first. This creates a dependency: code generation requires alias resolution, but alias resolution requires traversing the object graph.
8
+
9
+ pycodify solves this with two passes, preceded by a path-discovery pass
10
+ (see *Path factoring* below):
11
+
12
+ 1. **Collection pass**: Traverse the object, emit code fragments, and collect
13
+ ``(module, name)`` import pairs.
14
+ 2. **Resolution**: ``resolve_imports`` sorts the pairs, detects names exported by
15
+ more than one module, and derives aliases from each module path.
16
+ 3. **Regeneration pass**: Re-traverse with the resolved ``name_mappings`` and
17
+ emit final code using those local names.
18
+
19
+ This is not an optimization—it is structurally necessary. A single-pass algorithm cannot know whether ``Config`` needs aliasing until it has seen all types that might also be named ``Config``.
20
+
21
+ Extensible Formatter Registry
22
+ ------------------------------
23
+
24
+ Each type maps to a ``SourceFormatter`` that emits a ``SourceFragment(code, imports)``. Formatters register via ``__init_subclass__``—defining a formatter class automatically adds it to the registry:
25
+
26
+ .. code-block:: python
27
+
28
+ class EnumFormatter(SourceFormatter):
29
+ priority = 70
30
+
31
+ def can_format(self, value: Any) -> bool:
32
+ return isinstance(value, Enum)
33
+
34
+ def format(self, value: Enum, context: FormatContext) -> SourceFragment:
35
+ enum_type = imported_name(type(value).__module__, type(value).__qualname__, context)
36
+ return SourceFragment(f"{enum_type.code}.{value.name}", enum_type.imports)
37
+
38
+ Priority-based dispatch selects the most specific formatter. Domain extensions add formatters without modifying core code. A value no formatter accepts raises ``TypeError``: pycodify never emits ``repr`` text it cannot load back.
39
+
40
+ Qualified names
41
+ ---------------
42
+
43
+ ``imported_name(module, qualname, context)`` is the one rule for spelling an
44
+ importable object. It imports the root of ``__qualname__`` and spells the rest as
45
+ attribute access, so ``Outer.Mode.FAST`` imports ``Outer``. Enum members,
46
+ classes, functions, class-bound methods, named tuples and dataclass constructors
47
+ all use it. Objects defined inside a function raise ``TypeError``.
48
+
49
+ Values that own their spelling
50
+ ------------------------------
51
+
52
+ A value whose source is not its constructor subclasses ``PythonSourceLiteral``
53
+ and implements ``source_fragment(context)``, typically building on
54
+ ``imported_name`` so collision aliases apply.
55
+
56
+ Core Components
57
+ ---------------
58
+
59
+ **SourceFormatter**
60
+ Base class for all formatters. Subclasses implement ``can_format()`` and ``format()`` methods.
61
+
62
+ **SourceFragment**
63
+ Represents a piece of generated code with its required imports.
64
+
65
+ **FormatContext**
66
+ Carries indentation depth, ``clean_mode``, the ``(module, name)`` to
67
+ local-name mapping, and the pass's ``PathRendering``. ``indented()`` returns
68
+ a copied context and does not mutate its caller.
69
+
70
+ **resolve_imports**
71
+ Detects collisions and returns import lines plus the name mapping used by the
72
+ regeneration pass.
73
+
74
+ **generate_python_source**
75
+ Orchestrates collection, resolution, and regeneration to produce a complete
76
+ source string.
77
+
78
+ Path factoring
79
+ --------------
80
+
81
+ ``generate_python_source`` first renders the document with a ``PathCollector``
82
+ on ``FormatContext.paths`` to record every ``pathlib.Path`` value. A
83
+ ``PathFactoring`` plan then binds each directory shared by two or more absolute
84
+ paths (``path_root = Path('/data/screen')``) and each repeated absolute path
85
+ (``path_1 = path_root / 'plate_A'``) once, at the top of the document, and the
86
+ two rendering passes spell those paths through the bindings. Relative paths and
87
+ strings are never factored. ``to_source`` alone uses the base ``PathRendering``,
88
+ which spells each path as ``Path('...')``.
89
+
90
+ Clean Mode
91
+ ----------
92
+
93
+ Clean mode omits fields matching their default values. This requires:
94
+
95
+ 1. Accessing the dataclass field defaults
96
+ 2. Comparing current values to defaults
97
+ 3. Omitting fields where ``current_value == default_value``
98
+
99
+ Explicit mode includes all fields for complete reproducibility.
100
+
101
+ Stability and trust boundary
102
+ ----------------------------
103
+
104
+ Import ordering and collision aliases are deterministic for a fixed set of
105
+ ``(module, name)`` pairs. The library does not promise that arbitrary object
106
+ graphs produce byte-identical text across runs: container iteration, object
107
+ ``repr`` implementations, custom formatters, and dependency versions can all
108
+ affect output or replay.
109
+
110
+ The output is executable Python. pycodify owns formatting and import resolution,
111
+ not sandboxing or validation of generated programs. Hosts must restrict inputs
112
+ and custom formatters to trusted sources, review persisted code before running
113
+ it, and define their own compatibility policy for imported domain APIs.
114
+
115
+ Module Structure
116
+ ----------------
117
+
118
+ - **core.py**: Main API (``Assignment``, ``generate_python_source``)
119
+ - **formatters.py**: Built-in formatters (dataclass, enum, class, callable, primitive types)
120
+ - **paths.py**: Document-level path factoring
121
+
122
+ Lazy configuration dataclasses (ObjectState) are formatted by
123
+ ``objectstate.codegen``, which registers its own formatter.
124
+ - **imports.py**: Import resolution and collision handling
@@ -1,16 +1,18 @@
1
1
  """Sphinx configuration for pycodify."""
2
- import os
2
+
3
3
  import sys
4
+ from importlib import import_module
5
+ from pathlib import Path
4
6
 
5
7
  # Add source to path
6
- sys.path.insert(0, os.path.abspath("../src"))
8
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
7
9
 
8
10
  # Project information
9
11
  project = "pycodify"
10
12
  copyright = "2024, Tristan Simas"
11
13
  author = "Tristan Simas"
12
- version = "0.1.0"
13
- release = "0.1.0"
14
+ release = import_module("pycodify").__version__
15
+ version = ".".join(release.split(".")[:2])
14
16
 
15
17
  # General configuration
16
18
  extensions = [
@@ -54,7 +56,6 @@ autosummary_generate = True
54
56
  html_theme = "sphinx_rtd_theme"
55
57
  html_theme_options = {
56
58
  "logo_only": False,
57
- "display_version": True,
58
59
  "prev_next_buttons_location": "bottom",
59
60
  "style_external_links": False,
60
61
  "vcs_pageview_mode": "",
@@ -70,4 +71,3 @@ html_favicon = None
70
71
  intersphinx_mapping = {
71
72
  "python": ("https://docs.python.org/3", None),
72
73
  }
73
-
@@ -0,0 +1,38 @@
1
+ pycodify
2
+ ========
3
+
4
+ pycodify serializes Python objects to executable Python source while resolving
5
+ imports and name collisions.
6
+
7
+ Quick start
8
+ -----------
9
+
10
+ .. code-block:: python
11
+
12
+ from dataclasses import dataclass
13
+ from pycodify import Assignment, generate_python_source
14
+
15
+ @dataclass
16
+ class Config:
17
+ name: str = "default"
18
+ value: int = 42
19
+
20
+ config = Config(name="production", value=100)
21
+ code = generate_python_source(Assignment("config", config))
22
+
23
+ Explicit mode is the default (``clean_mode=False``) and includes default-valued
24
+ fields. Pass ``clean_mode=True`` only when default elision is desired.
25
+
26
+ .. toctree::
27
+ :maxdepth: 2
28
+
29
+ usage
30
+ api
31
+ architecture
32
+
33
+ Ownership
34
+ ---------
35
+
36
+ pycodify owns source fragments, imports, formatter registration, collision
37
+ handling, and generic Python formatting. Host packages own their domain
38
+ formatters and round-trip policies.
@@ -0,0 +1,201 @@
1
+ Usage Guide
2
+ ===========
3
+
4
+ Basic Usage
5
+ -----------
6
+
7
+ The simplest way to use pycodify is to generate source code for an object:
8
+
9
+ .. code-block:: python
10
+
11
+ from pycodify import Assignment, generate_python_source
12
+ from dataclasses import dataclass
13
+
14
+ @dataclass
15
+ class ProcessingConfig:
16
+ input_path: str = "/data/input"
17
+ output_path: str = "/data/output"
18
+ num_workers: int = 4
19
+
20
+ config = ProcessingConfig(
21
+ input_path="/data/production",
22
+ num_workers=8
23
+ )
24
+
25
+ code = generate_python_source(
26
+ Assignment("config", config),
27
+ clean_mode=True
28
+ )
29
+ print(code)
30
+
31
+ Clean Mode vs Explicit Mode
32
+ ----------------------------
33
+
34
+ **Clean mode** omits fields that match their default values and must be enabled
35
+ explicitly:
36
+
37
+ .. code-block:: python
38
+
39
+ # Clean mode - concise
40
+ code = generate_python_source(assignment, clean_mode=True)
41
+ # Output: config = ProcessingConfig(input_path='/data/production', num_workers=8)
42
+
43
+ **Explicit mode** includes all fields for complete reproducibility and is the
44
+ default:
45
+
46
+ .. code-block:: python
47
+
48
+ # Explicit mode - complete
49
+ code = generate_python_source(assignment, clean_mode=False)
50
+ # Output: config = ProcessingConfig(
51
+ # input_path='/data/production',
52
+ # output_path='/data/output',
53
+ # num_workers=8
54
+ # )
55
+
56
+ Working with Enums
57
+ ------------------
58
+
59
+ Enums are serialized with their full qualified names:
60
+
61
+ .. code-block:: python
62
+
63
+ from enum import Enum
64
+
65
+ class ImageFormat(Enum):
66
+ JPEG = "jpeg"
67
+ PNG = "png"
68
+ TIFF = "tiff"
69
+
70
+ @dataclass
71
+ class ImageConfig:
72
+ format: ImageFormat = ImageFormat.JPEG
73
+
74
+ config = ImageConfig(format=ImageFormat.PNG)
75
+ code = generate_python_source(Assignment("config", config))
76
+ # Output includes: from __main__ import ImageFormat
77
+ # config = ImageConfig(format=ImageFormat.PNG)
78
+
79
+ Handling Import Collisions
80
+ ---------------------------
81
+
82
+ When multiple modules export the same name, pycodify aliases every colliding
83
+ import. Aliases are derived from the complete module path, with dots replaced
84
+ by underscores:
85
+
86
+ .. code-block:: python
87
+
88
+ # pycodify generates:
89
+ # from package.module_a import Config as Config_package_module_a
90
+ # from package.module_b import Config as Config_package_module_b
91
+ # left = Config_package_module_a(...)
92
+ # right = Config_package_module_b(...)
93
+
94
+ ``resolve_imports`` sorts modules and imported names before emitting them, so a
95
+ given set of import requirements has stable ordering and aliases. Whole-file
96
+ byte-for-byte stability additionally depends on value iteration order, ``repr``
97
+ implementations, and any custom formatters.
98
+
99
+ Formatting one value
100
+ --------------------
101
+
102
+ ``to_source`` returns a ``SourceFragment`` rather than a complete file. Pass a
103
+ ``FormatContext`` when indentation, clean mode, or already-resolved names must
104
+ be controlled explicitly:
105
+
106
+ .. code-block:: python
107
+
108
+ from pycodify import FormatContext, to_source
109
+
110
+ context = FormatContext(indent=1, clean_mode=True)
111
+ fragment = to_source([1, 2], context)
112
+ print(fragment.code)
113
+ print(fragment.imports)
114
+
115
+ ``FormatContext.indented()`` returns a copy with one additional indentation
116
+ level. ``name_mappings`` maps ``(module, imported_name)`` pairs to the local
117
+ names chosen by ``resolve_imports``; callers normally let
118
+ ``generate_python_source`` populate it during the second pass.
119
+
120
+ Values that own their source
121
+ ----------------------------
122
+
123
+ A value whose source is not its dataclass constructor implements
124
+ ``PythonSourceLiteral``:
125
+
126
+ .. code-block:: python
127
+
128
+ from dataclasses import dataclass
129
+
130
+ from pycodify import (
131
+ Assignment,
132
+ FormatContext,
133
+ PythonSourceLiteral,
134
+ SourceFragment,
135
+ generate_python_source,
136
+ imported_name,
137
+ )
138
+
139
+ @dataclass(frozen=True)
140
+ class RegistryLookup(PythonSourceLiteral):
141
+ key: str
142
+
143
+ def source_fragment(self, context: FormatContext) -> SourceFragment:
144
+ lookup = imported_name("myapp.registry", "get_function", context)
145
+ return SourceFragment(f"{lookup.code}({self.key!r})", lookup.imports)
146
+
147
+ source = generate_python_source(Assignment("func", RegistryLookup("gauss")))
148
+ # from myapp.registry import get_function
149
+ #
150
+ # func = get_function('gauss')
151
+
152
+ Nested Dataclasses
153
+ -------------------
154
+
155
+ Nested dataclasses are properly serialized with all necessary imports:
156
+
157
+ .. code-block:: python
158
+
159
+ from dataclasses import dataclass, field
160
+
161
+ @dataclass
162
+ class DatabaseConfig:
163
+ host: str = "localhost"
164
+ port: int = 5432
165
+
166
+ @dataclass
167
+ class AppConfig:
168
+ database: DatabaseConfig = field(default_factory=DatabaseConfig)
169
+ debug: bool = False
170
+
171
+ config = AppConfig(
172
+ database=DatabaseConfig(host="prod.db.internal"),
173
+ debug=True
174
+ )
175
+
176
+ code = generate_python_source(Assignment("config", config))
177
+ # Generates imports for both DatabaseConfig and AppConfig
178
+
179
+ Executing Generated Code
180
+ ------------------------
181
+
182
+ The generated code is executable Python:
183
+
184
+ .. code-block:: python
185
+
186
+ code = generate_python_source(assignment)
187
+ namespace = {}
188
+ exec(code, namespace)
189
+ recreated_config = namespace["config"]
190
+ assert recreated_config == original_config
191
+
192
+ .. warning::
193
+
194
+ Generated output is executable Python, not a sandboxed data format. Execute
195
+ it only when the input objects, custom formatters, header, and resulting file
196
+ are trusted and have been reviewed. pycodify does not make ``exec`` safe for
197
+ untrusted input.
198
+
199
+ Replay compatibility is owned jointly by the generated imports and the APIs
200
+ they call. Explicit mode records default-valued fields, but neither mode can
201
+ guarantee replay across incompatible module moves or constructor changes.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pycodify"
7
- version = "0.1.2"
7
+ version = "0.2.0"
8
8
  description = "Python source code as a serialization format with automatic import resolution"
9
9
  authors = [{name = "Tristan Simas", email = "tristan.simas@mail.mcgill.ca"}]
10
10
  license = {text = "MIT"}
@@ -62,11 +62,11 @@ target-version = ["py310", "py311", "py312", "py313"]
62
62
 
63
63
  [tool.ruff]
64
64
  line-length = 100
65
- target-version = "0.1.2"
65
+ target-version = "py310"
66
66
  select = ["E", "F", "I", "N", "W", "UP"]
67
67
 
68
68
  [tool.mypy]
69
- python_version = "0.1.2"
69
+ python_version = "3.10"
70
70
  warn_return_any = true
71
71
  warn_unused_configs = true
72
72
  disallow_untyped_defs = true
@@ -1,20 +1,23 @@
1
+ # Register default formatters
2
+ from . import formatters as _formatters # noqa: F401
1
3
  from .core import (
2
4
  Assignment,
3
5
  BlankLine,
4
6
  CodeBlock,
5
7
  Comment,
6
8
  FormatContext,
9
+ PathRendering,
10
+ PythonSourceLiteral,
7
11
  SourceFormatter,
8
12
  SourceFragment,
9
13
  generate_python_source,
14
+ imported_name,
10
15
  to_source,
11
16
  )
12
17
  from .imports import resolve_imports
18
+ from .paths import PathCollector, PathConstructor, PathFactoring
13
19
 
14
- # Register default formatters
15
- from . import formatters as _formatters # noqa: F401
16
-
17
- __version__ = "0.1.2"
20
+ __version__ = "0.2.0"
18
21
 
19
22
  __all__ = [
20
23
  "Assignment",
@@ -22,9 +25,15 @@ __all__ = [
22
25
  "CodeBlock",
23
26
  "Comment",
24
27
  "FormatContext",
28
+ "PathCollector",
29
+ "PathConstructor",
30
+ "PathFactoring",
31
+ "PathRendering",
32
+ "PythonSourceLiteral",
25
33
  "SourceFormatter",
26
34
  "SourceFragment",
27
35
  "generate_python_source",
36
+ "imported_name",
28
37
  "resolve_imports",
29
38
  "to_source",
30
39
  ]