You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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)
3. **Regeneration Pass**: Re-traverse with resolved ``name_mappings``, emit final code
11
+
1. **Collection pass**: Traverse the object, emit code fragments, and collect
12
+
``(module, name)`` import pairs.
13
+
2. **Resolution**: ``resolve_imports`` sorts the pairs, detects names exported by
14
+
more than one module, and derives aliases from each module path.
15
+
3. **Regeneration pass**: Re-traverse with the resolved ``name_mappings`` and
16
+
emit final code using those local names.
14
17
15
18
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``.
16
19
@@ -45,13 +48,39 @@ Core Components
45
48
Represents a piece of generated code with its required imports.
46
49
47
50
**FormatContext**
48
-
Carries state during formatting: ``name_mappings``, ``visited_objects``, etc.
49
-
50
-
**ImportResolver**
51
-
Detects collisions and generates deterministic aliases for imports.
52
-
53
-
**SourceGenerator**
54
-
Orchestrates the two-pass algorithm and produces final executable code.
51
+
Carries indentation depth, ``clean_mode``, the ``(module, name)`` to
52
+
local-name mapping, and caller-owned typed extensions. ``indented()`` returns
53
+
a copied context and does not mutate its caller.
54
+
55
+
**resolve_imports**
56
+
Detects collisions and returns import lines plus the name mapping used by the
57
+
regeneration pass.
58
+
59
+
**generate_python_source**
60
+
Orchestrates collection, resolution, and regeneration to produce a complete
61
+
source string.
62
+
63
+
Immutable typed context extensions
64
+
----------------------------------
65
+
66
+
``FormatContext`` is frozen. Its default mappings are read-only mapping proxies,
67
+
and context evolution uses dataclass replacement rather than mutation. A host
68
+
may supply an ``extensions`` mapping keyed by the exact nominal type of each
69
+
rendering policy or state record. A formatter retrieves one value with
70
+
``context.extension(ExtensionType)``; pycodify does not search base classes,
71
+
string keys, or a fallback chain.
72
+
73
+
``generate_python_source(..., context=context)`` threads the caller's context
74
+
through collection and regeneration. Import resolution replaces only
75
+
``name_mappings`` for the second pass, so typed extensions and all other context
76
+
fields survive. When a context is supplied, its ``clean_mode`` is authoritative;
77
+
the separate ``clean_mode=`` argument is used only when pycodify constructs the
78
+
context.
79
+
80
+
Extensions provide formatting context, not formatter dispatch. New value
81
+
families still extend the ``SourceFormatter`` registry, while the exact extension
82
+
type allows that formatter to obtain host-owned rendering information without
83
+
adding host imports or fields to pycodify core.
55
84
56
85
Clean Mode
57
86
----------
@@ -64,6 +93,20 @@ Clean mode omits fields matching their default values. This requires:
64
93
65
94
Explicit mode includes all fields for complete reproducibility.
66
95
96
+
Stability and trust boundary
97
+
----------------------------
98
+
99
+
Import ordering and collision aliases are deterministic for a fixed set of
100
+
``(module, name)`` pairs. The library does not promise that arbitrary object
101
+
graphs produce byte-identical text across runs: container iteration, object
102
+
``repr`` implementations, custom formatters, and dependency versions can all
103
+
affect output or replay.
104
+
105
+
The output is executable Python. pycodify owns formatting and import resolution,
106
+
not sandboxing or validation of generated programs. Hosts must restrict inputs
107
+
and custom formatters to trusted sources, review persisted code before running
108
+
it, and define their own compatibility policy for imported domain APIs.
109
+
67
110
Lazy Dataclass Integration
68
111
---------------------------
69
112
@@ -86,4 +129,3 @@ Module Structure
86
129
- **core.py**: Main API (``Assignment``, ``generate_python_source``)
**pycodify** serializes Python objects to executable Python source code with automatic import resolution.
4
+
pycodify serializes Python objects to executable Python source while resolving
5
+
imports and name collisions.
5
6
6
-
The key insight: **Python source code is a serialization format.** Rather than inventing a format and writing loaders, pycodify emits code that Python itself interprets. The import system becomes the deserializer.
7
-
8
-
Quick Start
7
+
Quick start
9
8
-----------
10
9
11
10
.. code-block:: python
12
11
13
-
from pycodify import Assignment, generate_python_source
14
12
from dataclasses import dataclass
13
+
from pycodify import Assignment, generate_python_source
0 commit comments