linked-data-python 0.5.1__tar.gz → 0.6.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.
- {linked_data_python-0.5.1/linked_data_python.egg-info → linked_data_python-0.6.0}/PKG-INFO +1 -1
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/__main__.py +4 -1
- linked_data_python-0.6.0/ldpy/backend.py +90 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/build.py +41 -6
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/console.py +13 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/debug.py +24 -2
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/runtime.py +178 -56
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/sparql.py +4 -4
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/transpiler/core.py +32 -11
- {linked_data_python-0.5.1 → linked_data_python-0.6.0/linked_data_python.egg-info}/PKG-INFO +1 -1
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/linked_data_python.egg-info/SOURCES.txt +3 -1
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/pyproject.toml +1 -1
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_console.py +58 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_debug.py +41 -0
- linked_data_python-0.6.0/tests/test_docs.py +103 -0
- linked_data_python-0.6.0/tests/test_target_micropython.py +134 -0
- linked_data_python-0.5.1/tests/test_docs.py +0 -46
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/LICENSE.md +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/README.md +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/__init__.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/formatter.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/importer.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/lsp/__init__.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/lsp/__main__.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/lsp/backend.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/lsp/hover.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/lsp/islanddoc.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/lsp/rpc.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/lsp/server.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/lsp/translate.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/pygments_lexer.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/transpiler/__init__.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/transpiler/errors.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/ldpy/transpiler/linemap.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/linked_data_python.egg-info/dependency_links.txt +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/linked_data_python.egg-info/entry_points.txt +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/linked_data_python.egg-info/requires.txt +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/linked_data_python.egg-info/top_level.txt +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/setup.cfg +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_bench.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_bindings.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_build.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_call_suffix.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_charsets.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_coercion.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_current_graph.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_debug_stepping.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_disambiguation.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_docstrings.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_examples.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_formatter.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_generator.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_graphs.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_hover_parts.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_identity.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_import_hook.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_islanddoc.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_islands_terms.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_linemap.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_lsp_rpc.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_lsp_server.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_lsp_translate.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_match_island.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_micropython_subset.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_prefix_import.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_prefix_scoping.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_pygments_lexer.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_scope_modifiers.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_semantics.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_sourcemap_v3.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_sparql_expr.py +0 -0
- {linked_data_python-0.5.1 → linked_data_python-0.6.0}/tests/test_sparql_island.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: linked-data-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Python extended with Semantic Web primitives: IRIs, RDF literals and Turtle-notation graphs as expressions, transpiled by island parsing.
|
|
5
5
|
Author-email: Maxime Lefrançois <maxime.lefrancois@emse.fr>
|
|
6
6
|
License: MIT
|
|
@@ -30,6 +30,9 @@ def main(argv=None):
|
|
|
30
30
|
help="open the interactive console after the script.")
|
|
31
31
|
parser.add_argument("-m", "--map", action="store_true",
|
|
32
32
|
help="also write the language map (<source>.map).")
|
|
33
|
+
parser.add_argument("--target", choices=["micropython"], default=None,
|
|
34
|
+
help="transpile for a device: s{ } is refused here, "
|
|
35
|
+
"at build time, where the message can be read.")
|
|
33
36
|
parser.add_argument("source", nargs="?",
|
|
34
37
|
help=".ldpy (or .py) file to run.")
|
|
35
38
|
args = parser.parse_args(argv)
|
|
@@ -45,7 +48,7 @@ def main(argv=None):
|
|
|
45
48
|
with open(args.source, "r", encoding="utf-8") as f:
|
|
46
49
|
source = f.read()
|
|
47
50
|
try:
|
|
48
|
-
result = transpile(source, args.source)
|
|
51
|
+
result = transpile(source, args.source, target=args.target)
|
|
49
52
|
except LdpySyntaxError as e:
|
|
50
53
|
print(str(e), file=sys.stderr)
|
|
51
54
|
return 1
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""The RDF backend of the façade: rdflib on CPython, urdflib on MicroPython.
|
|
2
|
+
|
|
3
|
+
The generated code never touches rdflib: it goes through `ldpy.runtime`,
|
|
4
|
+
and `ldpy.runtime` takes its terms and graphs from HERE. So this module is
|
|
5
|
+
the one place that knows which library is underneath — and the contract a
|
|
6
|
+
backend must meet is exactly its public names (record urdflib/306 measures
|
|
7
|
+
them with `urdflib/tools/api_gap.py`).
|
|
8
|
+
|
|
9
|
+
Two backends:
|
|
10
|
+
|
|
11
|
+
* **rdflib**, whenever it imports — the default on CPython, and the only one
|
|
12
|
+
with a SPARQL engine (`s{ }`);
|
|
13
|
+
* **urdflib**, the MicroPython C module of the linked-data-python
|
|
14
|
+
organisation, when rdflib is absent. It has the terms and the graphs;
|
|
15
|
+
`Namespace`, `RDF` and `XSD` are pure Python and provided here.
|
|
16
|
+
|
|
17
|
+
The two are not exclusive options of the same kind: the backend is chosen
|
|
18
|
+
by what is importable, and SPARQL queries are a capability of one backend.
|
|
19
|
+
`e{ }` expressions (ldpy.sparql) are pure Python over the terms, and run on
|
|
20
|
+
both.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import rdflib as _lib
|
|
25
|
+
from rdflib import RDF, XSD, BNode, Literal, Namespace, Variable
|
|
26
|
+
from rdflib.term import Node
|
|
27
|
+
URIRef = _lib.URIRef
|
|
28
|
+
Graph = _lib.Graph
|
|
29
|
+
NAME = "rdflib"
|
|
30
|
+
HAS_SPARQL = True
|
|
31
|
+
HAS_NAMESPACE_MANAGER = True
|
|
32
|
+
except ImportError: # MicroPython
|
|
33
|
+
import urdflib as _lib
|
|
34
|
+
from urdflib import BNode, Literal, Node, URIRef, Variable, Graph
|
|
35
|
+
NAME = "urdflib"
|
|
36
|
+
HAS_SPARQL = False
|
|
37
|
+
HAS_NAMESPACE_MANAGER = False
|
|
38
|
+
|
|
39
|
+
class Namespace(str):
|
|
40
|
+
"""A namespace IRI whose attributes and items are terms — rdflib's
|
|
41
|
+
Namespace, reduced to what the emitted code uses."""
|
|
42
|
+
|
|
43
|
+
def __getattr__(self, name):
|
|
44
|
+
if name.startswith("__"):
|
|
45
|
+
raise AttributeError(name)
|
|
46
|
+
return URIRef(self + name)
|
|
47
|
+
|
|
48
|
+
def __getitem__(self, name):
|
|
49
|
+
return URIRef(self + name)
|
|
50
|
+
|
|
51
|
+
def term(self, name):
|
|
52
|
+
return URIRef(self + name)
|
|
53
|
+
|
|
54
|
+
RDF = Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#")
|
|
55
|
+
XSD = Namespace("http://www.w3.org/2001/XMLSchema#")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def new_graph(base=None, identifier=None):
|
|
59
|
+
"""A graph of the backend. rdflib's takes a base, urdflib's does not
|
|
60
|
+
(a base is a lexical matter, resolved by the transpiler already)."""
|
|
61
|
+
if NAME == "rdflib":
|
|
62
|
+
return Graph(base=base, identifier=identifier)
|
|
63
|
+
return Graph(identifier=identifier) if identifier is not None else Graph()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def bind_namespaces(graph, namespaces):
|
|
67
|
+
"""Give `graph` the serialisation prefixes of `namespaces` (a dict
|
|
68
|
+
prefix -> namespace IRI). On rdflib the runtime shares a cached
|
|
69
|
+
NamespaceManager instead (record ldpy/021); this is the plain path."""
|
|
70
|
+
for prefix, ns in namespaces.items():
|
|
71
|
+
graph.bind(prefix, str(ns))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def prepare_sparql(text, namespaces, update=False):
|
|
75
|
+
"""A prepared SPARQL query or update — rdflib's engine, or an error
|
|
76
|
+
that says which backend is missing it."""
|
|
77
|
+
if not HAS_SPARQL:
|
|
78
|
+
raise RuntimeError(
|
|
79
|
+
"s{ } needs a SPARQL engine, and the %s backend has none: "
|
|
80
|
+
"transpile with target='micropython' to be told at build time, "
|
|
81
|
+
"and use m{ } and e{ } on the device" % NAME)
|
|
82
|
+
from rdflib.plugins.sparql import prepareQuery, prepareUpdate
|
|
83
|
+
return (prepareUpdate if update else prepareQuery)(
|
|
84
|
+
text, initNs=dict(namespaces))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
__all__ = ["NAME", "HAS_SPARQL", "HAS_NAMESPACE_MANAGER",
|
|
88
|
+
"URIRef", "BNode", "Literal", "Variable", "Node", "Graph",
|
|
89
|
+
"Namespace", "RDF", "XSD",
|
|
90
|
+
"new_graph", "bind_namespaces", "prepare_sparql"]
|
|
@@ -16,7 +16,35 @@ from ldpy.transpiler import transpile, LdpySyntaxError
|
|
|
16
16
|
DEFAULT_OUT = ".ldpy-build"
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
#: What a device needs of ldpy at run time, and nothing else: the façade,
|
|
20
|
+
#: its backend, and the expressions of e{ }. The transpiler stays on the
|
|
21
|
+
#: host. `ldpy/__init__.py` is NOT copied — it imports the transpiler and
|
|
22
|
+
#: the import hook — and is replaced by the docstring below.
|
|
23
|
+
DEVICE_RUNTIME = ("runtime.py", "backend.py", "sparql.py")
|
|
24
|
+
DEVICE_INIT = ('"""ldpy runtime for a device: what `import ldpy.runtime as '
|
|
25
|
+
'_ldpy_` needs.\n\nWritten by `python -m ldpy.build --target '
|
|
26
|
+
'micropython`; the transpiler is not here.\n"""\n')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def bundle_device_runtime(out_dir):
|
|
30
|
+
"""Copy the device runtime into <out_dir>/ldpy/, so that the emitted
|
|
31
|
+
files and what they import travel together. Returns the paths."""
|
|
32
|
+
here = os.path.dirname(os.path.abspath(__file__))
|
|
33
|
+
dst = os.path.join(out_dir, "ldpy")
|
|
34
|
+
os.makedirs(dst, exist_ok=True)
|
|
35
|
+
written = []
|
|
36
|
+
with open(os.path.join(dst, "__init__.py"), "w", encoding="utf-8") as f:
|
|
37
|
+
f.write(DEVICE_INIT)
|
|
38
|
+
written.append(os.path.join(dst, "__init__.py"))
|
|
39
|
+
for name in DEVICE_RUNTIME:
|
|
40
|
+
with open(os.path.join(here, name), "rb") as fi, \
|
|
41
|
+
open(os.path.join(dst, name), "wb") as fo:
|
|
42
|
+
fo.write(fi.read())
|
|
43
|
+
written.append(os.path.join(dst, name))
|
|
44
|
+
return written
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def build_file(src_path, out_dir, rel=None, target=None):
|
|
20
48
|
"""Transpile one file; returns (py_path, map_path, result)."""
|
|
21
49
|
with open(src_path, "r", encoding="utf-8") as f:
|
|
22
50
|
source = f.read()
|
|
@@ -24,7 +52,7 @@ def build_file(src_path, out_dir, rel=None):
|
|
|
24
52
|
stem = rel[:-5] if rel.endswith(".ldpy") else rel
|
|
25
53
|
py_path = os.path.join(out_dir, stem + ".py")
|
|
26
54
|
map_path = os.path.join(out_dir, stem + ".ldpy.map")
|
|
27
|
-
result = transpile(source, src_path)
|
|
55
|
+
result = transpile(source, src_path, target=target)
|
|
28
56
|
result.map.generated_name = py_path
|
|
29
57
|
os.makedirs(os.path.dirname(py_path) or ".", exist_ok=True)
|
|
30
58
|
with open(py_path, "w", encoding="utf-8") as f:
|
|
@@ -37,7 +65,7 @@ def build_file(src_path, out_dir, rel=None):
|
|
|
37
65
|
return py_path, map_path, result
|
|
38
66
|
|
|
39
67
|
|
|
40
|
-
def build_tree(root, out_dir):
|
|
68
|
+
def build_tree(root, out_dir, target=None):
|
|
41
69
|
"""Recursively transpile every .ldpy under root. Plain .py files are
|
|
42
70
|
copied as they are (a mixed package must stay importable)."""
|
|
43
71
|
built, errors = [], []
|
|
@@ -49,7 +77,7 @@ def build_tree(root, out_dir):
|
|
|
49
77
|
rel = os.path.relpath(src, root)
|
|
50
78
|
if name.endswith(".ldpy"):
|
|
51
79
|
try:
|
|
52
|
-
built.append(build_file(src, out_dir, rel))
|
|
80
|
+
built.append(build_file(src, out_dir, rel, target))
|
|
53
81
|
except LdpySyntaxError as e:
|
|
54
82
|
errors.append(e)
|
|
55
83
|
elif name.endswith(".py"):
|
|
@@ -68,16 +96,23 @@ def main(argv=None):
|
|
|
68
96
|
parser.add_argument("source", help=".ldpy file or directory")
|
|
69
97
|
parser.add_argument("-o", "--out", default=DEFAULT_OUT,
|
|
70
98
|
help="output directory (default: %(default)s)")
|
|
99
|
+
parser.add_argument("--target", choices=["micropython"], default=None,
|
|
100
|
+
help="build for a device: s{ } is refused, and the "
|
|
101
|
+
"ldpy runtime is copied next to the output")
|
|
71
102
|
args = parser.parse_args(argv)
|
|
72
103
|
|
|
104
|
+
if args.target == "micropython":
|
|
105
|
+
os.makedirs(args.out, exist_ok=True)
|
|
106
|
+
bundle_device_runtime(args.out)
|
|
73
107
|
if os.path.isdir(args.source):
|
|
74
|
-
built, errors = build_tree(args.source, args.out)
|
|
108
|
+
built, errors = build_tree(args.source, args.out, args.target)
|
|
75
109
|
for e in errors:
|
|
76
110
|
print(str(e), file=sys.stderr)
|
|
77
111
|
print("%d file(s) transpiled into %s" % (len(built), args.out))
|
|
78
112
|
return 1 if errors else 0
|
|
79
113
|
try:
|
|
80
|
-
py_path, _, result = build_file(args.source, args.out
|
|
114
|
+
py_path, _, result = build_file(args.source, args.out,
|
|
115
|
+
target=args.target)
|
|
81
116
|
except LdpySyntaxError as e:
|
|
82
117
|
print(str(e), file=sys.stderr)
|
|
83
118
|
return 1
|
|
@@ -70,6 +70,12 @@ class LdpyConsole(code.InteractiveConsole):
|
|
|
70
70
|
self._prefixes = dict(prefixes or {})
|
|
71
71
|
self._prefix_cols = {k: 0 for k in self._prefixes}
|
|
72
72
|
self._base = base
|
|
73
|
+
# The current graph and the current bindings are declarations too:
|
|
74
|
+
# like @prefix, a top-level @graph must survive to the next entry
|
|
75
|
+
# (otherwise `+{ ... }` after `@graph as g` sees no graph at all).
|
|
76
|
+
self._graph_var = None
|
|
77
|
+
self._bindings_var = None
|
|
78
|
+
self._counter = 0
|
|
73
79
|
|
|
74
80
|
def runsource(self, source, filename=None, symbol="single"):
|
|
75
81
|
"""Transpile then compile one entry; True = incomplete entry
|
|
@@ -79,6 +85,10 @@ class LdpyConsole(code.InteractiveConsole):
|
|
|
79
85
|
t.prefixes = dict(self._prefixes)
|
|
80
86
|
t._prefix_col = dict(self._prefix_cols)
|
|
81
87
|
t.base = self._base
|
|
88
|
+
t._graph_var = self._graph_var
|
|
89
|
+
t._bindings_var = self._bindings_var
|
|
90
|
+
# fresh names must not collide with those of the previous entries
|
|
91
|
+
t._ns_counter = self._counter
|
|
82
92
|
try:
|
|
83
93
|
result = t.run()
|
|
84
94
|
except LdpySyntaxError as e:
|
|
@@ -98,6 +108,9 @@ class LdpyConsole(code.InteractiveConsole):
|
|
|
98
108
|
self._prefixes = dict(t.prefixes)
|
|
99
109
|
self._prefix_cols = dict(t._prefix_col)
|
|
100
110
|
self._base = t.base
|
|
111
|
+
self._graph_var = t._graph_var
|
|
112
|
+
self._bindings_var = t._bindings_var
|
|
113
|
+
self._counter = t._ns_counter
|
|
101
114
|
for w in result.warnings:
|
|
102
115
|
self.write(str(w) + "\n")
|
|
103
116
|
self.runcode(code_obj)
|
|
@@ -92,11 +92,30 @@ def probe():
|
|
|
92
92
|
#: re-exported: debug tooling looks for them here (pure functions, defined
|
|
93
93
|
#: alongside the map in ldpy/transpiler/linemap.py)
|
|
94
94
|
__all__ = ["stepping_rules", "probe", "package_dir", "load_map",
|
|
95
|
+
"shadow_rel",
|
|
95
96
|
"translate_breakpoints", "translate_frames",
|
|
96
97
|
"snap_breakpoint_line", "snap_breakpoint_lines",
|
|
97
98
|
"run_direct", "main"]
|
|
98
99
|
|
|
99
100
|
|
|
101
|
+
def shadow_rel(source, root=None):
|
|
102
|
+
"""Path of the shadow inside the output directory.
|
|
103
|
+
|
|
104
|
+
Without a root, the basename: the shadow sits beside the source, and two
|
|
105
|
+
files never meet. With one — a workspace whose whole build is gathered in
|
|
106
|
+
a single directory — the tree is mirrored, which is what keeps `a/m.ldpy`
|
|
107
|
+
and `b/m.ldpy` from writing the same `m.py`. A source outside the root
|
|
108
|
+
falls back to its basename rather than climbing out of the output
|
|
109
|
+
directory with `../`."""
|
|
110
|
+
name = os.path.basename(source)
|
|
111
|
+
if not root:
|
|
112
|
+
return name
|
|
113
|
+
rel = os.path.relpath(os.path.abspath(source), os.path.abspath(root))
|
|
114
|
+
if rel.startswith(os.pardir + os.sep) or rel == os.pardir:
|
|
115
|
+
return name
|
|
116
|
+
return rel
|
|
117
|
+
|
|
118
|
+
|
|
100
119
|
def load_map(map_path):
|
|
101
120
|
"""Load a LanguageMap from a .ldpy.map file (JSON)."""
|
|
102
121
|
with open(map_path, "r", encoding="utf-8") as f:
|
|
@@ -167,6 +186,10 @@ def main(argv=None):
|
|
|
167
186
|
"straight in the .ldpy)")
|
|
168
187
|
parser.add_argument("-o", "--out", default=DEFAULT_OUT,
|
|
169
188
|
help="shadow directory (default: %(default)s)")
|
|
189
|
+
parser.add_argument("--root", metavar="DIR",
|
|
190
|
+
help="tree root the shadow mirrors: the .py is "
|
|
191
|
+
"written at OUT/<path of the source relative "
|
|
192
|
+
"to DIR> (default: OUT/<basename>)")
|
|
170
193
|
parser.add_argument("--listen", metavar="HOTE:PORT",
|
|
171
194
|
help="start debugpy listening (e.g. 127.0.0.1:5678)")
|
|
172
195
|
parser.add_argument("--wait-for-client", action="store_true",
|
|
@@ -201,8 +224,7 @@ def main(argv=None):
|
|
|
201
224
|
|
|
202
225
|
try:
|
|
203
226
|
py_path, map_path, result = build_file(
|
|
204
|
-
args.source, args.out,
|
|
205
|
-
rel=os.path.basename(args.source))
|
|
227
|
+
args.source, args.out, rel=shadow_rel(args.source, args.root))
|
|
206
228
|
except LdpySyntaxError as e:
|
|
207
229
|
print(str(e), file=sys.stderr)
|
|
208
230
|
return 1
|