linked-data-python 0.0.4__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 (105) hide show
  1. {linked-data-python-0.0.4 → linked_data_python-0.2.0}/LICENSE.md +0 -0
  2. linked_data_python-0.2.0/PKG-INFO +158 -0
  3. linked_data_python-0.2.0/README.md +116 -0
  4. linked_data_python-0.2.0/ldpy/__init__.py +35 -0
  5. linked_data_python-0.2.0/ldpy/__main__.py +82 -0
  6. linked_data_python-0.2.0/ldpy/build.py +91 -0
  7. linked_data_python-0.2.0/ldpy/console.py +116 -0
  8. linked_data_python-0.2.0/ldpy/debug.py +235 -0
  9. linked_data_python-0.2.0/ldpy/formatter.py +329 -0
  10. linked_data_python-0.2.0/ldpy/importer.py +110 -0
  11. linked_data_python-0.2.0/ldpy/lsp/__init__.py +11 -0
  12. linked_data_python-0.2.0/ldpy/lsp/__main__.py +4 -0
  13. linked_data_python-0.2.0/ldpy/lsp/backend.py +118 -0
  14. linked_data_python-0.2.0/ldpy/lsp/rpc.py +104 -0
  15. linked_data_python-0.2.0/ldpy/lsp/server.py +353 -0
  16. linked_data_python-0.2.0/ldpy/lsp/translate.py +140 -0
  17. linked_data_python-0.2.0/ldpy/pygments_lexer.py +613 -0
  18. linked_data_python-0.2.0/ldpy/runtime.py +931 -0
  19. linked_data_python-0.2.0/ldpy/sparql.py +551 -0
  20. linked_data_python-0.2.0/ldpy/transpiler/__init__.py +14 -0
  21. linked_data_python-0.2.0/ldpy/transpiler/core.py +2558 -0
  22. linked_data_python-0.2.0/ldpy/transpiler/errors.py +38 -0
  23. linked_data_python-0.2.0/ldpy/transpiler/linemap.py +292 -0
  24. linked_data_python-0.2.0/linked_data_python.egg-info/PKG-INFO +158 -0
  25. linked_data_python-0.2.0/linked_data_python.egg-info/SOURCES.txt +63 -0
  26. {linked-data-python-0.0.4 → linked_data_python-0.2.0}/linked_data_python.egg-info/dependency_links.txt +0 -0
  27. linked_data_python-0.2.0/linked_data_python.egg-info/entry_points.txt +9 -0
  28. linked_data_python-0.2.0/linked_data_python.egg-info/requires.txt +22 -0
  29. {linked-data-python-0.0.4 → linked_data_python-0.2.0}/linked_data_python.egg-info/top_level.txt +0 -0
  30. linked_data_python-0.2.0/pyproject.toml +66 -0
  31. {linked-data-python-0.0.4 → linked_data_python-0.2.0}/setup.cfg +0 -0
  32. linked_data_python-0.2.0/tests/test_bench.py +62 -0
  33. linked_data_python-0.2.0/tests/test_bindings.py +270 -0
  34. linked_data_python-0.2.0/tests/test_build.py +48 -0
  35. linked_data_python-0.2.0/tests/test_call_suffix.py +144 -0
  36. linked_data_python-0.2.0/tests/test_charsets.py +108 -0
  37. linked_data_python-0.2.0/tests/test_coercion.py +141 -0
  38. linked_data_python-0.2.0/tests/test_console.py +96 -0
  39. linked_data_python-0.2.0/tests/test_current_graph.py +237 -0
  40. linked_data_python-0.2.0/tests/test_debug.py +167 -0
  41. linked_data_python-0.2.0/tests/test_debug_stepping.py +368 -0
  42. linked_data_python-0.2.0/tests/test_disambiguation.py +236 -0
  43. linked_data_python-0.2.0/tests/test_docs.py +46 -0
  44. linked_data_python-0.2.0/tests/test_docstrings.py +57 -0
  45. linked_data_python-0.2.0/tests/test_examples.py +34 -0
  46. linked_data_python-0.2.0/tests/test_formatter.py +274 -0
  47. linked_data_python-0.2.0/tests/test_generator.py +77 -0
  48. linked_data_python-0.2.0/tests/test_graphs.py +469 -0
  49. linked_data_python-0.2.0/tests/test_identity.py +109 -0
  50. linked_data_python-0.2.0/tests/test_import_hook.py +55 -0
  51. linked_data_python-0.2.0/tests/test_islands_terms.py +184 -0
  52. linked_data_python-0.2.0/tests/test_linemap.py +118 -0
  53. linked_data_python-0.2.0/tests/test_lsp_rpc.py +72 -0
  54. linked_data_python-0.2.0/tests/test_lsp_server.py +339 -0
  55. linked_data_python-0.2.0/tests/test_lsp_translate.py +147 -0
  56. linked_data_python-0.2.0/tests/test_match_island.py +182 -0
  57. linked_data_python-0.2.0/tests/test_micropython_subset.py +93 -0
  58. linked_data_python-0.2.0/tests/test_prefix_import.py +262 -0
  59. linked_data_python-0.2.0/tests/test_prefix_scoping.py +302 -0
  60. linked_data_python-0.2.0/tests/test_pygments_lexer.py +313 -0
  61. linked_data_python-0.2.0/tests/test_scope_modifiers.py +200 -0
  62. linked_data_python-0.2.0/tests/test_semantics.py +177 -0
  63. linked_data_python-0.2.0/tests/test_sourcemap_v3.py +120 -0
  64. linked_data_python-0.2.0/tests/test_sparql_expr.py +255 -0
  65. linked_data_python-0.2.0/tests/test_sparql_island.py +161 -0
  66. linked-data-python-0.0.4/CHANGELOG.md +0 -20
  67. linked-data-python-0.0.4/MANIFEST.in +0 -8
  68. linked-data-python-0.0.4/PKG-INFO +0 -134
  69. linked-data-python-0.0.4/README.md +0 -92
  70. linked-data-python-0.0.4/examples/async_stmt.ldpy +0 -14
  71. linked-data-python-0.0.4/examples/bnode_1.ldpy +0 -6
  72. linked-data-python-0.0.4/examples/bnode_2.ldpy +0 -7
  73. linked-data-python-0.0.4/examples/classdef.ldpy +0 -16
  74. linked-data-python-0.0.4/examples/decorated.ldpy +0 -30
  75. linked-data-python-0.0.4/examples/errors/error1.ldpy +0 -7
  76. linked-data-python-0.0.4/examples/errors/error2.ldpy +0 -5
  77. linked-data-python-0.0.4/examples/errors/error3.ldpy +0 -3
  78. linked-data-python-0.0.4/examples/for_stmt.ldpy +0 -31
  79. linked-data-python-0.0.4/examples/funcdef.ldpy +0 -28
  80. linked-data-python-0.0.4/examples/if_stmt.ldpy +0 -19
  81. linked-data-python-0.0.4/examples/instantiateBGP.ldpy +0 -27
  82. linked-data-python-0.0.4/examples/lists.ldpy +0 -6
  83. linked-data-python-0.0.4/examples/terms.ldpy +0 -40
  84. linked-data-python-0.0.4/examples/while_stmt.ldpy +0 -10
  85. linked-data-python-0.0.4/ldpy/__init__.py +0 -11
  86. linked-data-python-0.0.4/ldpy/__main__.py +0 -257
  87. linked-data-python-0.0.4/ldpy/grun/lib.py +0 -63
  88. linked-data-python-0.0.4/ldpy/grun/util.py +0 -11
  89. linked-data-python-0.0.4/ldpy/ldpy.py +0 -183
  90. linked-data-python-0.0.4/ldpy/rewriter/IndentedStringWriter.py +0 -54
  91. linked-data-python-0.0.4/ldpy/rewriter/LDPythonRewriter.py +0 -677
  92. linked-data-python-0.0.4/ldpy/rewriter/MultiChannelTokenStream.py +0 -127
  93. linked-data-python-0.0.4/ldpy/rewriter/Result.py +0 -49
  94. linked-data-python-0.0.4/ldpy/rewriter/__init__.py +0 -7
  95. linked-data-python-0.0.4/ldpy/rewriter/antlr/LDPythonLexer.py +0 -870
  96. linked-data-python-0.0.4/ldpy/rewriter/antlr/LDPythonParser.py +0 -9336
  97. linked-data-python-0.0.4/ldpy/rewriter/antlr/LDPythonVisitor.py +0 -573
  98. linked-data-python-0.0.4/ldpy/sparql/builtin.py +0 -326
  99. linked-data-python-0.0.4/ldpyIcon.png +0 -0
  100. linked-data-python-0.0.4/linked_data_python.egg-info/PKG-INFO +0 -134
  101. linked-data-python-0.0.4/linked_data_python.egg-info/SOURCES.txt +0 -42
  102. linked-data-python-0.0.4/linked_data_python.egg-info/entry_points.txt +0 -3
  103. linked-data-python-0.0.4/linked_data_python.egg-info/requires.txt +0 -3
  104. linked-data-python-0.0.4/setup.py +0 -58
  105. linked-data-python-0.0.4/test/.gitkeep +0 -0
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: linked-data-python
3
+ Version: 0.2.0
4
+ Summary: Python extended with Semantic Web primitives: IRIs, RDF literals and Turtle-notation graphs as expressions, transpiled by island parsing.
5
+ Author-email: Maxime Lefrançois <maxime.lefrancois@emse.fr>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/linked-data-python/ldpy
8
+ Project-URL: Documentation, https://linked-data-python.readthedocs.io/
9
+ Project-URL: Source, https://github.com/linked-data-python/ldpy
10
+ Keywords: rdf,semantic-web,turtle,sparql,transpiler,linked-data,knowledge-graph
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Compilers
20
+ Classifier: Topic :: Software Development :: Pre-processors
21
+ Classifier: Topic :: Text Processing :: Markup
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE.md
25
+ Requires-Dist: rdflib>=6.0
26
+ Provides-Extra: lsp
27
+ Requires-Dist: python-lsp-server; extra == "lsp"
28
+ Requires-Dist: pyflakes; extra == "lsp"
29
+ Provides-Extra: debug
30
+ Requires-Dist: debugpy; extra == "debug"
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest; extra == "dev"
33
+ Provides-Extra: highlight
34
+ Requires-Dist: pygments>=2.14; extra == "highlight"
35
+ Provides-Extra: format
36
+ Requires-Dist: black>=24; extra == "format"
37
+ Provides-Extra: docs
38
+ Requires-Dist: mkdocs; extra == "docs"
39
+ Requires-Dist: mkdocs-material; extra == "docs"
40
+ Requires-Dist: pygments>=2.14; extra == "docs"
41
+ Dynamic: license-file
42
+
43
+ # Linked-Data Python
44
+
45
+ **Python, with the Semantic Web in its syntax.** IRIs, prefixed names, RDF
46
+ literals, SPARQL variables and whole graphs written in Turtle's notation are
47
+ expressions of the language — interpolated with arbitrary Python, transpiled to
48
+ plain Python, running on rdflib.
49
+
50
+ ![](ldpyIcon.png)
51
+
52
+ ```text
53
+ @prefix sosa: <http://www.w3.org/ns/sosa/> .
54
+ @base <http://example.org/building/> .
55
+
56
+ def observation(sensor, value):
57
+ return g{ f<sensor/{sensor}> a sosa:Sensor ;
58
+ sosa:madeObservation [ sosa:hasSimpleResult {value} ] }
59
+ ```
60
+
61
+ The language also reads and queries, writes into a current graph, and turns
62
+ rows into triples:
63
+
64
+ ```text
65
+ @prefix ex: <http://example.org/> .
66
+ @graph as kg
67
+ for @bindings in csv.DictReader(f): # any iterable of mappings
68
+ +{ e<http://example.org/{?id}> ex:value ?v }
69
+
70
+ for s, v in m{ ?s ex:value ?v }: # a graph pattern, no engine
71
+ ...
72
+ rows = s{ SELECT ?s WHERE { ?s ex:value ?v } } # all of SPARQL, checked early
73
+ adult = e{ ?age >= 18 && BOUND(?name) } # deferred, over bindings
74
+ ```
75
+
76
+ `.ldpy` files are **transpiled to plain Python** by an *island parser*: the
77
+ Python is copied verbatim — every valid Python file is a valid ldpy file,
78
+ returned byte-identical — and only the RDF islands are parsed and rewritten.
79
+ The transpiler is ~1 500 lines with no parsing dependency and sustains
80
+ 56 000–110 000 source lines/s depending on island density.
81
+
82
+ ## Quick start
83
+
84
+ ```text
85
+ git clone https://github.com/linked-data-python/ldpy.git
86
+ cd linked-data-python && pip install -e . # or: pip install -e .[lsp,debug]
87
+
88
+ ldpy program.ldpy # run a file
89
+ ldpy # interactive console
90
+ ldpy-lsp # language server (LSP, stdio)
91
+ ldpy-debug program.ldpy # debug via the shadow .py + debugpy
92
+ ```
93
+
94
+ From Python: `import ldpy; ldpy.install()` then `import yourmodule` finds
95
+ `yourmodule.ldpy` on `sys.path`.
96
+
97
+ ## Documentation
98
+
99
+ Read it at [`docs/`](docs/README.md) — start with the
100
+ [home page](docs/README.md) for an overview, then:
101
+
102
+ - **Tutorials** — [first steps](docs/tutorials/getting-started.md), then
103
+ [build a knowledge graph](docs/tutorials/build-a-knowledge-graph.md) from
104
+ tabular data.
105
+ - **How-to guides** — [run & import](docs/how-to/run-and-import.md),
106
+ [build graphs from tables](docs/how-to/build-graphs-from-tables.md),
107
+ [read and query](docs/how-to/query-a-graph.md),
108
+ [migrate from rdflib](docs/how-to/migrate-from-rdflib.md),
109
+ [VS Code](docs/how-to/use-vscode.md), [debugging](docs/how-to/debug.md),
110
+ [language server](docs/how-to/language-server.md),
111
+ [highlighting](docs/how-to/highlight-ldpy.md).
112
+ - **Reference** — [the language](docs/reference/language/index.md), one page
113
+ per island family; [SPARQL expressions](docs/reference/sparql-expressions.md);
114
+ [CLI](docs/reference/cli.md); [Python API](docs/reference/api.md);
115
+ [language map formats](docs/reference/language-map.md).
116
+ - **Explanation** — [why](docs/explanation/why.md),
117
+ [what real RDF code does](docs/explanation/what-real-code-does.md) (the
118
+ corpus study that drove the language's second wave),
119
+ [designing the syntax](docs/explanation/designing-the-syntax.md),
120
+ [island parsing](docs/explanation/island-parsing.md),
121
+ [emission & semantics](docs/explanation/emission-and-semantics.md),
122
+ [tooling](docs/explanation/tooling.md),
123
+ [how this is tested](docs/explanation/how-it-is-tested.md).
124
+
125
+ Every `ldpy` and `python` block in the documentation is executed by the test
126
+ suite, and its assertions are the test.
127
+
128
+ ## Tooling
129
+
130
+ - **VS Code extension** (`vscode-ldpy`): highlighting (TextMate + LSP semantic
131
+ tokens), diagnostics as you type, completion/hover/definition, run and debug.
132
+ - **Language server**: dependency-free, LSP over stdio; delegates Python
133
+ intelligence to an unmodified `pylsp` through the language map.
134
+ - **Debugging**: `.ldpy` code compiles in `.ldpy` coordinates, so `pdb` and
135
+ `debugpy` work directly; `ldpy.build` also materialises real `.py` shadow
136
+ files with JSON and Source Map v3 maps.
137
+ - **Highlighting anywhere else**: the package registers a Pygments lexer built
138
+ on the language map — MkDocs, Sphinx and `pygmentize` colour `.ldpy` with no
139
+ further setup.
140
+ - **Benchmark harness** (`bench/`): seeded random program generator and
141
+ reproducible throughput campaigns.
142
+
143
+ ## Project
144
+
145
+ - Tests: `python -m pytest tests/ -q` — byte-identity over the CPython standard
146
+ library, golden transpilation, RDF isomorphism against rdflib as an oracle,
147
+ LSP end to end, executable documentation.
148
+ - Licence: MIT. Author: Maxime Lefrançois (Mines Saint-Étienne).
149
+ - The 2023 ANTLR-based release (v1, PyPI 0.0.4) is preliminary work, superseded
150
+ by this island-parsing rewrite (the `main` branch of this repository; the 2023
151
+ code remains on the legacy gitlab.com/coswot/ldpy).
152
+
153
+ ## Design records
154
+
155
+ Every non-trivial choice in this repository is written down, one file per
156
+ decision, in the [`pilotage`](https://github.com/linked-data-python/pilotage) repository. Comments and docs
157
+ cite them by identifier — `ldpy/024`, `vscode/103` — which resolves to
158
+ [`design/`](https://github.com/linked-data-python/pilotage/tree/main/design).
@@ -0,0 +1,116 @@
1
+ # Linked-Data Python
2
+
3
+ **Python, with the Semantic Web in its syntax.** IRIs, prefixed names, RDF
4
+ literals, SPARQL variables and whole graphs written in Turtle's notation are
5
+ expressions of the language — interpolated with arbitrary Python, transpiled to
6
+ plain Python, running on rdflib.
7
+
8
+ ![](ldpyIcon.png)
9
+
10
+ ```text
11
+ @prefix sosa: <http://www.w3.org/ns/sosa/> .
12
+ @base <http://example.org/building/> .
13
+
14
+ def observation(sensor, value):
15
+ return g{ f<sensor/{sensor}> a sosa:Sensor ;
16
+ sosa:madeObservation [ sosa:hasSimpleResult {value} ] }
17
+ ```
18
+
19
+ The language also reads and queries, writes into a current graph, and turns
20
+ rows into triples:
21
+
22
+ ```text
23
+ @prefix ex: <http://example.org/> .
24
+ @graph as kg
25
+ for @bindings in csv.DictReader(f): # any iterable of mappings
26
+ +{ e<http://example.org/{?id}> ex:value ?v }
27
+
28
+ for s, v in m{ ?s ex:value ?v }: # a graph pattern, no engine
29
+ ...
30
+ rows = s{ SELECT ?s WHERE { ?s ex:value ?v } } # all of SPARQL, checked early
31
+ adult = e{ ?age >= 18 && BOUND(?name) } # deferred, over bindings
32
+ ```
33
+
34
+ `.ldpy` files are **transpiled to plain Python** by an *island parser*: the
35
+ Python is copied verbatim — every valid Python file is a valid ldpy file,
36
+ returned byte-identical — and only the RDF islands are parsed and rewritten.
37
+ The transpiler is ~1 500 lines with no parsing dependency and sustains
38
+ 56 000–110 000 source lines/s depending on island density.
39
+
40
+ ## Quick start
41
+
42
+ ```text
43
+ git clone https://github.com/linked-data-python/ldpy.git
44
+ cd linked-data-python && pip install -e . # or: pip install -e .[lsp,debug]
45
+
46
+ ldpy program.ldpy # run a file
47
+ ldpy # interactive console
48
+ ldpy-lsp # language server (LSP, stdio)
49
+ ldpy-debug program.ldpy # debug via the shadow .py + debugpy
50
+ ```
51
+
52
+ From Python: `import ldpy; ldpy.install()` then `import yourmodule` finds
53
+ `yourmodule.ldpy` on `sys.path`.
54
+
55
+ ## Documentation
56
+
57
+ Read it at [`docs/`](docs/README.md) — start with the
58
+ [home page](docs/README.md) for an overview, then:
59
+
60
+ - **Tutorials** — [first steps](docs/tutorials/getting-started.md), then
61
+ [build a knowledge graph](docs/tutorials/build-a-knowledge-graph.md) from
62
+ tabular data.
63
+ - **How-to guides** — [run & import](docs/how-to/run-and-import.md),
64
+ [build graphs from tables](docs/how-to/build-graphs-from-tables.md),
65
+ [read and query](docs/how-to/query-a-graph.md),
66
+ [migrate from rdflib](docs/how-to/migrate-from-rdflib.md),
67
+ [VS Code](docs/how-to/use-vscode.md), [debugging](docs/how-to/debug.md),
68
+ [language server](docs/how-to/language-server.md),
69
+ [highlighting](docs/how-to/highlight-ldpy.md).
70
+ - **Reference** — [the language](docs/reference/language/index.md), one page
71
+ per island family; [SPARQL expressions](docs/reference/sparql-expressions.md);
72
+ [CLI](docs/reference/cli.md); [Python API](docs/reference/api.md);
73
+ [language map formats](docs/reference/language-map.md).
74
+ - **Explanation** — [why](docs/explanation/why.md),
75
+ [what real RDF code does](docs/explanation/what-real-code-does.md) (the
76
+ corpus study that drove the language's second wave),
77
+ [designing the syntax](docs/explanation/designing-the-syntax.md),
78
+ [island parsing](docs/explanation/island-parsing.md),
79
+ [emission & semantics](docs/explanation/emission-and-semantics.md),
80
+ [tooling](docs/explanation/tooling.md),
81
+ [how this is tested](docs/explanation/how-it-is-tested.md).
82
+
83
+ Every `ldpy` and `python` block in the documentation is executed by the test
84
+ suite, and its assertions are the test.
85
+
86
+ ## Tooling
87
+
88
+ - **VS Code extension** (`vscode-ldpy`): highlighting (TextMate + LSP semantic
89
+ tokens), diagnostics as you type, completion/hover/definition, run and debug.
90
+ - **Language server**: dependency-free, LSP over stdio; delegates Python
91
+ intelligence to an unmodified `pylsp` through the language map.
92
+ - **Debugging**: `.ldpy` code compiles in `.ldpy` coordinates, so `pdb` and
93
+ `debugpy` work directly; `ldpy.build` also materialises real `.py` shadow
94
+ files with JSON and Source Map v3 maps.
95
+ - **Highlighting anywhere else**: the package registers a Pygments lexer built
96
+ on the language map — MkDocs, Sphinx and `pygmentize` colour `.ldpy` with no
97
+ further setup.
98
+ - **Benchmark harness** (`bench/`): seeded random program generator and
99
+ reproducible throughput campaigns.
100
+
101
+ ## Project
102
+
103
+ - Tests: `python -m pytest tests/ -q` — byte-identity over the CPython standard
104
+ library, golden transpilation, RDF isomorphism against rdflib as an oracle,
105
+ LSP end to end, executable documentation.
106
+ - Licence: MIT. Author: Maxime Lefrançois (Mines Saint-Étienne).
107
+ - The 2023 ANTLR-based release (v1, PyPI 0.0.4) is preliminary work, superseded
108
+ by this island-parsing rewrite (the `main` branch of this repository; the 2023
109
+ code remains on the legacy gitlab.com/coswot/ldpy).
110
+
111
+ ## Design records
112
+
113
+ Every non-trivial choice in this repository is written down, one file per
114
+ decision, in the [`pilotage`](https://github.com/linked-data-python/pilotage) repository. Comments and docs
115
+ cite them by identifier — `ldpy/024`, `vscode/103` — which resolves to
116
+ [`design/`](https://github.com/linked-data-python/pilotage/tree/main/design).
@@ -0,0 +1,35 @@
1
+ """Linked-Data Python (ldpy) v2.
2
+
3
+ An "island parsing" transpiler: Python is copied through untouched, the RDF
4
+ islands (@prefix, @base, IRIs, prefixed names, RDF literals, variables,
5
+ graphs g{...}) are rewritten into Python expressions built on ldpy.runtime.
6
+
7
+ The historical ANTLR chain (v1) is still reachable through the ldpy.rewriter
8
+ package but is no longer imported by default.
9
+ """
10
+
11
+ from ldpy.transpiler import transpile, LdpySyntaxError, LdpyWarning
12
+ from ldpy.importer import install, uninstall, install_excepthook
13
+
14
+ __version__ = "0.1.0.dev0"
15
+ __date__ = "2026-08-26"
16
+
17
+
18
+ def transform_source(source, filename="<ldpy>"):
19
+ """Simplified v1 compatibility: returns (python_code, prefixes, map)."""
20
+ result = transpile(source, filename)
21
+ return result.code, result.prefixes, result.map
22
+
23
+
24
+ def Coercion(rules):
25
+ """Politique de conversion Python -> RDF (fiche 020) — voir
26
+ ldpy.runtime.Coercion; exposed here as public API."""
27
+ from ldpy.runtime import Coercion as _C
28
+ return _C(rules)
29
+
30
+
31
+ def instantiateBGP(input, solutionMappings, initialGraph=None):
32
+ """Instantiate a graph template with solution mappings
33
+ (re-export of ldpy.runtime.instantiateBGP, v1 compatibility)."""
34
+ from ldpy.runtime import instantiateBGP as _f
35
+ return _f(input, solutionMappings, initialGraph)
@@ -0,0 +1,82 @@
1
+ """ldpy — command-line execution.
2
+
3
+ Usage :
4
+ python -m ldpy source.ldpy # transpile and run
5
+ python -m ldpy -s source.ldpy # also print the transformed code
6
+ python -m ldpy -t source.ldpy # transpile seulement (stdout)
7
+ """
8
+
9
+ import argparse
10
+ import os
11
+ import sys
12
+
13
+ import ldpy
14
+ from ldpy.transpiler import transpile, LdpySyntaxError
15
+ from ldpy.transpiler.linemap import compile_mapped
16
+
17
+
18
+ def main(argv=None):
19
+ parser = argparse.ArgumentParser(
20
+ prog="ldpy",
21
+ description="ldpy extends Python syntax with Semantic Web "
22
+ "primitives (IRIs, RDF literals, graphs).")
23
+ parser.add_argument("-v", "--version", action="store_true",
24
+ help="print the version and exit.")
25
+ parser.add_argument("-s", "--show-changes", action="store_true",
26
+ help="print the transformed code before running it.")
27
+ parser.add_argument("-t", "--transpile-only", action="store_true",
28
+ help="write the transformed code to stdout, do not run.")
29
+ parser.add_argument("-i", "--interactive", action="store_true",
30
+ help="open the interactive console after the script.")
31
+ parser.add_argument("-m", "--map", action="store_true",
32
+ help="also write the language map (<source>.map).")
33
+ parser.add_argument("source", nargs="?",
34
+ help=".ldpy (or .py) file to run.")
35
+ args = parser.parse_args(argv)
36
+
37
+ if args.version:
38
+ print("ldpy " + ldpy.__version__)
39
+ return 0
40
+ if not args.source:
41
+ from ldpy.console import interact
42
+ interact()
43
+ return 0
44
+
45
+ with open(args.source, "r", encoding="utf-8") as f:
46
+ source = f.read()
47
+ try:
48
+ result = transpile(source, args.source)
49
+ except LdpySyntaxError as e:
50
+ print(str(e), file=sys.stderr)
51
+ return 1
52
+ for w in result.warnings:
53
+ print(str(w), file=sys.stderr)
54
+ if args.map:
55
+ with open(args.source + ".map", "w", encoding="utf-8") as f:
56
+ f.write(result.map.to_json(indent=1))
57
+ if args.transpile_only:
58
+ sys.stdout.write(result.code)
59
+ return 0
60
+ if args.show_changes:
61
+ print("ldpy>>> ======== transformed code ========", file=sys.stderr)
62
+ for lineno, line in enumerate(result.code.split("\n"), 1):
63
+ print("ldpy>>> %3d: %s" % (lineno, line), file=sys.stderr)
64
+ print("ldpy>>> =================================", file=sys.stderr)
65
+
66
+ ldpy.install()
67
+ from ldpy.importer import MAPS
68
+ src_path = os.path.abspath(args.source)
69
+ MAPS[args.source] = MAPS[src_path] = result.map
70
+ # remapped compilation: tracebacks, pdb and debugpy all speak in
71
+ # .ldpy coordinates (record ldpy/011)
72
+ code = compile_mapped(result.code, result.map, src_path)
73
+ g = {"__name__": "__main__", "__file__": src_path}
74
+ exec(code, g)
75
+ if args.interactive:
76
+ from ldpy.console import interact
77
+ interact(locals=g, prefixes=result.prefixes, base=result.base)
78
+ return 0
79
+
80
+
81
+ if __name__ == "__main__":
82
+ sys.exit(main())
@@ -0,0 +1,91 @@
1
+ """Materialisation: transpile .ldpy files into a shadow directory.
2
+
3
+ `python -m ldpy.build src/ -o .ldpy-build` mirrors the tree and writes:
4
+ - <module>.py (generated code)
5
+ - <module>.ldpy.map (language map JSON)
6
+
7
+ This is the base of debugging (debugpy runs on the shadow .py files) and of
8
+ language server (voir docs/explanation/tooling.md)."""
9
+
10
+ import os
11
+ import sys
12
+ import argparse
13
+
14
+ from ldpy.transpiler import transpile, LdpySyntaxError
15
+
16
+ DEFAULT_OUT = ".ldpy-build"
17
+
18
+
19
+ def build_file(src_path, out_dir, rel=None):
20
+ """Transpile one file; returns (py_path, map_path, result)."""
21
+ with open(src_path, "r", encoding="utf-8") as f:
22
+ source = f.read()
23
+ rel = rel or os.path.basename(src_path)
24
+ stem = rel[:-5] if rel.endswith(".ldpy") else rel
25
+ py_path = os.path.join(out_dir, stem + ".py")
26
+ map_path = os.path.join(out_dir, stem + ".ldpy.map")
27
+ result = transpile(source, src_path)
28
+ result.map.generated_name = py_path
29
+ os.makedirs(os.path.dirname(py_path) or ".", exist_ok=True)
30
+ with open(py_path, "w", encoding="utf-8") as f:
31
+ f.write(result.code)
32
+ with open(map_path, "w", encoding="utf-8") as f:
33
+ f.write(result.map.to_json(indent=1))
34
+ # Source Map v3: for standard tooling
35
+ with open(py_path + ".map", "w", encoding="utf-8") as f:
36
+ f.write(result.map.to_sourcemap_v3_json())
37
+ return py_path, map_path, result
38
+
39
+
40
+ def build_tree(root, out_dir):
41
+ """Recursively transpile every .ldpy under root. Plain .py files are
42
+ copied as they are (a mixed package must stay importable)."""
43
+ built, errors = [], []
44
+ for dirpath, dirnames, filenames in os.walk(root):
45
+ dirnames[:] = [d for d in dirnames
46
+ if d not in (DEFAULT_OUT, "__pycache__", ".git")]
47
+ for name in filenames:
48
+ src = os.path.join(dirpath, name)
49
+ rel = os.path.relpath(src, root)
50
+ if name.endswith(".ldpy"):
51
+ try:
52
+ built.append(build_file(src, out_dir, rel))
53
+ except LdpySyntaxError as e:
54
+ errors.append(e)
55
+ elif name.endswith(".py"):
56
+ dst = os.path.join(out_dir, rel)
57
+ os.makedirs(os.path.dirname(dst) or ".", exist_ok=True)
58
+ with open(src, "rb") as fi, open(dst, "wb") as fo:
59
+ fo.write(fi.read())
60
+ return built, errors
61
+
62
+
63
+ def main(argv=None):
64
+ parser = argparse.ArgumentParser(
65
+ prog="ldpy.build",
66
+ description="Transpile .ldpy files into a shadow directory "
67
+ "(.py + .ldpy.map).")
68
+ parser.add_argument("source", help=".ldpy file or directory")
69
+ parser.add_argument("-o", "--out", default=DEFAULT_OUT,
70
+ help="output directory (default: %(default)s)")
71
+ args = parser.parse_args(argv)
72
+
73
+ if os.path.isdir(args.source):
74
+ built, errors = build_tree(args.source, args.out)
75
+ for e in errors:
76
+ print(str(e), file=sys.stderr)
77
+ print("%d file(s) transpiled into %s" % (len(built), args.out))
78
+ return 1 if errors else 0
79
+ try:
80
+ py_path, _, result = build_file(args.source, args.out)
81
+ except LdpySyntaxError as e:
82
+ print(str(e), file=sys.stderr)
83
+ return 1
84
+ for w in result.warnings:
85
+ print(str(w), file=sys.stderr)
86
+ print(py_path)
87
+ return 0
88
+
89
+
90
+ if __name__ == "__main__":
91
+ sys.exit(main())
@@ -0,0 +1,116 @@
1
+ """Console interactive Linked-Data Python .
2
+
3
+ The point of the `ideas` package in v1 was to enter the interpreter and type
4
+ ldpy directly. This console does it without `ideas`: every entry is
5
+ transpiled then compiled; the state of top-level @prefix/@base persists from
6
+ one entry to the next (declarations made inside a block die with the entry —
7
+ block scope obliges).
8
+
9
+ $ python -m ldpy # console
10
+ $ python -m ldpy -i script.ldpy # run, then open the console
11
+ """
12
+
13
+ import atexit
14
+ import code
15
+ import codeop
16
+ import os
17
+ import sys
18
+
19
+ import ldpy
20
+ from ldpy.transpiler import LdpySyntaxError
21
+ from ldpy.transpiler.core import Transpiler, PRELUDE
22
+
23
+ HISTORY_FILE = os.path.join(os.path.expanduser("~"), ".ldpy_history")
24
+ HISTORY_LENGTH = 1000
25
+
26
+
27
+ def _setup_readline(locals):
28
+ """Line editing (arrows, Ctrl-A/E…), persistent history and Tab
29
+ completion on console names. A no-op if the readline module is missing
30
+ (Windows without pyreadline)."""
31
+ try:
32
+ import readline
33
+ import rlcompleter
34
+ except ImportError:
35
+ return
36
+ readline.set_completer(rlcompleter.Completer(locals).complete)
37
+ if "libedit" in (getattr(readline, "__doc__", "") or ""):
38
+ readline.parse_and_bind("bind ^I rl_complete") # macOS libedit
39
+ else:
40
+ readline.parse_and_bind("tab: complete")
41
+ try:
42
+ readline.read_history_file(HISTORY_FILE)
43
+ except OSError:
44
+ pass
45
+ readline.set_history_length(HISTORY_LENGTH)
46
+
47
+ def _save():
48
+ try:
49
+ readline.write_history_file(HISTORY_FILE)
50
+ except OSError:
51
+ pass
52
+ atexit.register(_save)
53
+
54
+ BANNER = ("ldpy %s — console Linked-Data Python (Python %s)\n"
55
+ "RDF islands are accepted: @prefix, <iri>, ex:name, g{ ... }, ?v")
56
+
57
+
58
+ class LdpyConsole(code.InteractiveConsole):
59
+ """Interactive console: transpile every entry before compiling it;
60
+ top-level @prefix/@base persist between entries."""
61
+
62
+ def __init__(self, locals=None, filename="<console>",
63
+ prefixes=None, base=None):
64
+ if locals is None:
65
+ locals = {"__name__": "__console__", "__doc__": None}
66
+ super().__init__(locals=locals, filename=filename)
67
+ # the runtime prelude is installed once and for all
68
+ if "__namespaces__" not in self.locals:
69
+ exec(PRELUDE, self.locals)
70
+ self._prefixes = dict(prefixes or {})
71
+ self._prefix_cols = {k: 0 for k in self._prefixes}
72
+ self._base = base
73
+
74
+ def runsource(self, source, filename=None, symbol="single"):
75
+ """Transpile then compile one entry; True = incomplete entry
76
+ (unclosed island or Python block), False = handled."""
77
+ filename = filename or self.filename
78
+ t = Transpiler(source, filename, emit_prelude=False)
79
+ t.prefixes = dict(self._prefixes)
80
+ t._prefix_col = dict(self._prefix_cols)
81
+ t.base = self._base
82
+ try:
83
+ result = t.run()
84
+ except LdpySyntaxError as e:
85
+ if getattr(e, "at_eof", False):
86
+ return True # unclosed island: wait for more
87
+ self.write(str(e) + "\n")
88
+ return False
89
+ try:
90
+ code_obj = codeop.compile_command(result.code, filename, symbol)
91
+ except (SyntaxError, ValueError, OverflowError):
92
+ self.showsyntaxerror(filename)
93
+ return False
94
+ if code_obj is None:
95
+ return True # Python incomplet (def, if, ...)
96
+ # the entry is complete: top-level declarations persist
97
+ t._unwind_scopes(0)
98
+ self._prefixes = dict(t.prefixes)
99
+ self._prefix_cols = dict(t._prefix_col)
100
+ self._base = t.base
101
+ for w in result.warnings:
102
+ self.write(str(w) + "\n")
103
+ self.runcode(code_obj)
104
+ return False
105
+
106
+
107
+ def interact(locals=None, prefixes=None, base=None):
108
+ """Open the ldpy console (banner, Ctrl-D to leave)."""
109
+ console = LdpyConsole(locals=locals, prefixes=prefixes, base=base)
110
+ _setup_readline(console.locals)
111
+ banner = BANNER % (ldpy.__version__, sys.version.split()[0])
112
+ try:
113
+ console.interact(banner=banner, exitmsg="")
114
+ except SystemExit:
115
+ pass
116
+ return console