codeplain 0.3.6__py3-none-any.whl → 0.3.7__py3-none-any.whl
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.
- _version.py +1 -1
- {codeplain-0.3.6.dist-info → codeplain-0.3.7.dist-info}/METADATA +4 -4
- {codeplain-0.3.6.dist-info → codeplain-0.3.7.dist-info}/RECORD +21 -14
- file_utils.py +1 -1
- module_renderer.py +3 -2
- path_resolution.py +64 -0
- plain2code.py +5 -10
- plain2code_arguments.py +200 -99
- plain2code_console.py +2 -1
- plain2code_logger.py +10 -15
- plain_file.py +39 -28
- tests/test_arg_provenance.py +89 -0
- tests/test_folder_path_resolution.py +165 -0
- tests/test_log_file_name_resolution.py +89 -0
- tests/test_logging_config_path_resolution.py +67 -0
- tests/test_path_resolution.py +65 -0
- tests/test_plainfileparser.py +2 -2
- tests/test_script_path_resolution.py +107 -0
- {codeplain-0.3.6.dist-info → codeplain-0.3.7.dist-info}/WHEEL +0 -0
- {codeplain-0.3.6.dist-info → codeplain-0.3.7.dist-info}/entry_points.txt +0 -0
- {codeplain-0.3.6.dist-info → codeplain-0.3.7.dist-info}/licenses/LICENSE +0 -0
_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.3.
|
|
1
|
+
__version__ = "0.3.7"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeplain
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.7
|
|
4
4
|
Summary: Transform plain language specifications into working code
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -9,7 +9,7 @@ Classifier: Operating System :: OS Independent
|
|
|
9
9
|
Classifier: Topic :: Software Development :: Code Generators
|
|
10
10
|
Requires-Python: ==3.11.*
|
|
11
11
|
Requires-Dist: gitpython==3.1.50
|
|
12
|
-
Requires-Dist: mistletoe==1.
|
|
12
|
+
Requires-Dist: mistletoe==1.5.1
|
|
13
13
|
Requires-Dist: networkx==3.6.1
|
|
14
14
|
Requires-Dist: python-frontmatter==1.3.0
|
|
15
15
|
Requires-Dist: python-liquid2==0.4.0
|
|
@@ -21,9 +21,9 @@ Requires-Dist: textual>=7.5.0
|
|
|
21
21
|
Requires-Dist: tiktoken==0.13.0
|
|
22
22
|
Requires-Dist: transitions==0.9.3
|
|
23
23
|
Provides-Extra: dev
|
|
24
|
-
Requires-Dist: black==26.
|
|
24
|
+
Requires-Dist: black==26.5.1; extra == 'dev'
|
|
25
25
|
Requires-Dist: flake8==7.3.0; extra == 'dev'
|
|
26
|
-
Requires-Dist: isort==
|
|
26
|
+
Requires-Dist: isort==8.0.1; extra == 'dev'
|
|
27
27
|
Requires-Dist: mypy==2.1.0; extra == 'dev'
|
|
28
28
|
Requires-Dist: pytest==9.1.1; extra == 'dev'
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
_version.py,sha256=
|
|
1
|
+
_version.py,sha256=J0I0c7-a50EOnWXMryTu_E6xhXSYFBPjVpeYP_a3vRI,22
|
|
2
2
|
change_detection.py,sha256=CazORZrezeZSw2I6VBvieN-c2SEjieZPo6pNrKwmMVA,6915
|
|
3
3
|
codeplain_REST_api.py,sha256=h_XoNws8fvf7OTtoVXhHh6OfLGg77UNFuggMBwaqBwE,21533
|
|
4
4
|
concept_utils.py,sha256=D4qVHFBRvfJZwiIq1AD_SV0Ev-RnOMg2yGWo6Cal7PM,7913
|
|
5
5
|
diff_utils.py,sha256=AjiQlqo5pRos_8hVXZo5yBurl5BzSrTMGrQv4dCtRCg,1198
|
|
6
6
|
event_bus.py,sha256=g5XLwRfjrvTIcB6urjREuCJzZj5TA93hy8lKXLGXuCE,650
|
|
7
|
-
file_utils.py,sha256=
|
|
7
|
+
file_utils.py,sha256=MNZ5li1ofIMOYB6FCrMa_UkX6nkGCgSZuaqzhqVc1Ok,11682
|
|
8
8
|
git_utils.py,sha256=LQQnL5DAFpH5fDBQgUZX2WfjWY4YC5OEGBPIXryTmnY,16896
|
|
9
9
|
memory_management.py,sha256=nHNudgaOqp5UGkIMi62TRCc2nUeFOM0w-I1FfxBFaQE,6120
|
|
10
|
-
module_renderer.py,sha256=
|
|
10
|
+
module_renderer.py,sha256=DZCIo_QzvhFw8EqQTKeF3HUbvfumrV2O1sz-gUhWpIA,6752
|
|
11
11
|
partial_rendering.py,sha256=0ChOaGytogBUEGISpbWkbjT9Hqy8pnOkJdJXcitJDgk,12219
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
path_resolution.py,sha256=0tMqU0a1g4DYhTOp7QjLVSEkv_cgasb4NI1mK809dUM,2233
|
|
13
|
+
plain2code.py,sha256=HGv3Or296pZRuqD5jcmuA3QnGl34aJD5f8619M7l5G4,15294
|
|
14
|
+
plain2code_arguments.py,sha256=3tAA9Haw-j97zztjXdhi6LxFtURP0Tfg9J_rvJDoXmw,17685
|
|
15
|
+
plain2code_console.py,sha256=6_PdrVBw9oDccqSGW_gVRYQjVgC_5W3ush4QT2_jGnw,4931
|
|
15
16
|
plain2code_events.py,sha256=AM8KJS68uiVXRMLt861KaGhs2H4yzujlMB_xiYPfHQc,1478
|
|
16
17
|
plain2code_exceptions.py,sha256=wkkEXw2k4LCd09PIH3_Hu0HyZvtXYjFaA653Sfey4FA,2287
|
|
17
|
-
plain2code_logger.py,sha256=
|
|
18
|
+
plain2code_logger.py,sha256=RJNhesE9y59lPILvGhiJpAB4snTwfsHpG1Ir6EaPhiQ,4989
|
|
18
19
|
plain2code_nodes.py,sha256=IwjQqJxRSJKgP6eEnkYGy3VQHIyoazgkFxAEkxkjfJ8,4426
|
|
19
20
|
plain2code_read_config.py,sha256=D5CxnmTRjddupecWvstcUm0kIJWWzq5-Zmk6sU0wbIg,2235
|
|
20
21
|
plain2code_state.py,sha256=1VwyxgKdA13fAJNKSiPBfBb3A20TKfGCNnOm06YdcS4,2233
|
|
21
22
|
plain2code_telemetry.py,sha256=7yuTH7mQE_wGVW2H1oiVZ3UJg98ArB5HcUUm9isnv9g,4354
|
|
22
23
|
plain2code_utils.py,sha256=id04t9Iez5pYygrE3fKEyrcfWDcniKFT2iQoF2CzTJs,1409
|
|
23
|
-
plain_file.py,sha256=
|
|
24
|
+
plain_file.py,sha256=k2rcq53q0kOf9_8egrZILpkrVkZdfJt7HU0gHBFskU4,35116
|
|
24
25
|
plain_modules.py,sha256=uvRPfxBJwstaWi5lgYKgtQdhhf0Naiy2mS41tLw_TjY,16186
|
|
25
26
|
plain_spec.py,sha256=7nb5lLJKHZ3uu9THkSXyJ5brIKkbGW0ooVfRclLdlzA,16062
|
|
26
27
|
system_config.py,sha256=trgytfXKXoCn0Pgy9u00x2D0ZRHlh_tADlbkwZnF8QE,1038
|
|
@@ -65,19 +66,25 @@ standard_template_library/typescript-react-app-boilerplate.plain,sha256=6LFxhEOz
|
|
|
65
66
|
standard_template_library/typescript-react-app-template.plain,sha256=DN5wEJcT8ZqQTf0vFKi9Z1isi9cin2UVNOZb3r28d3M,715
|
|
66
67
|
tests/__init__.py,sha256=Wk73Io62J15BtlLVIzxmASDWaaJkQLevS4BLK5LDAQg,16
|
|
67
68
|
tests/conftest.py,sha256=QZcp08htUlJGgmHDlRWFgsWXZ8o8IBWTD5QqJaUMlU8,790
|
|
69
|
+
tests/test_arg_provenance.py,sha256=Vp-ZtNbTlSNOE8iiHOin3YI9sobbN0tRPUIfrYjxttQ,3459
|
|
68
70
|
tests/test_change_detection.py,sha256=jH8SEhndX400DNqIKmcYLzxHqq9WFdgmuvY3XPq-ZFc,13534
|
|
69
71
|
tests/test_cli_output.py,sha256=owDP9pLHdErtPAr1paNKp1o044kuH-HxInHLIBLZQFI,18420
|
|
70
72
|
tests/test_file_utils.py,sha256=R_ZIhrTtwlhHxgmbBtA8PFEU5-QB4Fi5skJ7PS2KzdU,3614
|
|
73
|
+
tests/test_folder_path_resolution.py,sha256=_ZvVFu5m2-AH8iQNn0fCYkA0flVxVwdh1oQiWcPNIxw,6611
|
|
71
74
|
tests/test_git_utils.py,sha256=h3rWhhs4XVRYqzVzdmj51siF_AYD-GkbZiefK2iGfWo,15614
|
|
72
75
|
tests/test_imports.py,sha256=1ncbqQhtQW237kIjyV-Ufl7-R-PuduvoYFj2iS-DXW4,2608
|
|
76
|
+
tests/test_log_file_name_resolution.py,sha256=99g9NgPF-BZ98CvNyqDcltBAx4sLBE8gpQPAAieKiPc,3619
|
|
77
|
+
tests/test_logging_config_path_resolution.py,sha256=avz_ehRd18INwIXvAy1AwVzNNUz0vS8eu1nRe0bra8g,2650
|
|
73
78
|
tests/test_partial_rendering.py,sha256=r0RUUd1pI4-2ZkRrM1j8_kUqv-70hrOGjuKBhZoRmH0,22758
|
|
79
|
+
tests/test_path_resolution.py,sha256=yQpd-Unq1lwNv0wkRPwVs5pDT8JmyyDgbpiTQsyt--Y,2615
|
|
74
80
|
tests/test_plain2code.py,sha256=DQYFc0JVSkMI8LZCNaQ__2Nph-DcZZW17rprJA54eyw,3851
|
|
75
81
|
tests/test_plain_modules.py,sha256=S7sd4ZYNotXOgCyZzmBd714QFLih69nlZ3tV8WNtVPo,13766
|
|
76
82
|
tests/test_plainfile.py,sha256=wZ_PN4GULPWIpl_AK_r_6jkgtX0Ch8EHmN5IhX7huOE,11027
|
|
77
|
-
tests/test_plainfileparser.py,sha256=
|
|
83
|
+
tests/test_plainfileparser.py,sha256=xd9P0KqZJlJPn3RKT510IQbfBotk6nQ9O8p9iLwJiQk,22815
|
|
78
84
|
tests/test_plainspec.py,sha256=ng55E8ajrH2-gPC1p_QDIe_zXplPvoqHJ-d57UWKFTw,3577
|
|
79
85
|
tests/test_requires.py,sha256=6tnHYaKudxN-RiQjSFoi9nHe_x0ze3QF-f-j67zAE74,1250
|
|
80
86
|
tests/test_resolve_config_file.py,sha256=b87tDOvyzbsnE-QRWbZ97ou7NuyaAgskv-dbYBum31c,2830
|
|
87
|
+
tests/test_script_path_resolution.py,sha256=tiEqMevQRXr9YpfHN0xldOOQpLAUgnSqTzeZWkKHaUY,4156
|
|
81
88
|
tests/test_telemetry.py,sha256=hjm81aRQQZcZ0RB6aVbS3WO46_nIAii_csmbbtnUh74,7110
|
|
82
89
|
tests/e2e/conftest.py,sha256=5Dm67UTOE-M6hMb-mqZvwa6Ek_wmZgsR7fUv9ZEqCwY,4164
|
|
83
90
|
tests/e2e/test_hello_world_python.py,sha256=PQy4elzB7WvpiWLKa6_HFO0K8HA6_6hGLaAqRi0J3OU,1627
|
|
@@ -91,8 +98,8 @@ tui/spinner.py,sha256=Ro6Gd9Przf-whuHqPRY6HwI0T57yJjyNPbhDbigZKZE,2471
|
|
|
91
98
|
tui/state_handlers.py,sha256=4r_6m5YMcprQtgiTP0De3XFfGAF8VzgaWXHxPweYPbQ,16325
|
|
92
99
|
tui/styles.css,sha256=c13WY1nOgmtkT_FH9Ad2PjXrfi8LYwr6zzD6rBOh9tU,6685
|
|
93
100
|
tui/widget_helpers.py,sha256=mWDPrALsxLOzJg8KD9MXXafJKRiqG-EIp797jbOT77o,5972
|
|
94
|
-
codeplain-0.3.
|
|
95
|
-
codeplain-0.3.
|
|
96
|
-
codeplain-0.3.
|
|
97
|
-
codeplain-0.3.
|
|
98
|
-
codeplain-0.3.
|
|
101
|
+
codeplain-0.3.7.dist-info/METADATA,sha256=Gs_i0uMjyodZ11UAyrWIHyAp9vY4Zw_-todAZsYLRKg,6351
|
|
102
|
+
codeplain-0.3.7.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
103
|
+
codeplain-0.3.7.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
|
|
104
|
+
codeplain-0.3.7.dist-info/licenses/LICENSE,sha256=pCeKgQ1mXE5OmNUuKOfOZh1T1vqkeCUxghE7N8qgnzc,11345
|
|
105
|
+
codeplain-0.3.7.dist-info/RECORD,,
|
file_utils.py
CHANGED
|
@@ -293,7 +293,7 @@ def get_template_directories(plain_file_path, custom_template_dir=None, default_
|
|
|
293
293
|
3. Default template directory - for standard/fallback templates
|
|
294
294
|
"""
|
|
295
295
|
template_dirs = [
|
|
296
|
-
os.path.dirname(plain_file_path), # Highest precedence - directory containing plain file
|
|
296
|
+
os.path.dirname(os.path.abspath(plain_file_path)), # Highest precedence - directory containing plain file
|
|
297
297
|
]
|
|
298
298
|
|
|
299
299
|
if custom_template_dir:
|
module_renderer.py
CHANGED
|
@@ -158,13 +158,14 @@ class ModuleRenderer:
|
|
|
158
158
|
module.wipe_module()
|
|
159
159
|
|
|
160
160
|
self.loaded_modules = list[PlainModule]()
|
|
161
|
+
|
|
161
162
|
_, rendering_failed = self._render_module(self.plain_module, self.render_range, True)
|
|
163
|
+
|
|
162
164
|
if not rendering_failed:
|
|
163
|
-
# Get the last module that completed rendering
|
|
164
165
|
if self.args.copy_build:
|
|
165
166
|
rendered_code_path = f"{self.args.build_dest}/"
|
|
166
167
|
else:
|
|
167
|
-
rendered_code_path =
|
|
168
|
+
rendered_code_path = self.args.build_folder
|
|
168
169
|
|
|
169
170
|
self.run_state.set_render_generated_code_path(rendered_code_path)
|
|
170
171
|
self.event_bus.publish(RenderCompleted(rendered_code_path=rendered_code_path))
|
path_resolution.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Path resolution for CLI / config / default path arguments.
|
|
2
|
+
|
|
3
|
+
The rule, in one sentence: the resolution base for a relative path is determined
|
|
4
|
+
by *where the value was written*, not by which option key it sets.
|
|
5
|
+
|
|
6
|
+
- Values supplied on the command line resolve against the current working
|
|
7
|
+
directory (so they match what shell tab completion just produced).
|
|
8
|
+
- Values read from ``config.yaml`` resolve against the directory containing
|
|
9
|
+
that config file.
|
|
10
|
+
- Values left at their default (not supplied anywhere) resolve against the
|
|
11
|
+
directory containing the spec file.
|
|
12
|
+
|
|
13
|
+
Paths are expanded for ``~`` and returned as absolute paths. Canonicalization
|
|
14
|
+
(symlink resolution) is left to the caller, to be done just before I/O that
|
|
15
|
+
needs it (writing output, executing scripts) so we don't accidentally
|
|
16
|
+
dereference links the user wanted preserved.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
from typing import Literal, Optional
|
|
21
|
+
|
|
22
|
+
PathSource = Literal["cli", "config", "default"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def resolve_path(
|
|
26
|
+
value: str,
|
|
27
|
+
source: PathSource,
|
|
28
|
+
*,
|
|
29
|
+
cwd: str,
|
|
30
|
+
config_dir: Optional[str] = None,
|
|
31
|
+
spec_dir: str,
|
|
32
|
+
) -> str:
|
|
33
|
+
"""Resolve *value* to an absolute path using the base anchor for *source*.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
value: The path string as written by the user. May be absolute, relative,
|
|
37
|
+
or contain a leading ``~``.
|
|
38
|
+
source: Where the value came from -- ``"cli"``, ``"config"``, or
|
|
39
|
+
``"default"``.
|
|
40
|
+
cwd: Base directory for ``"cli"`` values.
|
|
41
|
+
config_dir: Base directory for ``"config"`` values. Must be provided
|
|
42
|
+
whenever ``source == "config"``.
|
|
43
|
+
spec_dir: Base directory for ``"default"`` values.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Absolute path with ``~`` expanded. Symlinks are not resolved.
|
|
47
|
+
"""
|
|
48
|
+
expanded = os.path.expanduser(value)
|
|
49
|
+
|
|
50
|
+
if os.path.isabs(expanded):
|
|
51
|
+
return os.path.normpath(expanded)
|
|
52
|
+
|
|
53
|
+
if source == "cli":
|
|
54
|
+
base = cwd
|
|
55
|
+
elif source == "config":
|
|
56
|
+
if config_dir is None:
|
|
57
|
+
raise ValueError("config_dir must be provided when source == 'config'")
|
|
58
|
+
base = config_dir
|
|
59
|
+
elif source == "default":
|
|
60
|
+
base = spec_dir
|
|
61
|
+
else:
|
|
62
|
+
raise ValueError(f"Unknown path source: {source!r}")
|
|
63
|
+
|
|
64
|
+
return os.path.normpath(os.path.join(base, expanded))
|
plain2code.py
CHANGED
|
@@ -48,7 +48,6 @@ from plain2code_logger import (
|
|
|
48
48
|
IndentedFormatter,
|
|
49
49
|
LoggingHandler,
|
|
50
50
|
dump_crash_logs,
|
|
51
|
-
get_log_file_path,
|
|
52
51
|
)
|
|
53
52
|
from plain2code_state import RunState
|
|
54
53
|
from plain2code_telemetry import capture_crash, initialize_telemetry
|
|
@@ -89,8 +88,7 @@ def setup_logging(
|
|
|
89
88
|
event_bus: EventBus,
|
|
90
89
|
run_state: RunState,
|
|
91
90
|
log_to_file: bool,
|
|
92
|
-
|
|
93
|
-
plain_file_path: Optional[str],
|
|
91
|
+
log_file_path: str,
|
|
94
92
|
headless: bool = False,
|
|
95
93
|
):
|
|
96
94
|
default_level = logging.DEBUG if args.verbose else logging.INFO
|
|
@@ -101,10 +99,9 @@ def setup_logging(
|
|
|
101
99
|
logging.getLogger("transitions").setLevel(logging.ERROR)
|
|
102
100
|
logging.getLogger("transitions.extensions.diagrams").setLevel(logging.ERROR)
|
|
103
101
|
|
|
104
|
-
log_file_path = get_log_file_path(plain_file_path, log_file_name)
|
|
105
|
-
|
|
106
102
|
# Try to load logging configuration from YAML file (takes precedence over --verbose)
|
|
107
103
|
config_loaded = False
|
|
104
|
+
|
|
108
105
|
if args.logging_config_path and os.path.exists(args.logging_config_path):
|
|
109
106
|
try:
|
|
110
107
|
with open(args.logging_config_path, "r") as f:
|
|
@@ -127,7 +124,7 @@ def setup_logging(
|
|
|
127
124
|
handler.setFormatter(formatter)
|
|
128
125
|
root_logger.addHandler(handler)
|
|
129
126
|
|
|
130
|
-
if log_to_file
|
|
127
|
+
if log_to_file:
|
|
131
128
|
try:
|
|
132
129
|
file_handler = logging.FileHandler(log_file_path, mode="w", encoding="utf-8")
|
|
133
130
|
file_handler.setFormatter(file_formatter)
|
|
@@ -356,7 +353,7 @@ def main(): # noqa: C901
|
|
|
356
353
|
# Parse the plain file (and its required modules) once; reused by dry-run and rendering.
|
|
357
354
|
try:
|
|
358
355
|
plain_module = plain_modules.PlainModule(
|
|
359
|
-
args.filename,
|
|
356
|
+
os.path.basename(args.filename),
|
|
360
357
|
args.build_folder,
|
|
361
358
|
args.conformance_tests_folder,
|
|
362
359
|
template_dirs,
|
|
@@ -383,9 +380,7 @@ def main(): # noqa: C901
|
|
|
383
380
|
# Suppress Rich console output.
|
|
384
381
|
console.quiet = True
|
|
385
382
|
|
|
386
|
-
default_log_level = setup_logging(
|
|
387
|
-
args, event_bus, run_state, args.log_to_file, args.log_file_name, args.filename, args.headless
|
|
388
|
-
)
|
|
383
|
+
default_log_level = setup_logging(args, event_bus, run_state, args.log_to_file, args.log_file_name, args.headless)
|
|
389
384
|
|
|
390
385
|
initialize_telemetry()
|
|
391
386
|
|