structile 6.0.2__tar.gz → 6.1.2__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 (29) hide show
  1. {structile-6.0.2/structile.egg-info → structile-6.1.2}/PKG-INFO +1 -1
  2. structile-6.1.2/VERSION +1 -0
  3. {structile-6.0.2 → structile-6.1.2}/structile/__init__.py +133 -122
  4. {structile-6.0.2 → structile-6.1.2}/structile/_paths.py +3 -2
  5. {structile-6.0.2 → structile-6.1.2}/structile/_plugins.py +10 -6
  6. {structile-6.0.2 → structile-6.1.2}/structile/convert.py +24 -22
  7. {structile-6.0.2 → structile-6.1.2}/structile/interpreters.py +8 -6
  8. structile-6.1.2/structile/static/structile.prod.html +183 -0
  9. {structile-6.0.2 → structile-6.1.2/structile.egg-info}/PKG-INFO +1 -1
  10. structile-6.0.2/VERSION +0 -1
  11. structile-6.0.2/structile/static/structile.prod.html +0 -199
  12. {structile-6.0.2 → structile-6.1.2}/LICENSE +0 -0
  13. {structile-6.0.2 → structile-6.1.2}/MANIFEST.in +0 -0
  14. {structile-6.0.2 → structile-6.1.2}/README.pypi.md +0 -0
  15. {structile-6.0.2 → structile-6.1.2}/pyproject.toml +0 -0
  16. {structile-6.0.2 → structile-6.1.2}/setup.cfg +0 -0
  17. {structile-6.0.2 → structile-6.1.2}/structile/__main__.py +0 -0
  18. {structile-6.0.2 → structile-6.1.2}/structile/_dom_lite.py +0 -0
  19. {structile-6.0.2 → structile-6.1.2}/structile/_log.py +0 -0
  20. {structile-6.0.2 → structile-6.1.2}/structile/config.py +0 -0
  21. {structile-6.0.2 → structile-6.1.2}/structile/dom_lite.js +0 -0
  22. {structile-6.0.2 → structile-6.1.2}/structile/render.py +0 -0
  23. {structile-6.0.2 → structile-6.1.2}/structile/serialize.py +0 -0
  24. {structile-6.0.2 → structile-6.1.2}/structile/static/widget.js +0 -0
  25. {structile-6.0.2 → structile-6.1.2}/structile/widget.py +0 -0
  26. {structile-6.0.2 → structile-6.1.2}/structile.egg-info/SOURCES.txt +0 -0
  27. {structile-6.0.2 → structile-6.1.2}/structile.egg-info/dependency_links.txt +0 -0
  28. {structile-6.0.2 → structile-6.1.2}/structile.egg-info/requires.txt +0 -0
  29. {structile-6.0.2 → structile-6.1.2}/structile.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: structile
3
- Version: 6.0.2
3
+ Version: 6.1.2
4
4
  Summary: A spatial editor and diff tool for structured data, inline in Jupyter
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.8
@@ -0,0 +1 @@
1
+ 6.1.2
@@ -6,9 +6,10 @@
6
6
 
7
7
  `open()` renders through whichever renderer is active — an inline
8
8
  Jupyter widget, a browser tab, a written HTML file, a terminal summary, or
