lvxml 0.2.1__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.
- lvxml-0.2.1/.gitignore +16 -0
- lvxml-0.2.1/CHANGELOG.md +62 -0
- lvxml-0.2.1/CONTRIBUTING.md +22 -0
- lvxml-0.2.1/LICENSE +21 -0
- lvxml-0.2.1/PKG-INFO +254 -0
- lvxml-0.2.1/README.md +221 -0
- lvxml-0.2.1/pyproject.toml +73 -0
- lvxml-0.2.1/scripts/capture_ni.py +41 -0
- lvxml-0.2.1/src/lvxml/__init__.py +32 -0
- lvxml-0.2.1/src/lvxml/__main__.py +9 -0
- lvxml-0.2.1/src/lvxml/_checks.py +110 -0
- lvxml-0.2.1/src/lvxml/_cli.py +167 -0
- lvxml-0.2.1/src/lvxml/_decode.py +109 -0
- lvxml-0.2.1/src/lvxml/_dump.py +106 -0
- lvxml-0.2.1/src/lvxml/_encode.py +158 -0
- lvxml-0.2.1/src/lvxml/_errors.py +16 -0
- lvxml-0.2.1/src/lvxml/_load.py +59 -0
- lvxml-0.2.1/src/lvxml/_node.py +85 -0
- lvxml-0.2.1/src/lvxml/_numbers.py +83 -0
- lvxml-0.2.1/src/lvxml/_parse.py +168 -0
- lvxml-0.2.1/src/lvxml/_schema.py +114 -0
- lvxml-0.2.1/src/lvxml/_treejson.py +162 -0
- lvxml-0.2.1/src/lvxml/py.typed +0 -0
- lvxml-0.2.1/tests/conftest.py +17 -0
- lvxml-0.2.1/tests/fixtures/ni/MANIFEST.json +11 -0
- lvxml-0.2.1/tests/fixtures/real/example-flattened-xml-file.xml +72 -0
- lvxml-0.2.1/tests/fixtures/schema/bare-cluster.xml +12 -0
- lvxml-0.2.1/tests/fixtures/schema/containers.xml +62 -0
- lvxml-0.2.1/tests/fixtures/schema/enums-refnums.xml +30 -0
- lvxml-0.2.1/tests/fixtures/schema/scalars.xml +24 -0
- lvxml-0.2.1/tests/test_cli.py +114 -0
- lvxml-0.2.1/tests/test_codec.py +124 -0
- lvxml-0.2.1/tests/test_contract.py +63 -0
- lvxml-0.2.1/tests/test_e2e_installed.py +97 -0
- lvxml-0.2.1/tests/test_ground_truth.py +142 -0
- lvxml-0.2.1/tests/test_hardening.py +168 -0
- lvxml-0.2.1/tests/test_tree.py +136 -0
- lvxml-0.2.1/tests/test_types.py +158 -0
- lvxml-0.2.1/tests/test_xsd_agreement.py +119 -0
- lvxml-0.2.1/tests/xsd_check.py +41 -0
lvxml-0.2.1/.gitignore
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
.venv/
|
|
4
|
+
venv/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
htmlcov/
|
|
13
|
+
|
|
14
|
+
# NI's schema, copied from a local LabVIEW install by scripts/capture_ni.py; only the manifest is tracked.
|
|
15
|
+
tests/fixtures/ni/*
|
|
16
|
+
!tests/fixtures/ni/MANIFEST.json
|
lvxml-0.2.1/CHANGELOG.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.1]
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- NI's `LVXMLSchema.xsd` is no longer tracked or shipped in the sdist. `scripts/capture_ni.py`
|
|
14
|
+
copies it from a local LabVIEW install into `tests/fixtures/ni` (checked against the tracked
|
|
15
|
+
manifest), and the tests that validate against it skip when it is absent.
|
|
16
|
+
|
|
17
|
+
## [0.2.0]
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Every element of LabVIEW's `LVXMLSchema.xsd`: `EXT`, complex `CSG`/`CDB`/`CXT`, `Path`,
|
|
22
|
+
`DAQChannel`, the `EB`/`EW`/`EL` enums, `Refnum`, `LvVariant`, `Timestamp`, `WDT`,
|
|
23
|
+
`DynamicData`, `DigitalData` and every waveform element.
|
|
24
|
+
- Multi-dimensional arrays: nested lists in row-major order, `Array(element, ndim=2)`.
|
|
25
|
+
- `LVEnum(choices, tag)` schema; enum values encode from a label or an index.
|
|
26
|
+
- Lossless typed tree: `parse`, `dump`, `Node`, `Document`, `to_node`, `to_value`, and
|
|
27
|
+
dict conversion with `to_dict`/`from_dict`. LabVIEW-written files round-trip byte for
|
|
28
|
+
byte.
|
|
29
|
+
- `from_lv_xml_all` for files holding several values.
|
|
30
|
+
- `validate`, which lists every problem with its location.
|
|
31
|
+
- `lvxml` command with `to-json`, `to-xml` and `validate`, also as `python -m lvxml`.
|
|
32
|
+
- `LVXMLError.path` locating the offending element.
|
|
33
|
+
- Tests against the official XSD and a file written by LabVIEW 2026.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- The reader enforces the schema's element order and rejects unknown elements, stray
|
|
38
|
+
text, attributes, a missing `<Name>`, elements outside the root's namespace, a DOCTYPE
|
|
39
|
+
and CDATA sections. Nesting deeper than 100 levels raises.
|
|
40
|
+
- `NumElts` must match the elements present, a `Cluster`'s `NumElts` is no longer
|
|
41
|
+
ignored, and an integer outside its type's range or duplicate cluster field names now
|
|
42
|
+
raise instead of being read silently. A comma decimal separator still raises, now with
|
|
43
|
+
a hint.
|
|
44
|
+
- Bytes input is decoded by the encoding its XML declaration names (UTF-8 by default);
|
|
45
|
+
any other input type raises `TypeError`.
|
|
46
|
+
- Error messages start with the location of the offending element.
|
|
47
|
+
- Floats are read strictly: digits with an optional period and exponent, or `NaN`,
|
|
48
|
+
`Inf` and `Infinity` in any case. Python-only spellings such as `1_000` raise.
|
|
49
|
+
- `to_lv_xml` writes empty elements as `<Name></Name>`, like LabVIEW, and non-finite
|
|
50
|
+
floats as `NaN`, `Inf` and `-Inf`. A `SGL` or `CSG` value too large for single
|
|
51
|
+
precision raises. An `Array` element can no longer be an `Array`, which the schema
|
|
52
|
+
does not allow.
|
|
53
|
+
|
|
54
|
+
## [0.1.0]
|
|
55
|
+
|
|
56
|
+
- First release.
|
|
57
|
+
- `from_lv_xml` parses LabVIEW Flatten-To-XML text into native Python values.
|
|
58
|
+
- `to_lv_xml` renders Python values back to XML that Unflatten From XML accepts, driven by
|
|
59
|
+
an `Array`/`Cluster`/scalar-tag schema.
|
|
60
|
+
- Covered subset: the `DBL`, `SGL`, `I8`/`I16`/`I32`/`I64`, `U8`/`U16`/`U32`/`U64` numerics,
|
|
61
|
+
`Boolean`, `String`, one-dimensional `Array` and `Cluster`, nested freely, with the
|
|
62
|
+
`<LVData>` file wrapper unwrapped on read. Unsupported types raise `LVXMLError`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for taking the time. This project keeps a small, predictable shape.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install -r requirements-dev.txt
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Before you open a pull request
|
|
12
|
+
|
|
13
|
+
- Keep public API in `src/lvxml/__init__.py`; internals live in `_`-prefixed modules.
|
|
14
|
+
- Add a test for the behaviour you change. Check new behaviour against the fixtures in
|
|
15
|
+
`tests/fixtures`: files LabVIEW itself wrote, and the official `LVXMLSchema.xsd` once
|
|
16
|
+
`python scripts/capture_ni.py` has copied it from a LabVIEW install (it is never
|
|
17
|
+
committed). Never edit a file under `tests/fixtures/real`; add new LabVIEW output
|
|
18
|
+
alongside it instead.
|
|
19
|
+
- Run `scripts/check.ps1` (format, lint, types, tests) and make sure it passes.
|
|
20
|
+
- One logical change per commit, imperative subject line.
|
|
21
|
+
|
|
22
|
+
Open an issue first if the change is large or shifts the public API.
|
lvxml-0.2.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Javier Lianes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
lvxml-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lvxml
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Codec for LabVIEW Flatten To XML / Unflatten From XML between its fixed schema and native Python values
|
|
5
|
+
Project-URL: Homepage, https://lianes8studio.com
|
|
6
|
+
Project-URL: Repository, https://github.com/JaviLianes8/lvxml
|
|
7
|
+
Project-URL: Issues, https://github.com/JaviLianes8/lvxml/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/JaviLianes8/lvxml/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: LinkedIn, https://www.linkedin.com/in/jlianes/
|
|
10
|
+
Project-URL: Funding, https://buymeacoffee.com/jlianesglrs
|
|
11
|
+
Author-email: Javier Lianes <javier@lianes8studio.com>
|
|
12
|
+
License-Expression: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: codec,flatten,labview,ni,unflatten,xml
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Environment :: Console
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Intended Audience :: Science/Research
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: build; extra == 'dev'
|
|
25
|
+
Requires-Dist: editables; extra == 'dev'
|
|
26
|
+
Requires-Dist: hatchling; extra == 'dev'
|
|
27
|
+
Requires-Dist: lxml; extra == 'dev'
|
|
28
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
31
|
+
Requires-Dist: twine; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# lvxml
|
|
35
|
+
|
|
36
|
+
Codec for LabVIEW Flatten To XML / Unflatten From XML between its fixed schema and native
|
|
37
|
+
Python values. Typed, zero dependencies, Python 3.10+.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
pip install lvxml
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Read LabVIEW XML as Python values
|
|
46
|
+
|
|
47
|
+
LabVIEW's Flatten To XML emits a fixed shape: each value is an element named after its
|
|
48
|
+
datatype (`DBL`, `I32`, `Boolean`, `String`, `Array`, `Cluster`, ...), carrying a `<Name>`
|
|
49
|
+
and either a `<Val>` or, for containers, `<Dimsize>` or `<NumElts>` and child elements.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from lvxml import from_lv_xml, from_lv_xml_all
|
|
53
|
+
|
|
54
|
+
from_lv_xml("<I32><Name>count</Name><Val>-7</Val></I32>")
|
|
55
|
+
# -7
|
|
56
|
+
|
|
57
|
+
from_lv_xml(
|
|
58
|
+
"<Cluster><Name>pt</Name><NumElts>2</NumElts>"
|
|
59
|
+
"<DBL><Name>x</Name><Val>1.0</Val></DBL>"
|
|
60
|
+
"<I32><Name>y</Name><Val>5</Val></I32></Cluster>"
|
|
61
|
+
)
|
|
62
|
+
# {'x': 1.0, 'y': 5}
|
|
63
|
+
|
|
64
|
+
with open("Example Flattened XML File.xml", encoding="utf-8") as f:
|
|
65
|
+
from_lv_xml_all(f.read()) # every value in an <LVData> file, Version header skipped
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| LabVIEW element | Python value |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| `I8` `I16` `I32` `I64` `U8` `U16` `U32` `U64` | `int`, range checked |
|
|
71
|
+
| `SGL` `DBL` `EXT` | `float` (`NaN`, `Inf`, `-Inf` accepted) |
|
|
72
|
+
| `CSG` `CDB` `CXT` | `complex`, from `re +im i` text |
|
|
73
|
+
| `Boolean` | `bool` (`1`/`0`, also `true`/`false`) |
|
|
74
|
+
| `String` `Path` `DAQChannel` | `str` |
|
|
75
|
+
| `EB` `EW` `EL` enums | the selected `<Choice>` label |
|
|
76
|
+
| `Refnum` | its `<Val>` text |
|
|
77
|
+
| `Array` | `list`, nested once per `<Dimsize>`, row-major |
|
|
78
|
+
| `Cluster` | `dict` keyed by field name, in order |
|
|
79
|
+
| `LvVariant`, `Timestamp`, `WDT`, `DynamicData`, `DigitalData`, waveforms | `list` of their items' values |
|
|
80
|
+
|
|
81
|
+
A cluster whose fields share a name (LabVIEW allows unnamed fields) cannot become a dict
|
|
82
|
+
without losing data, so it raises; read it through the typed tree below instead.
|
|
83
|
+
|
|
84
|
+
## Write Python values as LabVIEW XML
|
|
85
|
+
|
|
86
|
+
A schema names the LabVIEW type of each value, since a Python `int` could be any width
|
|
87
|
+
and a `list` any element type:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from lvxml import Array, Cluster, LVEnum, to_lv_xml
|
|
91
|
+
|
|
92
|
+
to_lv_xml([1.0, 2.5], Array("DBL"), name="xs")
|
|
93
|
+
to_lv_xml([[1, 2], [3, 4]], Array("I32", ndim=2), name="grid")
|
|
94
|
+
to_lv_xml(
|
|
95
|
+
{"x": 1.5, "flag": True, "mode": "Run", "z": 1 - 2j},
|
|
96
|
+
Cluster({"x": "DBL", "flag": "Boolean", "mode": LVEnum(("Idle", "Run")), "z": "CDB"}),
|
|
97
|
+
name="pt",
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A scalar schema is a tag string: any integer, float or complex tag above, `"Boolean"`,
|
|
102
|
+
`"String"`, `"Path"` or `"DAQChannel"`. `Array(element, ndim=1)` is an array (LabVIEW has
|
|
103
|
+
no array of arrays, so `element` cannot be an `Array`), `Cluster(fields)` an ordered
|
|
104
|
+
mapping of field name to schema, and `LVEnum(choices, tag="EW")` an enum whose value may
|
|
105
|
+
be given as a label or an index. An empty array is written with `Dimsize` 0 and one
|
|
106
|
+
default element, so the element type stays visible. `to_node` builds the typed tree
|
|
107
|
+
instead of text.
|
|
108
|
+
|
|
109
|
+
## The typed tree: lossless read and write
|
|
110
|
+
|
|
111
|
+
`parse` keeps everything: names, the raw `<Val>` text, enum choices, `Dimsize`,
|
|
112
|
+
`NumElts`, refnum fields and the `<Version>` header. `dump` writes it back in the layout
|
|
113
|
+
LabVIEW's file writer uses (one element per line, CRLF, no indentation), so a file
|
|
114
|
+
LabVIEW wrote comes back byte for byte.
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from lvxml import dump, parse, to_value
|
|
118
|
+
|
|
119
|
+
with open("Example Flattened XML File.xml", encoding="utf-8", newline="") as f:
|
|
120
|
+
text = f.read() # newline="" keeps LabVIEW's CRLF line breaks
|
|
121
|
+
|
|
122
|
+
document = parse(text)
|
|
123
|
+
document.version # '26.3.1f1'
|
|
124
|
+
node = document.values[3] # Node(tag='Cluster', name='Random Graph Data', num_elts=3, ...)
|
|
125
|
+
to_value(node) # the plain Python value
|
|
126
|
+
assert dump(document) == text
|
|
127
|
+
document.to_dict() # JSON-ready; Document.from_dict() reverses it
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`dump(tree, newline="\r\n", indent="", declaration=None)` also writes a single `Node`
|
|
131
|
+
bare, the way Flatten To XML returns it, and refuses a node the schema cannot express.
|
|
132
|
+
|
|
133
|
+
## Validate
|
|
134
|
+
|
|
135
|
+
`validate(text)` returns a list of problems, empty when the document is good. It first
|
|
136
|
+
checks the element structure `LVXMLSchema.xsd` defines, then every value: numbers that
|
|
137
|
+
parse and fit their type, enum indexes inside their choices, and `NumElts` and `Dimsize`
|
|
138
|
+
that match the elements present. Each problem names its location, such as
|
|
139
|
+
`Cluster[pt]/I8[y]: 300 is out of range for I8 (-128..127)`. Problems in the data raise
|
|
140
|
+
`LVXMLError` (a `ValueError`) with the same location in its `path` attribute. Building an
|
|
141
|
+
impossible schema object, such as `LVEnum(())`, raises `ValueError` or `TypeError`.
|
|
142
|
+
|
|
143
|
+
`parse`, `from_lv_xml`, `from_lv_xml_all` and `validate` take `str` or `bytes`; bytes are
|
|
144
|
+
decoded by the encoding the XML declaration names, or as UTF-8.
|
|
145
|
+
|
|
146
|
+
## Command line
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
lvxml to-json data.xml -o data.json # lossless tree, every name and raw value kept
|
|
150
|
+
lvxml to-json --values data.xml # plain values: numbers, strings, lists, dicts
|
|
151
|
+
lvxml to-xml data.json -o data.xml # tree JSON back to LabVIEW XML
|
|
152
|
+
lvxml validate data.xml other.xml # check shape and values, exit 1 on any problem
|
|
153
|
+
echo "<I32><Name>n</Name><Val>7</Val></I32>" | lvxml to-json --values -
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`-` reads standard input. `--encoding` sets the file encoding (default `utf-8`); if a
|
|
157
|
+
file is not UTF-8, name its encoding, for example `--encoding cp1252`.
|
|
158
|
+
`to-xml --newline lf` switches from CRLF. `--values` writes strict JSON: complex numbers
|
|
159
|
+
become `{"re": ..., "im": ...}` and non-finite floats the strings `"NaN"`, `"Inf"` and
|
|
160
|
+
`"-Inf"`. Errors go to standard error with exit status 1. `lvxml --help` lists
|
|
161
|
+
everything.
|
|
162
|
+
|
|
163
|
+
## What each rule rests on
|
|
164
|
+
|
|
165
|
+
Checked against the real LabVIEW file in the tests:
|
|
166
|
+
|
|
167
|
+
- The layout `dump` writes by default: the `<?xml version='1.0' standalone='yes' ?>`
|
|
168
|
+
declaration, `<LVData xmlns="http://www.ni.com/LVData">`, a `<Version>` header, one
|
|
169
|
+
element per line, CRLF, no indentation and no final line break.
|
|
170
|
+
- `DBL`, `Cluster` and one-dimensional `Array` values, and decimals written with a period.
|
|
171
|
+
|
|
172
|
+
Checked against the shipped XSD: the element structure of every type, including
|
|
173
|
+
multi-dimensional `Array` (repeated `<Dimsize>`), the enum `<Choice>` list and the
|
|
174
|
+
optional `Refnum` fields. The XSD declares the target namespace
|
|
175
|
+
`http://www.ni.com/labview` while the real file uses `http://www.ni.com/LVData`; the
|
|
176
|
+
reader accepts either, or none, keeps whichever it found, and requires every element to
|
|
177
|
+
share the root's namespace.
|
|
178
|
+
|
|
179
|
+
Taken from the format strings in LabVIEW 2026's run-time library (`lvrt.dll`), with no
|
|
180
|
+
real sample in the tests yet:
|
|
181
|
+
|
|
182
|
+
- complex values as `re +im i` (the writer's `<Val>%s +%s i</Val>`);
|
|
183
|
+
- only `&`, `<` and `>` escaped in text (the only entity strings beside the XML
|
|
184
|
+
writer's tags are `<`, `>` and `&`), so quotes are written as they are;
|
|
185
|
+
- integers and `Dimsize` as plain decimals (`%d`, `%lld`, `%llu`).
|
|
186
|
+
|
|
187
|
+
Choices `lvxml` makes where neither source settles the question:
|
|
188
|
+
|
|
189
|
+
- `Boolean` is written as `1` or `0`; the reader also accepts `true` and `false`.
|
|
190
|
+
- Non-finite floats are written as `NaN`, `Inf` and `-Inf` (the spellings found in the
|
|
191
|
+
run-time library) and read case-insensitively.
|
|
192
|
+
- Floats are written with the shortest text that reads back exactly. A comma decimal
|
|
193
|
+
separator raises.
|
|
194
|
+
- A carriage return inside a value is written as is and survives a read with `lvxml`,
|
|
195
|
+
where a plain XML parser would turn it into a line feed.
|
|
196
|
+
- A DOCTYPE or a CDATA section is refused, nesting deeper than 100 levels raises, and an
|
|
197
|
+
empty array whose shape would expand to more than 100000 nested lists raises.
|
|
198
|
+
|
|
199
|
+
## Test fixtures
|
|
200
|
+
|
|
201
|
+
The tests check the codec against ground truth from a LabVIEW 2026 installation:
|
|
202
|
+
|
|
203
|
+
- `LVXMLSchema.xsd` is the schema LabVIEW ships in
|
|
204
|
+
`C:\Program Files\National Instruments\LabVIEW 2026\vi.lib\Utility\LVXMLSchema.xsd`.
|
|
205
|
+
It belongs to NI, so it is not in the repository or the sdist: run
|
|
206
|
+
`python scripts/capture_ni.py` on a machine with LabVIEW to copy it into
|
|
207
|
+
`tests/fixtures/ni` (size and sha256 are checked against `tests/fixtures/ni/MANIFEST.json`).
|
|
208
|
+
Without it, the tests that validate against the schema skip.
|
|
209
|
+
- `tests/fixtures/real/example-flattened-xml-file.xml` is **real LabVIEW output**,
|
|
210
|
+
kept byte for byte: the file `Example Flattened XML File.xml` that LabVIEW 2026
|
|
211
|
+
(version header `26.3.1f1`) wrote to `Documents\LabVIEW Data` when running the shipped
|
|
212
|
+
example `examples\File IO\XML\Flatten and Unflatten XML\Flatten Data and Write to XML
|
|
213
|
+
File.vi`. It holds `DBL` values and a `Cluster` with a ten-element `Array`.
|
|
214
|
+
- `tests/fixtures/schema/*.xml` are **schema-derived**: written by hand from the XSD to
|
|
215
|
+
cover every element it defines. They are valid against the XSD, but their payloads are
|
|
216
|
+
illustrative; in particular the cluster contents of the timestamp and waveform elements
|
|
217
|
+
are not LabVIEW's actual internal layout, which the schema does not specify.
|
|
218
|
+
|
|
219
|
+
The real file and every schema-derived fixture are validated against the XSD with `lxml`
|
|
220
|
+
(a test-only dependency) when the schema is present, parsed, decoded, and written back: the real file byte for byte,
|
|
221
|
+
the others canonically equal. Samples that the XSD rejects are rejected by the reader as
|
|
222
|
+
well. An end-to-end test installs the package and drives the installed `lvxml` command
|
|
223
|
+
on the real file.
|
|
224
|
+
|
|
225
|
+
## Not covered
|
|
226
|
+
|
|
227
|
+
Fixed-point `FXP` numbers and the `<Default>` and `<Attributes>` children of a variant
|
|
228
|
+
appear among the run-time library's writer strings but are absent from the shipped
|
|
229
|
+
schema, so the reader raises a clear error for them. The
|
|
230
|
+
timestamp and waveform elements are read as the schema describes them (a name and
|
|
231
|
+
clusters), not interpreted further.
|
|
232
|
+
|
|
233
|
+
## Why
|
|
234
|
+
|
|
235
|
+
LabVIEW's Flatten To XML and Unflatten From XML use one fixed schema
|
|
236
|
+
(`LVXMLSchema.xsd`): datatype-named elements, `Name`-ordered fields, a period decimal
|
|
237
|
+
separator independent of locale. LabVIEW produces and reads this schema natively, and NI's
|
|
238
|
+
community GXML library also converts data to and from XML, but both run inside LabVIEW and
|
|
239
|
+
ship no Python reader. On the Python side, with no LabVIEW running, a generic XML library
|
|
240
|
+
parses the tree but knows nothing of that convention, so turning it into usable Python, or
|
|
241
|
+
producing XML LabVIEW will accept, is left to hand-written glue every time. Existing LabVIEW
|
|
242
|
+
Python packages (pylabview, lvkit, autolv) parse VI source files, not this flattened data
|
|
243
|
+
format, and no package on PyPI covers it. `lvxml` is a small, typed, zero-dependency codec
|
|
244
|
+
for it.
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT. See [LICENSE](LICENSE).
|
|
249
|
+
|
|
250
|
+
## Links
|
|
251
|
+
|
|
252
|
+
- Website: https://lianes8studio.com
|
|
253
|
+
- GitHub: https://github.com/JaviLianes8
|
|
254
|
+
- LinkedIn: https://www.linkedin.com/in/jlianes/
|
lvxml-0.2.1/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# lvxml
|
|
2
|
+
|
|
3
|
+
Codec for LabVIEW Flatten To XML / Unflatten From XML between its fixed schema and native
|
|
4
|
+
Python values. Typed, zero dependencies, Python 3.10+.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
pip install lvxml
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Read LabVIEW XML as Python values
|
|
13
|
+
|
|
14
|
+
LabVIEW's Flatten To XML emits a fixed shape: each value is an element named after its
|
|
15
|
+
datatype (`DBL`, `I32`, `Boolean`, `String`, `Array`, `Cluster`, ...), carrying a `<Name>`
|
|
16
|
+
and either a `<Val>` or, for containers, `<Dimsize>` or `<NumElts>` and child elements.
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from lvxml import from_lv_xml, from_lv_xml_all
|
|
20
|
+
|
|
21
|
+
from_lv_xml("<I32><Name>count</Name><Val>-7</Val></I32>")
|
|
22
|
+
# -7
|
|
23
|
+
|
|
24
|
+
from_lv_xml(
|
|
25
|
+
"<Cluster><Name>pt</Name><NumElts>2</NumElts>"
|
|
26
|
+
"<DBL><Name>x</Name><Val>1.0</Val></DBL>"
|
|
27
|
+
"<I32><Name>y</Name><Val>5</Val></I32></Cluster>"
|
|
28
|
+
)
|
|
29
|
+
# {'x': 1.0, 'y': 5}
|
|
30
|
+
|
|
31
|
+
with open("Example Flattened XML File.xml", encoding="utf-8") as f:
|
|
32
|
+
from_lv_xml_all(f.read()) # every value in an <LVData> file, Version header skipped
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| LabVIEW element | Python value |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `I8` `I16` `I32` `I64` `U8` `U16` `U32` `U64` | `int`, range checked |
|
|
38
|
+
| `SGL` `DBL` `EXT` | `float` (`NaN`, `Inf`, `-Inf` accepted) |
|
|
39
|
+
| `CSG` `CDB` `CXT` | `complex`, from `re +im i` text |
|
|
40
|
+
| `Boolean` | `bool` (`1`/`0`, also `true`/`false`) |
|
|
41
|
+
| `String` `Path` `DAQChannel` | `str` |
|
|
42
|
+
| `EB` `EW` `EL` enums | the selected `<Choice>` label |
|
|
43
|
+
| `Refnum` | its `<Val>` text |
|
|
44
|
+
| `Array` | `list`, nested once per `<Dimsize>`, row-major |
|
|
45
|
+
| `Cluster` | `dict` keyed by field name, in order |
|
|
46
|
+
| `LvVariant`, `Timestamp`, `WDT`, `DynamicData`, `DigitalData`, waveforms | `list` of their items' values |
|
|
47
|
+
|
|
48
|
+
A cluster whose fields share a name (LabVIEW allows unnamed fields) cannot become a dict
|
|
49
|
+
without losing data, so it raises; read it through the typed tree below instead.
|
|
50
|
+
|
|
51
|
+
## Write Python values as LabVIEW XML
|
|
52
|
+
|
|
53
|
+
A schema names the LabVIEW type of each value, since a Python `int` could be any width
|
|
54
|
+
and a `list` any element type:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from lvxml import Array, Cluster, LVEnum, to_lv_xml
|
|
58
|
+
|
|
59
|
+
to_lv_xml([1.0, 2.5], Array("DBL"), name="xs")
|
|
60
|
+
to_lv_xml([[1, 2], [3, 4]], Array("I32", ndim=2), name="grid")
|
|
61
|
+
to_lv_xml(
|
|
62
|
+
{"x": 1.5, "flag": True, "mode": "Run", "z": 1 - 2j},
|
|
63
|
+
Cluster({"x": "DBL", "flag": "Boolean", "mode": LVEnum(("Idle", "Run")), "z": "CDB"}),
|
|
64
|
+
name="pt",
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
A scalar schema is a tag string: any integer, float or complex tag above, `"Boolean"`,
|
|
69
|
+
`"String"`, `"Path"` or `"DAQChannel"`. `Array(element, ndim=1)` is an array (LabVIEW has
|
|
70
|
+
no array of arrays, so `element` cannot be an `Array`), `Cluster(fields)` an ordered
|
|
71
|
+
mapping of field name to schema, and `LVEnum(choices, tag="EW")` an enum whose value may
|
|
72
|
+
be given as a label or an index. An empty array is written with `Dimsize` 0 and one
|
|
73
|
+
default element, so the element type stays visible. `to_node` builds the typed tree
|
|
74
|
+
instead of text.
|
|
75
|
+
|
|
76
|
+
## The typed tree: lossless read and write
|
|
77
|
+
|
|
78
|
+
`parse` keeps everything: names, the raw `<Val>` text, enum choices, `Dimsize`,
|
|
79
|
+
`NumElts`, refnum fields and the `<Version>` header. `dump` writes it back in the layout
|
|
80
|
+
LabVIEW's file writer uses (one element per line, CRLF, no indentation), so a file
|
|
81
|
+
LabVIEW wrote comes back byte for byte.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from lvxml import dump, parse, to_value
|
|
85
|
+
|
|
86
|
+
with open("Example Flattened XML File.xml", encoding="utf-8", newline="") as f:
|
|
87
|
+
text = f.read() # newline="" keeps LabVIEW's CRLF line breaks
|
|
88
|
+
|
|
89
|
+
document = parse(text)
|
|
90
|
+
document.version # '26.3.1f1'
|
|
91
|
+
node = document.values[3] # Node(tag='Cluster', name='Random Graph Data', num_elts=3, ...)
|
|
92
|
+
to_value(node) # the plain Python value
|
|
93
|
+
assert dump(document) == text
|
|
94
|
+
document.to_dict() # JSON-ready; Document.from_dict() reverses it
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`dump(tree, newline="\r\n", indent="", declaration=None)` also writes a single `Node`
|
|
98
|
+
bare, the way Flatten To XML returns it, and refuses a node the schema cannot express.
|
|
99
|
+
|
|
100
|
+
## Validate
|
|
101
|
+
|
|
102
|
+
`validate(text)` returns a list of problems, empty when the document is good. It first
|
|
103
|
+
checks the element structure `LVXMLSchema.xsd` defines, then every value: numbers that
|
|
104
|
+
parse and fit their type, enum indexes inside their choices, and `NumElts` and `Dimsize`
|
|
105
|
+
that match the elements present. Each problem names its location, such as
|
|
106
|
+
`Cluster[pt]/I8[y]: 300 is out of range for I8 (-128..127)`. Problems in the data raise
|
|
107
|
+
`LVXMLError` (a `ValueError`) with the same location in its `path` attribute. Building an
|
|
108
|
+
impossible schema object, such as `LVEnum(())`, raises `ValueError` or `TypeError`.
|
|
109
|
+
|
|
110
|
+
`parse`, `from_lv_xml`, `from_lv_xml_all` and `validate` take `str` or `bytes`; bytes are
|
|
111
|
+
decoded by the encoding the XML declaration names, or as UTF-8.
|
|
112
|
+
|
|
113
|
+
## Command line
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
lvxml to-json data.xml -o data.json # lossless tree, every name and raw value kept
|
|
117
|
+
lvxml to-json --values data.xml # plain values: numbers, strings, lists, dicts
|
|
118
|
+
lvxml to-xml data.json -o data.xml # tree JSON back to LabVIEW XML
|
|
119
|
+
lvxml validate data.xml other.xml # check shape and values, exit 1 on any problem
|
|
120
|
+
echo "<I32><Name>n</Name><Val>7</Val></I32>" | lvxml to-json --values -
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`-` reads standard input. `--encoding` sets the file encoding (default `utf-8`); if a
|
|
124
|
+
file is not UTF-8, name its encoding, for example `--encoding cp1252`.
|
|
125
|
+
`to-xml --newline lf` switches from CRLF. `--values` writes strict JSON: complex numbers
|
|
126
|
+
become `{"re": ..., "im": ...}` and non-finite floats the strings `"NaN"`, `"Inf"` and
|
|
127
|
+
`"-Inf"`. Errors go to standard error with exit status 1. `lvxml --help` lists
|
|
128
|
+
everything.
|
|
129
|
+
|
|
130
|
+
## What each rule rests on
|
|
131
|
+
|
|
132
|
+
Checked against the real LabVIEW file in the tests:
|
|
133
|
+
|
|
134
|
+
- The layout `dump` writes by default: the `<?xml version='1.0' standalone='yes' ?>`
|
|
135
|
+
declaration, `<LVData xmlns="http://www.ni.com/LVData">`, a `<Version>` header, one
|
|
136
|
+
element per line, CRLF, no indentation and no final line break.
|
|
137
|
+
- `DBL`, `Cluster` and one-dimensional `Array` values, and decimals written with a period.
|
|
138
|
+
|
|
139
|
+
Checked against the shipped XSD: the element structure of every type, including
|
|
140
|
+
multi-dimensional `Array` (repeated `<Dimsize>`), the enum `<Choice>` list and the
|
|
141
|
+
optional `Refnum` fields. The XSD declares the target namespace
|
|
142
|
+
`http://www.ni.com/labview` while the real file uses `http://www.ni.com/LVData`; the
|
|
143
|
+
reader accepts either, or none, keeps whichever it found, and requires every element to
|
|
144
|
+
share the root's namespace.
|
|
145
|
+
|
|
146
|
+
Taken from the format strings in LabVIEW 2026's run-time library (`lvrt.dll`), with no
|
|
147
|
+
real sample in the tests yet:
|
|
148
|
+
|
|
149
|
+
- complex values as `re +im i` (the writer's `<Val>%s +%s i</Val>`);
|
|
150
|
+
- only `&`, `<` and `>` escaped in text (the only entity strings beside the XML
|
|
151
|
+
writer's tags are `<`, `>` and `&`), so quotes are written as they are;
|
|
152
|
+
- integers and `Dimsize` as plain decimals (`%d`, `%lld`, `%llu`).
|
|
153
|
+
|
|
154
|
+
Choices `lvxml` makes where neither source settles the question:
|
|
155
|
+
|
|
156
|
+
- `Boolean` is written as `1` or `0`; the reader also accepts `true` and `false`.
|
|
157
|
+
- Non-finite floats are written as `NaN`, `Inf` and `-Inf` (the spellings found in the
|
|
158
|
+
run-time library) and read case-insensitively.
|
|
159
|
+
- Floats are written with the shortest text that reads back exactly. A comma decimal
|
|
160
|
+
separator raises.
|
|
161
|
+
- A carriage return inside a value is written as is and survives a read with `lvxml`,
|
|
162
|
+
where a plain XML parser would turn it into a line feed.
|
|
163
|
+
- A DOCTYPE or a CDATA section is refused, nesting deeper than 100 levels raises, and an
|
|
164
|
+
empty array whose shape would expand to more than 100000 nested lists raises.
|
|
165
|
+
|
|
166
|
+
## Test fixtures
|
|
167
|
+
|
|
168
|
+
The tests check the codec against ground truth from a LabVIEW 2026 installation:
|
|
169
|
+
|
|
170
|
+
- `LVXMLSchema.xsd` is the schema LabVIEW ships in
|
|
171
|
+
`C:\Program Files\National Instruments\LabVIEW 2026\vi.lib\Utility\LVXMLSchema.xsd`.
|
|
172
|
+
It belongs to NI, so it is not in the repository or the sdist: run
|
|
173
|
+
`python scripts/capture_ni.py` on a machine with LabVIEW to copy it into
|
|
174
|
+
`tests/fixtures/ni` (size and sha256 are checked against `tests/fixtures/ni/MANIFEST.json`).
|
|
175
|
+
Without it, the tests that validate against the schema skip.
|
|
176
|
+
- `tests/fixtures/real/example-flattened-xml-file.xml` is **real LabVIEW output**,
|
|
177
|
+
kept byte for byte: the file `Example Flattened XML File.xml` that LabVIEW 2026
|
|
178
|
+
(version header `26.3.1f1`) wrote to `Documents\LabVIEW Data` when running the shipped
|
|
179
|
+
example `examples\File IO\XML\Flatten and Unflatten XML\Flatten Data and Write to XML
|
|
180
|
+
File.vi`. It holds `DBL` values and a `Cluster` with a ten-element `Array`.
|
|
181
|
+
- `tests/fixtures/schema/*.xml` are **schema-derived**: written by hand from the XSD to
|
|
182
|
+
cover every element it defines. They are valid against the XSD, but their payloads are
|
|
183
|
+
illustrative; in particular the cluster contents of the timestamp and waveform elements
|
|
184
|
+
are not LabVIEW's actual internal layout, which the schema does not specify.
|
|
185
|
+
|
|
186
|
+
The real file and every schema-derived fixture are validated against the XSD with `lxml`
|
|
187
|
+
(a test-only dependency) when the schema is present, parsed, decoded, and written back: the real file byte for byte,
|
|
188
|
+
the others canonically equal. Samples that the XSD rejects are rejected by the reader as
|
|
189
|
+
well. An end-to-end test installs the package and drives the installed `lvxml` command
|
|
190
|
+
on the real file.
|
|
191
|
+
|
|
192
|
+
## Not covered
|
|
193
|
+
|
|
194
|
+
Fixed-point `FXP` numbers and the `<Default>` and `<Attributes>` children of a variant
|
|
195
|
+
appear among the run-time library's writer strings but are absent from the shipped
|
|
196
|
+
schema, so the reader raises a clear error for them. The
|
|
197
|
+
timestamp and waveform elements are read as the schema describes them (a name and
|
|
198
|
+
clusters), not interpreted further.
|
|
199
|
+
|
|
200
|
+
## Why
|
|
201
|
+
|
|
202
|
+
LabVIEW's Flatten To XML and Unflatten From XML use one fixed schema
|
|
203
|
+
(`LVXMLSchema.xsd`): datatype-named elements, `Name`-ordered fields, a period decimal
|
|
204
|
+
separator independent of locale. LabVIEW produces and reads this schema natively, and NI's
|
|
205
|
+
community GXML library also converts data to and from XML, but both run inside LabVIEW and
|
|
206
|
+
ship no Python reader. On the Python side, with no LabVIEW running, a generic XML library
|
|
207
|
+
parses the tree but knows nothing of that convention, so turning it into usable Python, or
|
|
208
|
+
producing XML LabVIEW will accept, is left to hand-written glue every time. Existing LabVIEW
|
|
209
|
+
Python packages (pylabview, lvkit, autolv) parse VI source files, not this flattened data
|
|
210
|
+
format, and no package on PyPI covers it. `lvxml` is a small, typed, zero-dependency codec
|
|
211
|
+
for it.
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
MIT. See [LICENSE](LICENSE).
|
|
216
|
+
|
|
217
|
+
## Links
|
|
218
|
+
|
|
219
|
+
- Website: https://lianes8studio.com
|
|
220
|
+
- GitHub: https://github.com/JaviLianes8
|
|
221
|
+
- LinkedIn: https://www.linkedin.com/in/jlianes/
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lvxml"
|
|
7
|
+
version = "0.2.1"
|
|
8
|
+
description = "Codec for LabVIEW Flatten To XML / Unflatten From XML between its fixed schema and native Python values"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Javier Lianes", email = "javier@lianes8studio.com" }]
|
|
14
|
+
keywords = ["labview", "xml", "codec", "flatten", "unflatten", "ni"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"Topic :: Scientific/Engineering",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Typing :: Typed",
|
|
23
|
+
]
|
|
24
|
+
dependencies = []
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
# lxml validates fixtures against LabVIEW's XSD in the tests; hatchling lets the end-to-end
|
|
28
|
+
# test install the package in editable mode without network access (with editables).
|
|
29
|
+
dev = ["ruff", "mypy", "pytest", "build", "twine", "lxml", "hatchling", "editables"]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
lvxml = "lvxml._cli:main"
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://lianes8studio.com"
|
|
36
|
+
Repository = "https://github.com/JaviLianes8/lvxml"
|
|
37
|
+
Issues = "https://github.com/JaviLianes8/lvxml/issues"
|
|
38
|
+
Changelog = "https://github.com/JaviLianes8/lvxml/blob/main/CHANGELOG.md"
|
|
39
|
+
LinkedIn = "https://www.linkedin.com/in/jlianes/"
|
|
40
|
+
Funding = "https://buymeacoffee.com/jlianesglrs"
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["src/lvxml"]
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.sdist]
|
|
46
|
+
only-include = [
|
|
47
|
+
"src/lvxml",
|
|
48
|
+
"tests",
|
|
49
|
+
"scripts/capture_ni.py",
|
|
50
|
+
"README.md",
|
|
51
|
+
"CHANGELOG.md",
|
|
52
|
+
"CONTRIBUTING.md",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"pyproject.toml",
|
|
55
|
+
]
|
|
56
|
+
# NI's schema is a local test reference only: it is never distributed, only its manifest is.
|
|
57
|
+
exclude = ["**/__pycache__", "**/*.pyc", "tests/fixtures/ni/*.xsd"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff]
|
|
60
|
+
line-length = 100
|
|
61
|
+
target-version = "py310"
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint]
|
|
64
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
|
|
65
|
+
|
|
66
|
+
[tool.mypy]
|
|
67
|
+
python_version = "3.10"
|
|
68
|
+
strict = true
|
|
69
|
+
|
|
70
|
+
[tool.pytest.ini_options]
|
|
71
|
+
testpaths = ["tests"]
|
|
72
|
+
pythonpath = ["src"]
|
|
73
|
+
addopts = "-q"
|