rocket-engine 0.0.2b1__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.
- rocket_engine/__init__.py +8 -0
- rocket_engine/__main__.py +12 -0
- rocket_engine/adapters/README.md +54 -0
- rocket_engine/adapters/__init__.py +7 -0
- rocket_engine/adapters/api.py +39 -0
- rocket_engine/adapters/base.py +46 -0
- rocket_engine/adapters/cace/__init__.py +163 -0
- rocket_engine/adapters/circulax/README.md +10 -0
- rocket_engine/adapters/circulax/__init__.py +112 -0
- rocket_engine/adapters/circulax/_driver.py +190 -0
- rocket_engine/adapters/circulax/adapter.py +538 -0
- rocket_engine/adapters/circulax/results.py +291 -0
- rocket_engine/adapters/cli/README.md +34 -0
- rocket_engine/adapters/cli/__init__.py +305 -0
- rocket_engine/adapters/connectivity/__init__.py +196 -0
- rocket_engine/adapters/connectivity/results.py +214 -0
- rocket_engine/adapters/cosim/README.md +9 -0
- rocket_engine/adapters/cosim/__init__.py +1390 -0
- rocket_engine/adapters/cosim/results.py +214 -0
- rocket_engine/adapters/devsim/__init__.py +171 -0
- rocket_engine/adapters/digital_flow/README.md +9 -0
- rocket_engine/adapters/digital_flow/__init__.py +1121 -0
- rocket_engine/adapters/digital_flow/results.py +227 -0
- rocket_engine/adapters/digital_flow/state.py +462 -0
- rocket_engine/adapters/drc/README.klayout.md +7 -0
- rocket_engine/adapters/drc/README.md +7 -0
- rocket_engine/adapters/drc/__init__.py +1 -0
- rocket_engine/adapters/drc/klayout.py +952 -0
- rocket_engine/adapters/drc/magic.py +287 -0
- rocket_engine/adapters/drc/results.py +184 -0
- rocket_engine/adapters/drc/results_kl.py +188 -0
- rocket_engine/adapters/elmer/__init__.py +163 -0
- rocket_engine/adapters/envelope.py +120 -0
- rocket_engine/adapters/equivalence/__init__.py +156 -0
- rocket_engine/adapters/errors.py +181 -0
- rocket_engine/adapters/extract_magic/README.md +15 -0
- rocket_engine/adapters/extract_magic/__init__.py +425 -0
- rocket_engine/adapters/extract_magic/results.py +209 -0
- rocket_engine/adapters/formal_sby/README.md +7 -0
- rocket_engine/adapters/formal_sby/__init__.py +773 -0
- rocket_engine/adapters/formal_sby/formal_edge.py +195 -0
- rocket_engine/adapters/formal_sby/results.py +185 -0
- rocket_engine/adapters/gdsfactory/__init__.py +194 -0
- rocket_engine/adapters/gdsfactory/_driver.py +459 -0
- rocket_engine/adapters/gdsfactory/adapter.py +609 -0
- rocket_engine/adapters/gdsfactory/results.py +181 -0
- rocket_engine/adapters/hdl_sim/README.md +7 -0
- rocket_engine/adapters/hdl_sim/__init__.py +369 -0
- rocket_engine/adapters/hdl_sim/ghdl.py +155 -0
- rocket_engine/adapters/hdl_sim/results.py +178 -0
- rocket_engine/adapters/hdl_sim/verilator.py +167 -0
- rocket_engine/adapters/http/README.md +15 -0
- rocket_engine/adapters/http/__init__.py +458 -0
- rocket_engine/adapters/http/events.py +63 -0
- rocket_engine/adapters/ipc/README.md +9 -0
- rocket_engine/adapters/ipc/__init__.py +111 -0
- rocket_engine/adapters/lvs_netgen/README.md +20 -0
- rocket_engine/adapters/lvs_netgen/__init__.py +889 -0
- rocket_engine/adapters/lvs_netgen/results.py +168 -0
- rocket_engine/adapters/mcp/README.md +19 -0
- rocket_engine/adapters/mcp/__init__.py +85 -0
- rocket_engine/adapters/mcp/api-schema.json +6107 -0
- rocket_engine/adapters/mcp/pull.py +246 -0
- rocket_engine/adapters/mcp/server.py +794 -0
- rocket_engine/adapters/meep/__init__.py +167 -0
- rocket_engine/adapters/meow/README.md +7 -0
- rocket_engine/adapters/meow/__init__.py +190 -0
- rocket_engine/adapters/meow/_driver.py +227 -0
- rocket_engine/adapters/meow/_envelope.py +108 -0
- rocket_engine/adapters/meow/adapter.py +491 -0
- rocket_engine/adapters/meow/results.py +218 -0
- rocket_engine/adapters/mutation/__init__.py +192 -0
- rocket_engine/adapters/ngspice/README.md +28 -0
- rocket_engine/adapters/ngspice/__init__.py +627 -0
- rocket_engine/adapters/ngspice/results.py +215 -0
- rocket_engine/adapters/postlayout/README.md +28 -0
- rocket_engine/adapters/postlayout/__init__.py +17 -0
- rocket_engine/adapters/postlayout/adapter.py +1062 -0
- rocket_engine/adapters/postlayout/results.py +330 -0
- rocket_engine/adapters/postlayout/translate.py +71 -0
- rocket_engine/adapters/profile.py +220 -0
- rocket_engine/adapters/sax/README.md +23 -0
- rocket_engine/adapters/sax/__init__.py +516 -0
- rocket_engine/adapters/sax/_driver.py +148 -0
- rocket_engine/adapters/sax/results.py +291 -0
- rocket_engine/adapters/stdio_console.py +1151 -0
- rocket_engine/adapters/syntax/README.md +5 -0
- rocket_engine/adapters/syntax/__init__.py +366 -0
- rocket_engine/adapters/syntax/results.py +181 -0
- rocket_engine/adapters/synth_yosys/README.md +7 -0
- rocket_engine/adapters/synth_yosys/__init__.py +655 -0
- rocket_engine/adapters/synth_yosys/amaranth.py +120 -0
- rocket_engine/adapters/synth_yosys/ghdl.py +128 -0
- rocket_engine/adapters/synth_yosys/results.py +173 -0
- rocket_engine/adapters/timing_sta/README.md +9 -0
- rocket_engine/adapters/timing_sta/__init__.py +726 -0
- rocket_engine/adapters/view_sync/__init__.py +185 -0
- rocket_engine/adapters/view_sync/results.py +189 -0
- rocket_engine/adapters/vlsir/__init__.py +142 -0
- rocket_engine/adapters/xschem/__init__.py +440 -0
- rocket_engine/adapters/xschem/deck.py +266 -0
- rocket_engine/clients/__init__.py +5 -0
- rocket_engine/clients/runtime.py +255 -0
- rocket_engine/composition.py +2279 -0
- rocket_engine/execution/README.md +30 -0
- rocket_engine/execution/__init__.py +150 -0
- rocket_engine/execution/apig/__init__.py +1 -0
- rocket_engine/execution/apig/pool.py +463 -0
- rocket_engine/execution/apig/submit.py +124 -0
- rocket_engine/execution/block_runs_scope.py +479 -0
- rocket_engine/execution/coalesce.py +414 -0
- rocket_engine/execution/console/__init__.py +1 -0
- rocket_engine/execution/console/apply_session.py +318 -0
- rocket_engine/execution/console/lifecycle.py +265 -0
- rocket_engine/execution/dispatch.py +205 -0
- rocket_engine/execution/errors.py +31 -0
- rocket_engine/execution/ports.py +797 -0
- rocket_engine/execution/pyexec/__init__.py +1 -0
- rocket_engine/execution/pyexec/apply_context.py +547 -0
- rocket_engine/execution/pyexec/approvals.py +173 -0
- rocket_engine/execution/pyexec/runner.py +404 -0
- rocket_engine/execution/pyexec/support.py +464 -0
- rocket_engine/execution/python_exec.py +897 -0
- rocket_engine/execution/queue.py +85 -0
- rocket_engine/execution/replay_safe.py +601 -0
- rocket_engine/execution/routing/__init__.py +1 -0
- rocket_engine/execution/routing/calls.py +364 -0
- rocket_engine/execution/routing/namespace.py +785 -0
- rocket_engine/execution/routing/ports.py +102 -0
- rocket_engine/execution/routing/tools.py +346 -0
- rocket_engine/execution/runner.py +311 -0
- rocket_engine/execution/runtime.py +1149 -0
- rocket_engine/execution/service.py +279 -0
- rocket_engine/execution/worker.py +464 -0
- rocket_engine/interfaces/README.md +26 -0
- rocket_engine/interfaces/__init__.py +58 -0
- rocket_engine/interfaces/apig_assets.py +407 -0
- rocket_engine/interfaces/celllink.py +461 -0
- rocket_engine/interfaces/documents.py +92 -0
- rocket_engine/interfaces/envelopes.py +131 -0
- rocket_engine/interfaces/errors.py +16 -0
- rocket_engine/interfaces/layout.py +410 -0
- rocket_engine/interfaces/layout_schema.yaml +278 -0
- rocket_engine/interfaces/paths.py +223 -0
- rocket_engine/interfaces/pdks.py +550 -0
- rocket_engine/interfaces/ports.py +72 -0
- rocket_engine/interfaces/references.py +115 -0
- rocket_engine/interfaces/refs.py +85 -0
- rocket_engine/interfaces/runparams.py +304 -0
- rocket_engine/interfaces/setup.py +552 -0
- rocket_engine/interfaces/setup_catalogue.yaml +243 -0
- rocket_engine/interfaces/setup_probes.py +66 -0
- rocket_engine/interfaces/vocabulary.py +58 -0
- rocket_engine/interfaces/yaml_dump.py +31 -0
- rocket_engine/library/README.md +22 -0
- rocket_engine/library/__init__.py +13 -0
- rocket_engine/library/apig_catalog.py +371 -0
- rocket_engine/library/catalog.py +185 -0
- rocket_engine/library/errors.py +16 -0
- rocket_engine/mutate/__init__.py +776 -0
- rocket_engine/mutate/operators.py +700 -0
- rocket_engine/ops/__init__.py +1 -0
- rocket_engine/ops/deferred.py +279 -0
- rocket_engine/ops/editors.py +404 -0
- rocket_engine/ops/evidence.py +194 -0
- rocket_engine/ops/gates.py +262 -0
- rocket_engine/ops/ports.py +76 -0
- rocket_engine/ops/queries.py +417 -0
- rocket_engine/ops/runs.py +553 -0
- rocket_engine/ops/submit.py +614 -0
- rocket_engine/project/README.md +27 -0
- rocket_engine/project/__init__.py +6 -0
- rocket_engine/project/canvas/__init__.py +55 -0
- rocket_engine/project/canvas/compile.py +109 -0
- rocket_engine/project/canvas/integrate.py +48 -0
- rocket_engine/project/canvas/schema.py +241 -0
- rocket_engine/project/canvas/templates.py +46 -0
- rocket_engine/project/canvas/validate.py +603 -0
- rocket_engine/project/edit/__init__.py +5 -0
- rocket_engine/project/edit/cas.py +87 -0
- rocket_engine/project/edit/transactions.py +282 -0
- rocket_engine/project/errors.py +16 -0
- rocket_engine/project/frontend13.py +685 -0
- rocket_engine/project/geometry/__init__.py +23 -0
- rocket_engine/project/geometry/ops.py +451 -0
- rocket_engine/project/geometry/snap.py +103 -0
- rocket_engine/project/ip/__init__.py +1 -0
- rocket_engine/project/ip/blocks.py +195 -0
- rocket_engine/project/ip/entry.schema.json +78 -0
- rocket_engine/project/ip/lifecycle.py +402 -0
- rocket_engine/project/ip/verbs.py +123 -0
- rocket_engine/project/layers/__init__.py +6 -0
- rocket_engine/project/layers/bindings.py +115 -0
- rocket_engine/project/layers/catalog.py +228 -0
- rocket_engine/project/layers/presets.py +43 -0
- rocket_engine/project/ledger/__init__.py +1 -0
- rocket_engine/project/ledger/docs.py +167 -0
- rocket_engine/project/ledger/entities.py +176 -0
- rocket_engine/project/ledger/events.py +29 -0
- rocket_engine/project/ledger/runs.py +257 -0
- rocket_engine/project/ledger/shared.py +174 -0
- rocket_engine/project/ledger/snapshots.py +385 -0
- rocket_engine/project/ledger/wires.py +552 -0
- rocket_engine/project/process/__init__.py +14 -0
- rocket_engine/project/process/model.py +193 -0
- rocket_engine/project/process/recipes.py +121 -0
- rocket_engine/project/schematic/__init__.py +1 -0
- rocket_engine/project/schematic/hierarchy.py +312 -0
- rocket_engine/project/schematic/instances.py +393 -0
- rocket_engine/project/schematic/sheets.py +198 -0
- rocket_engine/project/service.py +2941 -0
- rocket_engine/project/symbols/__init__.py +1 -0
- rocket_engine/project/symbols/cells.py +324 -0
- rocket_engine/project/symbols/linkage.py +356 -0
- rocket_engine/project/symbols/symbols.py +352 -0
- rocket_engine/project/templates/__init__.py +303 -0
- rocket_engine/project/templates/errors.py +25 -0
- rocket_engine/project/testbench/__init__.py +49 -0
- rocket_engine/project/testbench/blocks.py +619 -0
- rocket_engine/project/testbench/templates.py +134 -0
- rocket_engine/stores/README.md +12 -0
- rocket_engine/stores/__init__.py +6 -0
- rocket_engine/stores/ecs.py +1508 -0
- rocket_engine/stores/layers.py +1023 -0
- rocket_engine/stores/settings.py +1188 -0
- rocket_engine/stores/wireplace.py +1324 -0
- rocket_engine/workflows/README.md +18 -0
- rocket_engine/workflows/__init__.py +53 -0
- rocket_engine/workflows/capability_plan.py +102 -0
- rocket_engine/workflows/completion.py +228 -0
- rocket_engine/workflows/connectgraph.py +734 -0
- rocket_engine/workflows/connectgraph_emit.py +336 -0
- rocket_engine/workflows/gates.py +144 -0
- rocket_engine/workflows/photonic_wire.py +76 -0
- rocket_engine/workflows/stages.py +324 -0
- rocket_engine/workflows/viewsync.py +399 -0
- rocket_engine-0.0.2b1.dist-info/METADATA +200 -0
- rocket_engine-0.0.2b1.dist-info/RECORD +241 -0
- rocket_engine-0.0.2b1.dist-info/WHEEL +4 -0
- rocket_engine-0.0.2b1.dist-info/entry_points.txt +2 -0
- rocket_engine-0.0.2b1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""Path safety: unix normalization, containment, slug minting (PATH-GUARD).
|
|
2
|
+
|
|
3
|
+
Policy: minted-strict, encountered-robust. Names the engine mints
|
|
4
|
+
(slugs) use a strict lowercase charset and never contain spaces;
|
|
5
|
+
paths the engine meets keep their spaces and are never rejected
|
|
6
|
+
for them. All joins are root-join only: relative parts under a
|
|
7
|
+
root, `..` rejected outright, absolute inputs rejected, and the
|
|
8
|
+
resolved candidate must stay inside the resolved root (symlink
|
|
9
|
+
escapes fail closed). Dedup is append-only: a taken base gains
|
|
10
|
+
`-1`, `-2`, … without ever reinterpreting trailing digits.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from .errors import ContractError
|
|
17
|
+
|
|
18
|
+
SLUG_PATTERN = r"^[a-z0-9][a-z0-9_.-]*$"
|
|
19
|
+
_SLUG_RE = re.compile(SLUG_PATTERN)
|
|
20
|
+
_NON_SLUG_RUN_RE = re.compile(r"[^a-z0-9_.-]+")
|
|
21
|
+
_DRIVE_RE = re.compile(r"^[A-Za-z]:")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PathError(ContractError):
|
|
25
|
+
"""Path-safety violation with a stable machine code.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
code: Stable machine-readable error code.
|
|
29
|
+
message: Human-readable detail.
|
|
30
|
+
fields: Itemized fields, when the error names any.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def to_unix(value):
|
|
35
|
+
"""Fold backslashes to forward slashes; spaces survive verbatim.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
value: Path text or Path to normalize.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Unix-normalized path text.
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
PathError: invalid_path for non-text input.
|
|
45
|
+
"""
|
|
46
|
+
if isinstance(value, Path):
|
|
47
|
+
value = str(value)
|
|
48
|
+
if not isinstance(value, str):
|
|
49
|
+
raise PathError("invalid_path", f"Path must be text, got {type(value).__name__}.")
|
|
50
|
+
return value.replace("\\", "/")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def is_slug(value):
|
|
54
|
+
"""True when value matches the minted-strict slug charset.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
value: Candidate slug text.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
True when the value is a valid slug.
|
|
61
|
+
"""
|
|
62
|
+
return isinstance(value, str) and _SLUG_RE.match(value) is not None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def slugify(display_name):
|
|
66
|
+
"""Map a display name onto the strict slug charset.
|
|
67
|
+
|
|
68
|
+
Never rejects spaces: runs of non-slug characters fold to one
|
|
69
|
+
dash, lowercased.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
display_name: Display text to mint from.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Minted slug.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
PathError: invalid_slug for non-text input or a name that
|
|
79
|
+
mints no slug.
|
|
80
|
+
"""
|
|
81
|
+
if not isinstance(display_name, str):
|
|
82
|
+
raise PathError(
|
|
83
|
+
"invalid_slug", f"Display name must be text, got {type(display_name).__name__}."
|
|
84
|
+
)
|
|
85
|
+
slug = _NON_SLUG_RUN_RE.sub("-", display_name.lower()).strip("-_.")
|
|
86
|
+
if not slug or _SLUG_RE.match(slug) is None:
|
|
87
|
+
raise PathError("invalid_slug", f"Display name mints no slug: {display_name!r}.")
|
|
88
|
+
return slug
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def mint_slug(display_name, taken):
|
|
92
|
+
"""Mint a slug with append-only dedup (`adc-2` -> `adc-2-1`).
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
display_name: Display text to mint from.
|
|
96
|
+
taken: Container of already-taken slugs.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Fresh slug, suffixed when the base is taken.
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
PathError: invalid_slug for a name that mints no slug.
|
|
103
|
+
"""
|
|
104
|
+
base = slugify(display_name)
|
|
105
|
+
if base not in taken:
|
|
106
|
+
return base
|
|
107
|
+
index = 1
|
|
108
|
+
while f"{base}-{index}" in taken:
|
|
109
|
+
index += 1
|
|
110
|
+
return f"{base}-{index}"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class SlugStore:
|
|
114
|
+
"""Stored (display_name, slug) pairs; render displays by lookup."""
|
|
115
|
+
|
|
116
|
+
def __init__(self):
|
|
117
|
+
self._by_slug = {}
|
|
118
|
+
self._pairs = []
|
|
119
|
+
|
|
120
|
+
def mint(self, display_name):
|
|
121
|
+
"""Mint and store a slug for one display name.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
display_name: Display text to mint from.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Fresh slug stored against the display name.
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
PathError: invalid_slug for a name that mints no slug.
|
|
131
|
+
"""
|
|
132
|
+
slug = mint_slug(display_name, self._by_slug)
|
|
133
|
+
self._by_slug[slug] = display_name
|
|
134
|
+
self._pairs.append((display_name, slug))
|
|
135
|
+
return slug
|
|
136
|
+
|
|
137
|
+
def display_for(self, slug):
|
|
138
|
+
"""Render the display name stored for one slug.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
slug: Slug to look up.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Stored display name.
|
|
145
|
+
|
|
146
|
+
Raises:
|
|
147
|
+
PathError: unknown_slug for an unstored slug.
|
|
148
|
+
"""
|
|
149
|
+
try:
|
|
150
|
+
return self._by_slug[slug]
|
|
151
|
+
except KeyError:
|
|
152
|
+
raise PathError("unknown_slug", f"Unknown slug: {slug}.") from None
|
|
153
|
+
|
|
154
|
+
def pairs(self):
|
|
155
|
+
"""Stored (display_name, slug) pairs in mint order.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Copy of the stored pair list.
|
|
159
|
+
"""
|
|
160
|
+
return list(self._pairs)
|
|
161
|
+
|
|
162
|
+
def __contains__(self, slug):
|
|
163
|
+
return slug in self._by_slug
|
|
164
|
+
|
|
165
|
+
def __len__(self):
|
|
166
|
+
return len(self._pairs)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _check_part(part):
|
|
170
|
+
text = to_unix(part)
|
|
171
|
+
if text == "":
|
|
172
|
+
return None
|
|
173
|
+
if text.startswith("/") or _DRIVE_RE.match(text):
|
|
174
|
+
raise PathError("path_escape", f"Absolute path rejected under root-join: {part!r}.")
|
|
175
|
+
for segment in text.split("/"):
|
|
176
|
+
if segment == "..":
|
|
177
|
+
raise PathError("path_escape", f"'..' rejected under root-join: {part!r}.")
|
|
178
|
+
return text
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def join_contained(root, *parts):
|
|
182
|
+
"""Join relative parts under root; resolve and enforce containment.
|
|
183
|
+
|
|
184
|
+
Backslashes are folded, spaces survive, `.` segments pass through,
|
|
185
|
+
and the resolved candidate must stay inside the resolved root.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
root: Root text or Path every part joins under.
|
|
189
|
+
parts: Relative path parts to join.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Resolved candidate path, contained in the resolved root.
|
|
193
|
+
|
|
194
|
+
Raises:
|
|
195
|
+
PathError: invalid_path for a non-text root, path_escape
|
|
196
|
+
for absolute inputs, `..` segments, unresolvable joins,
|
|
197
|
+
or symlink escapes.
|
|
198
|
+
"""
|
|
199
|
+
if isinstance(root, Path):
|
|
200
|
+
root_text = str(root)
|
|
201
|
+
elif isinstance(root, str):
|
|
202
|
+
root_text = root
|
|
203
|
+
else:
|
|
204
|
+
raise PathError("invalid_path", f"Root must be text, got {type(root).__name__}.")
|
|
205
|
+
cleaned = []
|
|
206
|
+
for part in parts:
|
|
207
|
+
text = _check_part(part)
|
|
208
|
+
if text is not None:
|
|
209
|
+
cleaned.append(text)
|
|
210
|
+
try:
|
|
211
|
+
root_resolved = Path(root_text).resolve()
|
|
212
|
+
candidate_resolved = Path(root_text, *cleaned).resolve()
|
|
213
|
+
except PathError:
|
|
214
|
+
raise
|
|
215
|
+
except Exception as exc:
|
|
216
|
+
raise PathError("path_escape", f"Cannot resolve under root: {exc}.") from exc
|
|
217
|
+
try:
|
|
218
|
+
candidate_resolved.relative_to(root_resolved)
|
|
219
|
+
except ValueError:
|
|
220
|
+
raise PathError(
|
|
221
|
+
"path_escape", f"Path escapes root: {candidate_resolved}."
|
|
222
|
+
) from None
|
|
223
|
+
return candidate_resolved
|