9
- nothing at all — matplotlib-backend style (see `render.py`). See README.md
10
- for the full picture (renderers, install steps, supported types, current
11
- limitations).
9
+ nothing at all — matplotlib-backend style (see `render.py`). See
10
+ https://danieltuzes.github.io/structile/python-library/ for the full
11
+ picture (renderers, install steps, supported types, current limitations)
12
+ — no repo checkout needed to read it.
12
13
  """
13
14
  from __future__ import annotations
14
15
 
@@ -227,105 +228,107 @@ def open(
227
228
  the last expression in a Jupyter cell, or pass it to
228
229
  `IPython.display.display`, and it renders itself richly there too.
229
230
 
230
- See README.md ("Python / Jupyter (structile.open)") for examples and the
231
- full picture — this is a parameter reference, not a tutorial. Shadows
231
+ See https://danieltuzes.github.io/structile/python-library/ for
232
+ examples and the full picture — this is a parameter reference, not a
233
+ tutorial. Shadows
232
234
  the `open` builtin — always call this namespace-qualified
233
235
  (`structile.open(...)`), never `from structile import open`.
234
236
 
235
- obj: the value to display — any mix of dict/list/tuple/set/None/bool/
236
- int (any size)/float/str/numpy scalar — or a `pathlib.Path`/`str`
237
- naming an existing file to load it from (JSON, falling back to
238
- plain text). When loaded from a path whose content IS valid JSON,
239
- the file's own literal bytes (not a re-serialization) are what the
240
- viewer's source pane shows/edits/saves — so original formatting
241
- (whitespace, key order, ...) survives untouched until an edit
242
- actually touches it. There's no such "original text" for an
243
- in-memory `obj` (no path) or for a path that falls back to plain
244
- text (below) — both show a freshly-serialized JSON view instead, as
245
- they always have. A path whose extension is markup-like (`.xml`,
246
- `.html`/`.htm` — see `interpreters.MARKUP_FORMATS`), or has an
247
- interpreter attached to it (`interpreter=`, or something already
248
- registered via `register_interpreter`), or `format=` set explicitly
249
- to anything other than `"json"`/`"python"`, switches to interpreter
250
- mode: `obj` (or a literal string, with `format=` then required) is
251
- handed to an interpreter instead of being read as a Python value —
252
- see `interpreter=` below. `.xml`/`.html` use the DOM-based
253
- `interpretXML(xmlDocument)` contract; any other format (`.ini`,
254
- `.toml`, ...) uses the raw-text `interpretText(text)` contract — see
255
- interpreters/generic_xml.js vs interpreters/generic_ini.js.
256
- name: display name. Defaults to the loaded file's stem, else "data".
257
- height: iframe height in px (`widget` renderer only).
258
- config: viewer settings for this call — an `Options` instance, or a
259
- plain dict of the same keys (`config={"gap": 8, "forNull": "N/A"}`),
260
- whichever's more convenient; a dict is just turned into an `Options`
261
- internally (see `options_from_dict`).
262
- **settings: individual viewer settings for this call — the numeric
263
- layout knobs (`gap`, `n`, `m`, `N`, `M`, `nameMax`, `valMax`,
264
- `headerLabelMax`, `detCols`, `detRows`, `dwellMs`, `kvRows`,
265
- `kvCols`, `maxWidthFrac`), `theme`, the "Special values" display
266
- overrides the viewer's Settings panel edits under that name —
267
- `forNull`/`forEmpty` (strings shown in place of `None`/`""`) and
268
- `values` (a `{exact_string: replacement}` table) — cosmetic only,
269
- never changes the underlying data; and `linkOpen`/`linkClose`, the
270
- viewer's cross-reference-value marker — a dict value/table cell
271
- wrapped between them, verbatim (delimiters included), that names
272
- another dict/table anywhere in the document becomes a clickable
273
- link that jumps to it (see `Options`). `config=` and
274
- `**settings` both take precedence over `structile.options` /
275
- `set_option(...)`; anything left unset everywhere falls back to the
276
- viewer's default. There's no Settings panel in embedded (widget)
277
- mode, so this is the only way to reach these from Python there —
278
- e.g. `structile.open(data, forNull="N/A")` to show `None` as `"N/A"`
279
- everywhere it appears, including nested inside dicts/lists, or
280
- `structile.open(data, linkOpen="<link.id", linkClose=">")` so a value
281
- like `"<link.id.20>"` links to a same-named dict/table.
282
- interpreter: a path to a `.js` interpreter script, raw JS source
283
- (`interpretXML(xmlDocument)` + optional `serializeXML(value)` for
284
- markup; `interpretText(text)` + optional `serializeText(value)` for
285
- any other text format — see interpreters/generic_xml.js,
286
- interpreters/html.js, interpreters/generic_ini.js), or an
287
- `InterpreterSource` (source text supplied directly rather than read
288
- from a path — what an installed plugin package hands
289
- `register_interpreter`; see `structile.plugins()`); a list mixing
290
- any of the above; or a dict keyed by file extension (`{".xml": [...],
291
- ".html": [...]}`) — only the entry matching the actual file/format
292
- is tried, never another extension's candidates (see
293
- `resolve_candidates`). Multiple candidates are never disambiguated
294
- in Python — the source(s) are forwarded as-is to whichever renderer
295
- is active, which tries each one itself (`tryInterpreterCandidates`
296
- in structile.html) and reports which one worked. This
297
- needs no Node.js, for any renderer including `widget`. Not required
298
- if something's already registered for the extension
299
- (`register_interpreter`, set up once at import time) — this only
300
- overrides that for one call.
301
- format: `"xml"` | `"html"` | any other string (e.g. `"ini"`) —
302
- required alongside `interpreter=`/a registered interpreter when
303
- `obj` isn't a path (no extension to infer it from) — *unless*
304
- `interpreter=` is a dict spanning more than one extension, in which
305
- case omitting `format=` forwards every entry (each under its own
306
- extension's format) to the renderer, which determines the format
307
- itself the same no-Node way (`tryInterpreterCandidatesAnyFormat`) —
308
- Python never has to run anything just to decide which format
309
- applies, or which contract (DOM vs. raw-text) it uses. Any format
310
- other than `"xml"`/`"html"` runs through the raw-text
311
- `interpretText(text)` contract instead of DOM parsing.
312
- path: save destination, overriding `obj`'s own source path (if any) —
313
- `widget` renderer only (the other renderers never write back to a
314
- source file; see the `browser`/`file`/`none`/`text` docs).
315
- renderer: `"widget"` | `"browser"` | `"file"` | `"none"` | `"text"` —
316
- overrides resolution for this call only. Otherwise resolved via
317
- `STRUCTILE_RENDERER` -> `config`/`structile.options.renderer`
318
- (`set_option`/`use`) -> auto-detect (see `render.detect_renderer`).
319
- out: also write the standalone HTML snapshot here (any renderer).
320
- auto_open: set `False` to suppress the `browser` renderer's automatic
321
- `webbrowser.open()` (it still writes the file; call `.open()` later
322
- to open it manually).
323
- default: called on any value that isn't one of `obj`'s own supported
324
- types (see above); its return value is normalized in its place,
325
- same escape hatch as `json.dumps(..., default=...)` — e.g.
326
- `default=str` displays an otherwise-unsupported object as whatever
327
- `str()` shows for it, instead of raising `TypeError`. See
328
- `structile.normalize`.
237
+ Args:
238
+ obj: the value to display — any mix of dict/list/tuple/set/None/bool/
239
+ int (any size)/float/str/numpy scalar — or a `pathlib.Path`/`str`
240
+ naming an existing file to load it from (JSON, falling back to
241
+ plain text). When loaded from a path whose content IS valid JSON,
242
+ the file's own literal bytes (not a re-serialization) are what the
243
+ viewer's source pane shows/edits/saves — so original formatting
244
+ (whitespace, key order, ...) survives untouched until an edit
245
+ actually touches it. There's no such "original text" for an
246
+ in-memory `obj` (no path) or for a path that falls back to plain
247
+ text (below) — both show a freshly-serialized JSON view instead, as
248
+ they always have. A path whose extension is markup-like (`.xml`,
249
+ `.html`/`.htm` — see `interpreters.MARKUP_FORMATS`), or has an
250
+ interpreter attached to it (`interpreter=`, or something already
251
+ registered via `register_interpreter`), or `format=` set explicitly
252
+ to anything other than `"json"`/`"python"`, switches to interpreter
253
+ mode: `obj` (or a literal string, with `format=` then required) is
254
+ handed to an interpreter instead of being read as a Python value —
255
+ see `interpreter=` below. `.xml`/`.html` use the DOM-based
256
+ `interpretXML(xmlDocument)` contract; any other format (`.ini`,
257
+ `.toml`, ...) uses the raw-text `interpretText(text)` contract — see
258
+ interpreters/generic_xml.js vs interpreters/generic_ini.js.
259
+ name: display name. Defaults to the loaded file's stem, else "data".
260
+ height: iframe height in px (`widget` renderer only).
261
+ config: viewer settings for this call — an `Options` instance, or a
262
+ plain dict of the same keys (`config={"gap": 8, "forNull": "N/A"}`),
263
+ whichever's more convenient; a dict is just turned into an `Options`
264
+ internally (see `options_from_dict`).
265
+ **settings: individual viewer settings for this call — the numeric
266
+ layout knobs (`gap`, `n`, `m`, `N`, `M`, `nameMax`, `valMax`,
267
+ `headerLabelMax`, `detCols`, `detRows`, `dwellMs`, `kvRows`,
268
+ `kvCols`, `maxWidthFrac`), `theme`, the "Special values" display
269
+ overrides the viewer's Settings panel edits under that name —
270
+ `forNull`/`forEmpty` (strings shown in place of `None`/`""`) and
271
+ `values` (a `{exact_string: replacement}` table) — cosmetic only,
272
+ never changes the underlying data; and `linkOpen`/`linkClose`, the
273
+ viewer's cross-reference-value marker — a dict value/table cell
274
+ wrapped between them, verbatim (delimiters included), that names
275
+ another dict/table anywhere in the document becomes a clickable
276
+ link that jumps to it (see `Options`). `config=` and
277
+ `**settings` both take precedence over `structile.options` /
278
+ `set_option(...)`; anything left unset everywhere falls back to the
279
+ viewer's default. There's no Settings panel in embedded (widget)
280
+ mode, so this is the only way to reach these from Python there —
281
+ e.g. `structile.open(data, forNull="N/A")` to show `None` as `"N/A"`
282
+ everywhere it appears, including nested inside dicts/lists, or
283
+ `structile.open(data, linkOpen="<link.id", linkClose=">")` so a value
284
+ like `"<link.id.20>"` links to a same-named dict/table.
285
+ interpreter: a path to a `.js` interpreter script, raw JS source
286
+ (`interpretXML(xmlDocument)` + optional `serializeXML(value)` for
287
+ markup; `interpretText(text)` + optional `serializeText(value)` for
288
+ any other text format — see interpreters/generic_xml.js,
289
+ interpreters/html.js, interpreters/generic_ini.js), or an
290
+ `InterpreterSource` (source text supplied directly rather than read
291
+ from a path — what an installed plugin package hands
292
+ `register_interpreter`; see `structile.plugins()`); a list mixing
293
+ any of the above; or a dict keyed by file extension (`{".xml": [...],
294
+ ".html": [...]}`) — only the entry matching the actual file/format
295
+ is tried, never another extension's candidates (see
296
+ `resolve_candidates`). Multiple candidates are never disambiguated
297
+ in Python — the source(s) are forwarded as-is to whichever renderer
298
+ is active, which tries each one itself (`tryInterpreterCandidates`
299
+ in structile.html) and reports which one worked. This
300
+ needs no Node.js, for any renderer including `widget`. Not required
301
+ if something's already registered for the extension
302
+ (`register_interpreter`, set up once at import time) — this only
303
+ overrides that for one call.
304
+ format: `"xml"` | `"html"` | any other string (e.g. `"ini"`) —
305
+ required alongside `interpreter=`/a registered interpreter when
306
+ `obj` isn't a path (no extension to infer it from) — *unless*
307
+ `interpreter=` is a dict spanning more than one extension, in which
308
+ case omitting `format=` forwards every entry (each under its own
309
+ extension's format) to the renderer, which determines the format
310
+ itself the same no-Node way (`tryInterpreterCandidatesAnyFormat`) —
311
+ Python never has to run anything just to decide which format
312
+ applies, or which contract (DOM vs. raw-text) it uses. Any format
313
+ other than `"xml"`/`"html"` runs through the raw-text
314
+ `interpretText(text)` contract instead of DOM parsing.
315
+ path: save destination, overriding `obj`'s own source path (if any) —
316
+ `widget` renderer only (the other renderers never write back to a
317
+ source file; see the `browser`/`file`/`none`/`text` docs).
318
+ renderer: `"widget"` | `"browser"` | `"file"` | `"none"` | `"text"` —
319
+ overrides resolution for this call only. Otherwise resolved via
320
+ `STRUCTILE_RENDERER` -> `config`/`structile.options.renderer`
321
+ (`set_option`/`use`) -> auto-detect (see `render.detect_renderer`).
322
+ out: also write the standalone HTML snapshot here (any renderer).
323
+ auto_open: set `False` to suppress the `browser` renderer's automatic
324
+ `webbrowser.open()` (it still writes the file; call `.open()` later
325
+ to open it manually).
326
+ default: called on any value that isn't one of `obj`'s own supported
327
+ types (see above); its return value is normalized in its place,
328
+ same escape hatch as `json.dumps(..., default=...)` — e.g.
329
+ `default=str` displays an otherwise-unsupported object as whatever
330
+ `str()` shows for it, instead of raising `TypeError`. See
331
+ `structile.normalize`.
329
332
  """
330
333
  if isinstance(config, dict):
331
334
  config = options_from_dict(config)
@@ -482,8 +485,8 @@ def diff(
482
485
  `.left_payload`/`.right_payload`, `.left_value`/`.right_value`,
483
486
  `.open()`, `.to_html()`, `.save(path)`.
484
487
 
485
- See README.md ("Comparing two files (structile.diff)") for examples.
486
- Deliberately narrower than `open()`: no per-side viewer
488
+ See https://danieltuzes.github.io/structile/python-library/#comparing-two-files-structilediff
489
+ for examples. Deliberately narrower than `open()`: no per-side viewer
487
490
  `config=`/`**settings`, and the diff graph itself is always read-only
488
491
  for every renderer (including `widget`) — only each side's own source
489
492
  pane can be independently edited/saved (see `.left_value`/
@@ -491,27 +494,35 @@ def diff(
491
494
  for the `widget` renderer, same as `RenderHandle.value` does for
492
495
  `open()`).
493
496
 
494
- left, right: each independently accepts anything `open()`'s
495
- `obj` does — a Python value, a path, or (with `interpreter=`/
496
- `format=`) a raw XML string.
497
- interpreter, format: a single value applies to BOTH sides (the common
498
- case: two files in the same format/schema); a `(left, right)`
499
- 2-tuple gives each side its own — this is what lets `left` and
500
- `right` be two genuinely different, mutually-incompatible XML
501
- schemas, each with its own interpreter. See `open()`'s
502
- `interpreter=`/`format=` docs for what a single value can be.
503
- name: `(left_name, right_name)` — defaults to each side's own inferred
504
- name (its file stem, or "left"/"right" for a raw string/value).
505
- view: `"unified"` | `"split"`.
506
- key_columns: table key-column overrides — same shape the standalone
507
- viewer's own diff header accepts.
508
- height: iframe height in px (`widget` renderer only).
509
- renderer, out, auto_open: same as `open()`.
510
- default: same escape hatch as `open()`'s `default=`, for
511
- whichever side(s) are plain Python values (a markup/interpreter-mode
512
- side never reaches `normalize()`, so this has no effect there) — a
513
- single value applies to both sides; a `(left, right)` 2-tuple gives
514
- each side its own, same convention as `interpreter=`/`format=`.
497
+ Args:
498
+ left: each independently accepts anything `open()`'s
499
+ `obj` does — a Python value, a path, or (with `interpreter=`/
500
+ `format=`) a raw XML string.
501
+ right: same contract as `left`, independently — the two sides don't
502
+ have to be the same format or schema.
503
+ interpreter: a single value applies to BOTH sides (the common
504
+ case: two files in the same format/schema); a `(left, right)`
505
+ 2-tuple gives each side its own — this is what lets `left` and
506
+ `right` be two genuinely different, mutually-incompatible XML
507
+ schemas, each with its own interpreter. See `open()`'s
508
+ `interpreter=` docs for what a single value can be.
509
+ format: same single-value-or-`(left, right)`-tuple convention as
510
+ `interpreter=`. See `open()`'s `format=` docs for what a single
511
+ value can be.
512
+ name: `(left_name, right_name)` — defaults to each side's own inferred
513
+ name (its file stem, or "left"/"right" for a raw string/value).
514
+ view: `"unified"` | `"split"`.
515
+ key_columns: table key-column overrides — same shape the standalone
516
+ viewer's own diff header accepts.
517
+ height: iframe height in px (`widget` renderer only).
518
+ renderer: same as `open()`.
519
+ out: same as `open()`.
520
+ auto_open: same as `open()`.
521
+ default: same escape hatch as `open()`'s `default=`, for
522
+ whichever side(s) are plain Python values (a markup/interpreter-mode
523
+ side never reaches `normalize()`, so this has no effect there) — a
524
+ single value applies to both sides; a `(left, right)` 2-tuple gives
525
+ each side its own, same convention as `interpreter=`/`format=`.
515
526
  """
516
527
  resolved_renderer = resolve_renderer(renderer, None) or _detect_renderer()
517
528
  left_interp, right_interp = interpreter if isinstance(interpreter, tuple) else (interpreter, interpreter)
@@ -61,8 +61,9 @@ class InterpreterSource:
61
61
  """Interpreter JS source text supplied directly, rather than read from a
62
62
  file path — for an interpreter that lives inside an installed package
63
63
  (e.g. loaded via `importlib.resources.files()` from an entry-point
64
- plugin — see docs/advanced.md's "Distributing interpreters as a
65
- package" and `structile.load_plugins`) instead of at a stable filesystem
64
+ plugin — see
65
+ https://danieltuzes.github.io/structile/advanced-usage/#distributing-interpreters-as-a-package
66
+ and `structile.load_plugins`) instead of at a stable filesystem
66
67
  path.
67
68
 
68
69
  Accepted anywhere a path is accepted: the `interpreter=` argument of
@@ -1,11 +1,14 @@
1
1
  """Entry-point-based interpreter plugin discovery — lets an organisation
2
2
  distribute its own interpreter(s) as an ordinary, `pip install`-able Python
3
3
  package that `structile` picks up with no import of the plugin package, no
4
- registration call, and no wrapper API. See docs/advanced.md's "Distributing
5
- interpreters as a package" section for the full mechanics (precedence,
6
- discovery timing/caching, the `STRUCTILE_DISABLE_PLUGINS` escape hatch) and
7
- README.md for the end-user-facing walkthrough; `tests/fixtures/
8
- structile_demo_plugin/` is a working, copyable template.
4
+ registration call, and no wrapper API. See
5
+ https://danieltuzes.github.io/structile/advanced-usage/#distributing-interpreters-as-a-package
6
+ for the full mechanics (precedence,
7
+ discovery timing/caching, the `STRUCTILE_DISABLE_PLUGINS` escape hatch,
8
+ repo-only, contributor-facing) and
9
+ https://danieltuzes.github.io/structile/interpreters/#python-an-installable-plugin
10
+ for the end-user-facing walkthrough (public, no repo checkout needed);
11
+ `tests/fixtures/structile_demo_plugin/` is a working, copyable template.
9
12
 
10
13
  Plugin contract (public and stable — external packages pin against this):
11
14
 
@@ -92,7 +95,8 @@ class _RegistryFacade:
92
95
  plugin could reach into or break.
93
96
 
94
97
  Enforces the "a plugin registration never beats a real, direct
95
- `register_interpreter()` call" precedence rule (see docs/advanced.md's
98
+ `register_interpreter()` call" precedence rule (see
99
+ https://danieltuzes.github.io/structile/advanced-usage/#distributing-interpreters-as-a-package's
96
100
  precedence table) and tracks which extensions THIS plugin registered,
97
101
  for `structile.plugins()`. If two plugins register the same extension,
98
102
  the last one loaded wins and a WARNING names both distributions —
@@ -66,28 +66,30 @@ def convert(
66
66
  ) -> str:
67
67
  """Convert data from one of the viewer's formats to another, entirely
68
68
  outside the widget/notebook — parse `src`, then serialize the result as
69
- `dst_format`, returning the converted text. See README.md ("Converting
70
- between formats") for the built-in-vs-JS-runtime boundary and examples.
71
-
72
- src: a path (format inferred from its extension: .json/.py/.xml/.html —
73
- or, once an interpreter is attached to it, any other extension too,
74
- e.g. .ini) or a literal string of source text (sniffed: JSON, else
75
- Python-repr, else XML if it looks like markup — there's no
76
- `src_format=` parameter, so a literal string for any OTHER format
77
- needs a `.` path instead, since there's no reliable way to sniff an
78
- arbitrary text format from content alone the way XML's leading `<`
79
- works).
80
- dst_format: `"json"` | `"python"` | `"xml"` | `"html"` | any other
81
- string an interpreter is registered/provided for (e.g. `"ini"`).
82
- interpreter: a path to a `.js` interpreter script, raw JS source, or a
83
- list of candidates tried in order (same contract as
84
- `structile.open(..., interpreter=...)` — see `select_interpreter`) —
85
- required whenever `src` or `dst_format` isn't `"json"`/`"python"`
86
- and nothing is registered for the relevant extension (see
87
- `register_interpreter`); needs the optional `mini-racer` package
88
- (`pip install mini-racer`) to run it — no Node.js, no jsdom, no
89
- system install (see interpreters.run_interpreter_source). Converting
90
- purely between "json" and "python" needs nothing extra at all.
69
+ `dst_format`, returning the converted text. See
70
+ https://danieltuzes.github.io/structile/python-library/#converting-between-formats
71
+ for the built-in-vs-JS-runtime boundary and examples.
72
+
73
+ Args:
74
+ src: a path (format inferred from its extension: .json/.py/.xml/.html —
75
+ or, once an interpreter is attached to it, any other extension too,
76
+ e.g. .ini) or a literal string of source text (sniffed: JSON, else
77
+ Python-repr, else XML if it looks like markup — there's no
78
+ `src_format=` parameter, so a literal string for any OTHER format
79
+ needs a `.` path instead, since there's no reliable way to sniff an
80
+ arbitrary text format from content alone the way XML's leading `<`
81
+ works).
82
+ dst_format: `"json"` | `"python"` | `"xml"` | `"html"` | any other
83
+ string an interpreter is registered/provided for (e.g. `"ini"`).
84
+ interpreter: a path to a `.js` interpreter script, raw JS source, or a
85
+ list of candidates tried in order (same contract as
86
+ `structile.open(..., interpreter=...)` — see `select_interpreter`) —
87
+ required whenever `src` or `dst_format` isn't `"json"`/`"python"`
88
+ and nothing is registered for the relevant extension (see
89
+ `register_interpreter`); needs the optional `mini-racer` package
90
+ (`pip install mini-racer`) to run it — no Node.js, no jsdom, no
91
+ system install (see interpreters.run_interpreter_source). Converting
92
+ purely between "json" and "python" needs nothing extra at all.
91
93
  """
92
94
  text, src_format, src_ext = _infer_source(src, interpreter)
93
95
  value = _parse_by_format(text, src_format, interpreter, src_ext)
@@ -1,6 +1,8 @@
1
1
  """Interpreter registry + selection — markup (XML, HTML included) and any
2
- other text format alike — see README.md's "Custom formats via an
3
- interpreter" section and docs/advanced.md's "Interpreter selection" section
2
+ other text format alike — see
3
+ https://danieltuzes.github.io/structile/interpreters/ (the public guide;
4
+ no repo checkout needed) and
5
+ https://danieltuzes.github.io/structile/advanced-usage/#interpreter-selection
4
6
  for the full picture.
5
7
 
6
8
  An interpreter is caller-supplied JS: `interpretXML(xmlDocument)` (optionally
@@ -222,8 +224,8 @@ def run_interpreter_source(
222
224
  raise RuntimeError(
223
225
  "structile: this needs the 'mini-racer' package (run `pip install "
224
226
  "mini-racer`) — an optional capability, not a hard dependency of "
225
- 'structile itself. See README.md\'s "Custom formats via an '
226
- 'interpreter" section.'
227
+ "structile itself. See "
228
+ "https://danieltuzes.github.io/structile/interpreters/"
227
229
  )
228
230
  from py_mini_racer import JSEvalException, MiniRacer
229
231
 
@@ -348,8 +350,8 @@ def select_interpreter(
348
350
  raise RuntimeError(
349
351
  f"structile: {reason} for {fmt!r} data needs the 'mini-racer' package "
350
352
  "(run `pip install mini-racer`) — an optional capability, not a hard "
351
- 'dependency of structile itself. See README.md\'s "Custom formats via '
352
- 'an interpreter" section.'
353
+ "dependency of structile itself. See "
354
+ "https://danieltuzes.github.io/structile/interpreters/"
353
355
  )
354
356
  if len(candidates) > 1:
355
357
  logger.debug("structile: %d interpreter candidates for %r, trying each in order", len(candidates), fmt)