lvpath 0.3.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.
- lvpath-0.3.1/.gitignore +12 -0
- lvpath-0.3.1/CHANGELOG.md +58 -0
- lvpath-0.3.1/CONTRIBUTING.md +19 -0
- lvpath-0.3.1/LICENSE +21 -0
- lvpath-0.3.1/PKG-INFO +271 -0
- lvpath-0.3.1/README.md +244 -0
- lvpath-0.3.1/pyproject.toml +66 -0
- lvpath-0.3.1/src/lvpath/__init__.py +48 -0
- lvpath-0.3.1/src/lvpath/_cli.py +134 -0
- lvpath-0.3.1/src/lvpath/_convert.py +48 -0
- lvpath-0.3.1/src/lvpath/_flatbridge.py +69 -0
- lvpath-0.3.1/src/lvpath/_flatten.py +139 -0
- lvpath-0.3.1/src/lvpath/_kinds.py +21 -0
- lvpath-0.3.1/src/lvpath/_platform.py +36 -0
- lvpath-0.3.1/src/lvpath/_portable.py +139 -0
- lvpath-0.3.1/src/lvpath/py.typed +0 -0
- lvpath-0.3.1/tests/fixtures/labview2026_paths.json +124 -0
- lvpath-0.3.1/tests/test_cli.py +92 -0
- lvpath-0.3.1/tests/test_cli_e2e.py +184 -0
- lvpath-0.3.1/tests/test_contract.py +44 -0
- lvpath-0.3.1/tests/test_convert.py +70 -0
- lvpath-0.3.1/tests/test_flatten.py +125 -0
- lvpath-0.3.1/tests/test_flatten_contract.py +43 -0
- lvpath-0.3.1/tests/test_portable.py +194 -0
- lvpath-0.3.1/tests/test_portable_contract.py +137 -0
lvpath-0.3.1/.gitignore
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
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.3.1]
|
|
10
|
+
|
|
11
|
+
- The LabVIEW 2026 path fixture no longer carries source lines copied from NI files: each
|
|
12
|
+
row keeps only the path value, its expected renderings, and where it was found (file and
|
|
13
|
+
setting). The sdist is limited to the package, the tests and the docs.
|
|
14
|
+
|
|
15
|
+
## [0.3.0]
|
|
16
|
+
|
|
17
|
+
- Added the portable path form LabVIEW writes into configuration and project
|
|
18
|
+
files. `from_portable_path` reads `/C/DIR1/DIR2` as `C:\DIR1\DIR2`,
|
|
19
|
+
`//server/share/dir` as a UNC path and `../a/b` as a relative path;
|
|
20
|
+
`to_portable_path` writes them back, keeping the drive letter case. The empty value is `<Not A Path>`.
|
|
21
|
+
- `PortablePathError` reports a value with no faithful counterpart instead of
|
|
22
|
+
rewriting it into another path: a POSIX absolute path read as Windows, a
|
|
23
|
+
drive-relative `C:file`, a rooted Windows path without a drive, a UNC path
|
|
24
|
+
without a share and a `\\?\` device path.
|
|
25
|
+
- The `lvpath` command line gains `to-native` and `to-labview`, and its help lists
|
|
26
|
+
runnable examples for every subcommand.
|
|
27
|
+
- The test suite carries a verbatim excerpt of path values from files shipped with
|
|
28
|
+
LabVIEW 2026 and checks every value in both directions and through a round trip.
|
|
29
|
+
- The README no longer implies that `to_os_path` translates `/C/foo`; it documents
|
|
30
|
+
the portable form, its rules and its one ambiguity.
|
|
31
|
+
|
|
32
|
+
## [0.2.0]
|
|
33
|
+
|
|
34
|
+
- Added the binary flattened path form (`Flatten To String` `PTH0`). `FlatPath` is
|
|
35
|
+
a typed, frozen result with `to_bytes` and `from_bytes`; `parse_flat_path` and
|
|
36
|
+
`build_flat_path` are the codec entry points. The NI `C:\File` example round-trips
|
|
37
|
+
byte for byte in both directions.
|
|
38
|
+
- `to_flat_path` and `from_flat_path` bridge the flattened form to and from
|
|
39
|
+
`pathlib.PurePath` on a chosen platform flavor.
|
|
40
|
+
- `FlatPathType` exposes the two documented path type codes (absolute `0`, relative
|
|
41
|
+
`1`). UNC and not-a-path codes are undocumented, so they are not guessed: the
|
|
42
|
+
codec still round-trips their raw bytes, while the PurePath bridge refuses an
|
|
43
|
+
un-interpretable type code with `FlatPathError`.
|
|
44
|
+
- Added an `lvpath` command line with `decode` and `encode` subcommands and
|
|
45
|
+
`--version`.
|
|
46
|
+
|
|
47
|
+
## [0.1.0]
|
|
48
|
+
|
|
49
|
+
- First release.
|
|
50
|
+
- `to_os_path` parses a LabVIEW path string into a `PurePath` of the Windows or
|
|
51
|
+
POSIX flavor; `to_lv_path` serializes an OS path back into a LabVIEW string.
|
|
52
|
+
- `path_kind` and `PathKind` detect absolute, relative and not-a-path values;
|
|
53
|
+
the empty string is the not-a-path value on every platform.
|
|
54
|
+
- `NotAPathError` is raised when the not-a-path value is converted to an OS path.
|
|
55
|
+
- Covers the textual `Path To String` / `String To Path` conventions: Windows
|
|
56
|
+
drive and UNC anchors, POSIX roots, lenient forward slashes, and clean
|
|
57
|
+
cross-platform conversion of relative path objects. Classic Mac OS colon paths
|
|
58
|
+
and the binary flatten path type are out of scope.
|
|
@@ -0,0 +1,19 @@
|
|
|
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/lvpath/__init__.py`; internals live in `_`-prefixed modules.
|
|
14
|
+
- Add a test for the behaviour you change. Tests inject time, disk and network; they
|
|
15
|
+
never touch the real thing.
|
|
16
|
+
- Run `scripts/check.ps1` (format, lint, types, tests) and make sure it passes.
|
|
17
|
+
- One logical change per commit, imperative subject line.
|
|
18
|
+
|
|
19
|
+
Open an issue first if the change is large or shifts the public API.
|
lvpath-0.3.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.
|
lvpath-0.3.1/PKG-INFO
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lvpath
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Convert LabVIEW path strings, the portable /C/DIR form of config and project files, and the flattened PTH0 form to and from native paths.
|
|
5
|
+
Project-URL: Homepage, https://lianes8studio.com
|
|
6
|
+
Project-URL: Repository, https://github.com/JaviLianes8/lvpath
|
|
7
|
+
Project-URL: Issues, https://github.com/JaviLianes8/lvpath/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/JaviLianes8/lvpath/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: flatten,ini,labview,lvproj,ni,path,pathlib
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: build; extra == 'dev'
|
|
22
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
25
|
+
Requires-Dist: twine; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# lvpath
|
|
29
|
+
|
|
30
|
+
Convert between LabVIEW path strings and native OS paths: the host text form, the
|
|
31
|
+
portable form LabVIEW writes into config and project files (`/C/DIR1/DIR2`), and the
|
|
32
|
+
flattened `PTH0` binary form, with absolute, relative and not-a-path detection.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
pip install lvpath
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from lvpath import to_os_path, to_lv_path, path_kind, PathKind, NotAPathError
|
|
44
|
+
|
|
45
|
+
# Parse a LabVIEW path string into a PurePath of the chosen platform flavor.
|
|
46
|
+
to_os_path("C:\\Users\\test\\data.txt", platform="windows")
|
|
47
|
+
# PureWindowsPath('C:/Users/test/data.txt')
|
|
48
|
+
|
|
49
|
+
to_os_path("/usr/local/bin", platform="posix")
|
|
50
|
+
# PurePosixPath('/usr/local/bin')
|
|
51
|
+
|
|
52
|
+
# Serialize an OS path back into a LabVIEW path string.
|
|
53
|
+
to_lv_path("logs/run.txt", platform="windows")
|
|
54
|
+
# 'logs\\run.txt'
|
|
55
|
+
|
|
56
|
+
# A relative path object crosses platforms cleanly.
|
|
57
|
+
win = to_os_path("reports\\q1\\data.txt", platform="windows")
|
|
58
|
+
to_lv_path(win, platform="posix")
|
|
59
|
+
# 'reports/q1/data.txt'
|
|
60
|
+
|
|
61
|
+
# Detect the kind. The empty string is LabVIEW's <Not A Path> on every platform.
|
|
62
|
+
path_kind("", platform="posix") # PathKind.NOT_A_PATH
|
|
63
|
+
path_kind("sub\\file", platform="windows") # PathKind.RELATIVE
|
|
64
|
+
path_kind("C:\\x", platform="windows") # PathKind.ABSOLUTE
|
|
65
|
+
|
|
66
|
+
# Not-a-path has no OS equivalent, so to_os_path refuses it.
|
|
67
|
+
try:
|
|
68
|
+
to_os_path("", platform="posix")
|
|
69
|
+
except NotAPathError:
|
|
70
|
+
...
|
|
71
|
+
|
|
72
|
+
# None (or "") serializes back to the not-a-path value.
|
|
73
|
+
to_lv_path(None, platform="windows") # ''
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`platform` accepts `"windows"`, `"posix"`, a `Platform` member, or `None` to use
|
|
77
|
+
the convention of the operating system running the code.
|
|
78
|
+
|
|
79
|
+
## Portable form (config and project files)
|
|
80
|
+
|
|
81
|
+
When LabVIEW writes a path value into a configuration (`.ini`) file or a project
|
|
82
|
+
(`.lvproj`) file, it does not use the native Windows text. It uses a
|
|
83
|
+
platform-independent form: the drive letter becomes a leading component, every
|
|
84
|
+
separator is a forward slash, a UNC share keeps a leading double slash and the empty
|
|
85
|
+
string is `<Not A Path>`. `C:\DIR1\DIR2` is stored as `/C/DIR1/DIR2`.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from lvpath import from_portable_path, to_portable_path, PortablePathError
|
|
89
|
+
|
|
90
|
+
# Read a stored value into a native path.
|
|
91
|
+
from_portable_path("/C/Program Files (x86)/Arduino", platform="windows")
|
|
92
|
+
# PureWindowsPath('C:/Program Files (x86)/Arduino')
|
|
93
|
+
from_portable_path("//server/share/dir", platform="windows")
|
|
94
|
+
# PureWindowsPath('//server/share/dir')
|
|
95
|
+
from_portable_path("../builds/Main Application", platform="windows")
|
|
96
|
+
# PureWindowsPath('../builds/Main Application')
|
|
97
|
+
from_portable_path("/home/lvuser/natinst/bin", platform="posix")
|
|
98
|
+
# PurePosixPath('/home/lvuser/natinst/bin')
|
|
99
|
+
|
|
100
|
+
# Write a native path back in the stored form.
|
|
101
|
+
to_portable_path("C:\\DIR1\\DIR2", platform="windows") # '/C/DIR1/DIR2'
|
|
102
|
+
to_portable_path("D:\\", platform="windows") # '/D'
|
|
103
|
+
to_portable_path("\\\\server\\share\\dir", platform="windows") # '//server/share/dir'
|
|
104
|
+
to_portable_path(None) # '' (<Not A Path>)
|
|
105
|
+
|
|
106
|
+
# Values with no faithful counterpart are refused, never rewritten into another path.
|
|
107
|
+
try:
|
|
108
|
+
from_portable_path("/home/lvuser", platform="windows")
|
|
109
|
+
except PortablePathError:
|
|
110
|
+
...
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The rules below are checked against path values in files shipped with LabVIEW 2026
|
|
114
|
+
(`tests/fixtures/labview2026_paths.json` records each value with the file and setting it
|
|
115
|
+
was found in; the NI files themselves are not copied).
|
|
116
|
+
No shipped file holds a bare drive or a UNC value, so the `/D` and `//server/share/dir`
|
|
117
|
+
rows are covered by synthetic tests that follow the same convention:
|
|
118
|
+
|
|
119
|
+
| Stored value | Windows | POSIX |
|
|
120
|
+
| -------------------------------- | ------------------------------ | -------------------------------- |
|
|
121
|
+
| `/C/Program Files (x86)/Arduino` | `C:\Program Files (x86)\Arduino` | `/C/Program Files (x86)/Arduino` |
|
|
122
|
+
| `/c/ni-rt/startup` | `c:\ni-rt\startup` | `/c/ni-rt/startup` |
|
|
123
|
+
| `/D` | `D:\` | `/D` |
|
|
124
|
+
| `//server/share/dir` | `\\server\share\dir` | `//server/share/dir` |
|
|
125
|
+
| `../builds/Main Application` | `..\builds\Main Application` | `../builds/Main Application` |
|
|
126
|
+
| `/home/lvuser/natinst/bin` | `PortablePathError` | `/home/lvuser/natinst/bin` |
|
|
127
|
+
| empty | `NotAPathError` | `NotAPathError` |
|
|
128
|
+
|
|
129
|
+
- The drive letter keeps its case, so a value in normal form (no trailing slash,
|
|
130
|
+
repeated separator or `.` component, as LabVIEW writes it) round-trips exactly.
|
|
131
|
+
- A value already in native Windows form (`C:\NI Installers`), which some NI files
|
|
132
|
+
store, is accepted as is when reading on Windows.
|
|
133
|
+
- On POSIX the stored form is already native and is parsed unchanged. A one-letter
|
|
134
|
+
first directory is therefore ambiguous: `/C/x` reads as the drive `C:` on Windows
|
|
135
|
+
and as the directory `/C/x` on POSIX. Pick `platform` for the system that will
|
|
136
|
+
use the path.
|
|
137
|
+
- `PortablePathError` is raised for a POSIX absolute path read as Windows, a
|
|
138
|
+
drive-relative `C:file`, a rooted Windows path without a drive (`\dir`), a UNC
|
|
139
|
+
path without a share and a `\\?\` device path.
|
|
140
|
+
- A `str` is parsed with the flavor of `platform`; a `PurePath` keeps its own
|
|
141
|
+
flavor, and only a relative one may be carried to the other flavor.
|
|
142
|
+
- Text is normalised by `pathlib`: repeated separators collapse, a trailing slash and
|
|
143
|
+
`.` components are dropped, so `/C/x/` writes back as `/C/x`. A backslash inside a
|
|
144
|
+
value read on Windows is a separator (`/C/x\y` reads as `C:\x\y`).
|
|
145
|
+
- The sibling `lvconfig` library (0.3.0 and later) gives the same results on drive,
|
|
146
|
+
UNC and relative values in normal form, and refuses the same values lvpath refuses
|
|
147
|
+
(`C:file`, `D:`, `\dir`, `//server`, a POSIX absolute path on Windows). The one
|
|
148
|
+
difference left: lvpath normalises text through `pathlib`, where lvconfig only
|
|
149
|
+
swaps separators.
|
|
150
|
+
|
|
151
|
+
## Flattened form
|
|
152
|
+
|
|
153
|
+
`Flatten To String` renders a LabVIEW Path as a small big-endian binary
|
|
154
|
+
container: the ASCII signature `PTH0`, a `u32` total length, a `u16` path type, a
|
|
155
|
+
`u16` component count and then one length-prefixed (Pascal) string per component.
|
|
156
|
+
NI documents the absolute path `C:\File` flattening to the bytes
|
|
157
|
+
`50 54 48 30 00 00 00 0B 00 00 00 02 01 43 04 66 69 6C 65`.
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from lvpath import FlatPath, parse_flat_path, from_flat_path, to_flat_path
|
|
161
|
+
|
|
162
|
+
golden = bytes.fromhex("505448300000000b0000000201430466696c65")
|
|
163
|
+
|
|
164
|
+
# Parse the flattened bytes into a typed result.
|
|
165
|
+
parse_flat_path(golden)
|
|
166
|
+
# FlatPath(path_type=0, components=('C', 'file'))
|
|
167
|
+
|
|
168
|
+
# Round-trips byte for byte.
|
|
169
|
+
parse_flat_path(golden).to_bytes() == golden # True
|
|
170
|
+
|
|
171
|
+
# Bridge to and from pathlib on a chosen platform flavor.
|
|
172
|
+
from_flat_path(golden, platform="windows") # PureWindowsPath('C:/file')
|
|
173
|
+
to_flat_path("C:/file", platform="windows") # the golden bytes
|
|
174
|
+
to_flat_path("/usr/local/bin", platform="posix") # a POSIX absolute flattened path
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Only the two documented path type codes are interpreted: `0` absolute and `1`
|
|
178
|
+
relative, exposed as `FlatPathType`. UNC and empty / not-a-path forms carry type
|
|
179
|
+
codes the public NI documentation does not state, so they are not guessed: the
|
|
180
|
+
low-level codec still round-trips their raw bytes faithfully through `FlatPath`,
|
|
181
|
+
while `to_flat_path` and `from_flat_path` refuse an un-interpretable type code
|
|
182
|
+
with a clear `FlatPathError` instead of inventing a meaning.
|
|
183
|
+
|
|
184
|
+
## Command line
|
|
185
|
+
|
|
186
|
+
Installing the package provides an `lvpath` command for the portable string form and
|
|
187
|
+
the flattened form.
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
# A stored LabVIEW path string to a native path, and back.
|
|
191
|
+
lvpath to-native --platform windows "/C/Program Files (x86)/Arduino"
|
|
192
|
+
# C:\Program Files (x86)\Arduino
|
|
193
|
+
lvpath to-labview --platform windows "C:\DIR1\DIR2"
|
|
194
|
+
# /C/DIR1/DIR2
|
|
195
|
+
|
|
196
|
+
# Decode the NI C:\File flattened example into a path.
|
|
197
|
+
lvpath decode --platform windows 505448300000000b0000000201430466696c65
|
|
198
|
+
# C:\file
|
|
199
|
+
|
|
200
|
+
# Encode a path into its flattened hex form.
|
|
201
|
+
lvpath encode --platform windows "C:\file"
|
|
202
|
+
# 505448300000000b0000000201430466696c65
|
|
203
|
+
|
|
204
|
+
lvpath --version
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`lvpath decode` accepts hex with or without spaces. `lvpath to-labview ""` prints an
|
|
208
|
+
empty line, the `<Not A Path>` value, while `lvpath to-native ""` is an error. Errors
|
|
209
|
+
are written to standard error and the command exits with status 1. Output is UTF-8.
|
|
210
|
+
A value that starts with `-` goes after `--`. Under Git Bash on Windows, set
|
|
211
|
+
`MSYS_NO_PATHCONV=1` so the shell passes POSIX-looking arguments such as `/C/x`
|
|
212
|
+
unchanged. Run `lvpath --help` for the full usage.
|
|
213
|
+
|
|
214
|
+
## Covered conventions
|
|
215
|
+
|
|
216
|
+
LabVIEW's `Path To String` and `String To Path` render and parse a path in the
|
|
217
|
+
standard textual form of a host platform. `to_os_path`, `to_lv_path` and `path_kind`
|
|
218
|
+
model that native textual form (so `to_os_path("/C/foo", platform="windows")` is the
|
|
219
|
+
rooted Windows path `\C\foo`; read stored values with `from_portable_path` instead):
|
|
220
|
+
|
|
221
|
+
- **Separators:** `\` on Windows, `/` on POSIX. Forward slashes are also accepted
|
|
222
|
+
when parsing a Windows path, matching LabVIEW's lenient reader.
|
|
223
|
+
- **Absolute, Windows:** drive-letter paths (`C:\dir\file`) and UNC paths
|
|
224
|
+
(`\\server\share\dir`).
|
|
225
|
+
- **Absolute, POSIX:** a leading `/` (modern LabVIEW on macOS and Linux).
|
|
226
|
+
- **Relative:** any other non-empty string, including up-level (`..\up`) and
|
|
227
|
+
drive-relative (`C:file`) forms.
|
|
228
|
+
- **Not-a-path:** the empty string, LabVIEW's `<Not A Path>` value, on every
|
|
229
|
+
platform. `None` serializes back to it.
|
|
230
|
+
|
|
231
|
+
### What it does not do
|
|
232
|
+
|
|
233
|
+
- It does not touch the filesystem, resolve symbolic links, or normalise `.`/`..`
|
|
234
|
+
beyond what `pathlib` does.
|
|
235
|
+
- It does not handle classic Mac OS colon-separated paths (pre-OS X). Modern
|
|
236
|
+
LabVIEW on macOS uses POSIX `/`.
|
|
237
|
+
- The flattened codec interprets only the documented absolute and relative path
|
|
238
|
+
type codes. It does not guess the undocumented UNC or not-a-path type codes, and
|
|
239
|
+
it does not touch the textual-form behaviour above.
|
|
240
|
+
- It does not expand LabVIEW symbolic path prefixes such as `<vilib>` or
|
|
241
|
+
`<instrlib>`, and it does not resolve a relative stored value against the file
|
|
242
|
+
it came from.
|
|
243
|
+
|
|
244
|
+
## Why
|
|
245
|
+
|
|
246
|
+
LabVIEW uses two text forms for a path: the native form of its host platform for
|
|
247
|
+
`Path To String` / `String To Path`, and a platform-independent form (`/C/DIR1/DIR2`
|
|
248
|
+
for `C:\DIR1\DIR2`) when it writes a path into configuration and project files. Read
|
|
249
|
+
with plain string handling, the stored form names the wrong file on Windows, and a
|
|
250
|
+
native string only means something together with the platform convention it was
|
|
251
|
+
written in, so round-tripping LabVIEW path data in Python needs explicit,
|
|
252
|
+
platform-aware handling.
|
|
253
|
+
|
|
254
|
+
The standard library already does most of this. `pathlib`'s `PureWindowsPath` and
|
|
255
|
+
`PurePosixPath` are built to manipulate foreign-platform paths with no I/O, so they
|
|
256
|
+
cover the bulk of the separator and anchor handling on their own, and this library is
|
|
257
|
+
a thin wrapper over them rather than a reimplementation. What they do not know is the
|
|
258
|
+
genuinely LabVIEW-specific residue: the stored drive and UNC form (`/C/foo` to
|
|
259
|
+
`C:\foo` and back, through `from_portable_path` and `to_portable_path`), the empty
|
|
260
|
+
string standing for `<Not A Path>`, and LabVIEW's exact serialization rules. No PyPI package targets those conventions. `lvpath` adds exactly
|
|
261
|
+
that layer on top of `pathlib`, with a small, typed, zero-dependency surface.
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT. See [LICENSE](LICENSE).
|
|
266
|
+
|
|
267
|
+
## Links
|
|
268
|
+
|
|
269
|
+
- Website: https://lianes8studio.com
|
|
270
|
+
- GitHub: https://github.com/JaviLianes8
|
|
271
|
+
- LinkedIn: https://www.linkedin.com/in/jlianes/
|
lvpath-0.3.1/README.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# lvpath
|
|
2
|
+
|
|
3
|
+
Convert between LabVIEW path strings and native OS paths: the host text form, the
|
|
4
|
+
portable form LabVIEW writes into config and project files (`/C/DIR1/DIR2`), and the
|
|
5
|
+
flattened `PTH0` binary form, with absolute, relative and not-a-path detection.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
pip install lvpath
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from lvpath import to_os_path, to_lv_path, path_kind, PathKind, NotAPathError
|
|
17
|
+
|
|
18
|
+
# Parse a LabVIEW path string into a PurePath of the chosen platform flavor.
|
|
19
|
+
to_os_path("C:\\Users\\test\\data.txt", platform="windows")
|
|
20
|
+
# PureWindowsPath('C:/Users/test/data.txt')
|
|
21
|
+
|
|
22
|
+
to_os_path("/usr/local/bin", platform="posix")
|
|
23
|
+
# PurePosixPath('/usr/local/bin')
|
|
24
|
+
|
|
25
|
+
# Serialize an OS path back into a LabVIEW path string.
|
|
26
|
+
to_lv_path("logs/run.txt", platform="windows")
|
|
27
|
+
# 'logs\\run.txt'
|
|
28
|
+
|
|
29
|
+
# A relative path object crosses platforms cleanly.
|
|
30
|
+
win = to_os_path("reports\\q1\\data.txt", platform="windows")
|
|
31
|
+
to_lv_path(win, platform="posix")
|
|
32
|
+
# 'reports/q1/data.txt'
|
|
33
|
+
|
|
34
|
+
# Detect the kind. The empty string is LabVIEW's <Not A Path> on every platform.
|
|
35
|
+
path_kind("", platform="posix") # PathKind.NOT_A_PATH
|
|
36
|
+
path_kind("sub\\file", platform="windows") # PathKind.RELATIVE
|
|
37
|
+
path_kind("C:\\x", platform="windows") # PathKind.ABSOLUTE
|
|
38
|
+
|
|
39
|
+
# Not-a-path has no OS equivalent, so to_os_path refuses it.
|
|
40
|
+
try:
|
|
41
|
+
to_os_path("", platform="posix")
|
|
42
|
+
except NotAPathError:
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
# None (or "") serializes back to the not-a-path value.
|
|
46
|
+
to_lv_path(None, platform="windows") # ''
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`platform` accepts `"windows"`, `"posix"`, a `Platform` member, or `None` to use
|
|
50
|
+
the convention of the operating system running the code.
|
|
51
|
+
|
|
52
|
+
## Portable form (config and project files)
|
|
53
|
+
|
|
54
|
+
When LabVIEW writes a path value into a configuration (`.ini`) file or a project
|
|
55
|
+
(`.lvproj`) file, it does not use the native Windows text. It uses a
|
|
56
|
+
platform-independent form: the drive letter becomes a leading component, every
|
|
57
|
+
separator is a forward slash, a UNC share keeps a leading double slash and the empty
|
|
58
|
+
string is `<Not A Path>`. `C:\DIR1\DIR2` is stored as `/C/DIR1/DIR2`.
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from lvpath import from_portable_path, to_portable_path, PortablePathError
|
|
62
|
+
|
|
63
|
+
# Read a stored value into a native path.
|
|
64
|
+
from_portable_path("/C/Program Files (x86)/Arduino", platform="windows")
|
|
65
|
+
# PureWindowsPath('C:/Program Files (x86)/Arduino')
|
|
66
|
+
from_portable_path("//server/share/dir", platform="windows")
|
|
67
|
+
# PureWindowsPath('//server/share/dir')
|
|
68
|
+
from_portable_path("../builds/Main Application", platform="windows")
|
|
69
|
+
# PureWindowsPath('../builds/Main Application')
|
|
70
|
+
from_portable_path("/home/lvuser/natinst/bin", platform="posix")
|
|
71
|
+
# PurePosixPath('/home/lvuser/natinst/bin')
|
|
72
|
+
|
|
73
|
+
# Write a native path back in the stored form.
|
|
74
|
+
to_portable_path("C:\\DIR1\\DIR2", platform="windows") # '/C/DIR1/DIR2'
|
|
75
|
+
to_portable_path("D:\\", platform="windows") # '/D'
|
|
76
|
+
to_portable_path("\\\\server\\share\\dir", platform="windows") # '//server/share/dir'
|
|
77
|
+
to_portable_path(None) # '' (<Not A Path>)
|
|
78
|
+
|
|
79
|
+
# Values with no faithful counterpart are refused, never rewritten into another path.
|
|
80
|
+
try:
|
|
81
|
+
from_portable_path("/home/lvuser", platform="windows")
|
|
82
|
+
except PortablePathError:
|
|
83
|
+
...
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The rules below are checked against path values in files shipped with LabVIEW 2026
|
|
87
|
+
(`tests/fixtures/labview2026_paths.json` records each value with the file and setting it
|
|
88
|
+
was found in; the NI files themselves are not copied).
|
|
89
|
+
No shipped file holds a bare drive or a UNC value, so the `/D` and `//server/share/dir`
|
|
90
|
+
rows are covered by synthetic tests that follow the same convention:
|
|
91
|
+
|
|
92
|
+
| Stored value | Windows | POSIX |
|
|
93
|
+
| -------------------------------- | ------------------------------ | -------------------------------- |
|
|
94
|
+
| `/C/Program Files (x86)/Arduino` | `C:\Program Files (x86)\Arduino` | `/C/Program Files (x86)/Arduino` |
|
|
95
|
+
| `/c/ni-rt/startup` | `c:\ni-rt\startup` | `/c/ni-rt/startup` |
|
|
96
|
+
| `/D` | `D:\` | `/D` |
|
|
97
|
+
| `//server/share/dir` | `\\server\share\dir` | `//server/share/dir` |
|
|
98
|
+
| `../builds/Main Application` | `..\builds\Main Application` | `../builds/Main Application` |
|
|
99
|
+
| `/home/lvuser/natinst/bin` | `PortablePathError` | `/home/lvuser/natinst/bin` |
|
|
100
|
+
| empty | `NotAPathError` | `NotAPathError` |
|
|
101
|
+
|
|
102
|
+
- The drive letter keeps its case, so a value in normal form (no trailing slash,
|
|
103
|
+
repeated separator or `.` component, as LabVIEW writes it) round-trips exactly.
|
|
104
|
+
- A value already in native Windows form (`C:\NI Installers`), which some NI files
|
|
105
|
+
store, is accepted as is when reading on Windows.
|
|
106
|
+
- On POSIX the stored form is already native and is parsed unchanged. A one-letter
|
|
107
|
+
first directory is therefore ambiguous: `/C/x` reads as the drive `C:` on Windows
|
|
108
|
+
and as the directory `/C/x` on POSIX. Pick `platform` for the system that will
|
|
109
|
+
use the path.
|
|
110
|
+
- `PortablePathError` is raised for a POSIX absolute path read as Windows, a
|
|
111
|
+
drive-relative `C:file`, a rooted Windows path without a drive (`\dir`), a UNC
|
|
112
|
+
path without a share and a `\\?\` device path.
|
|
113
|
+
- A `str` is parsed with the flavor of `platform`; a `PurePath` keeps its own
|
|
114
|
+
flavor, and only a relative one may be carried to the other flavor.
|
|
115
|
+
- Text is normalised by `pathlib`: repeated separators collapse, a trailing slash and
|
|
116
|
+
`.` components are dropped, so `/C/x/` writes back as `/C/x`. A backslash inside a
|
|
117
|
+
value read on Windows is a separator (`/C/x\y` reads as `C:\x\y`).
|
|
118
|
+
- The sibling `lvconfig` library (0.3.0 and later) gives the same results on drive,
|
|
119
|
+
UNC and relative values in normal form, and refuses the same values lvpath refuses
|
|
120
|
+
(`C:file`, `D:`, `\dir`, `//server`, a POSIX absolute path on Windows). The one
|
|
121
|
+
difference left: lvpath normalises text through `pathlib`, where lvconfig only
|
|
122
|
+
swaps separators.
|
|
123
|
+
|
|
124
|
+
## Flattened form
|
|
125
|
+
|
|
126
|
+
`Flatten To String` renders a LabVIEW Path as a small big-endian binary
|
|
127
|
+
container: the ASCII signature `PTH0`, a `u32` total length, a `u16` path type, a
|
|
128
|
+
`u16` component count and then one length-prefixed (Pascal) string per component.
|
|
129
|
+
NI documents the absolute path `C:\File` flattening to the bytes
|
|
130
|
+
`50 54 48 30 00 00 00 0B 00 00 00 02 01 43 04 66 69 6C 65`.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from lvpath import FlatPath, parse_flat_path, from_flat_path, to_flat_path
|
|
134
|
+
|
|
135
|
+
golden = bytes.fromhex("505448300000000b0000000201430466696c65")
|
|
136
|
+
|
|
137
|
+
# Parse the flattened bytes into a typed result.
|
|
138
|
+
parse_flat_path(golden)
|
|
139
|
+
# FlatPath(path_type=0, components=('C', 'file'))
|
|
140
|
+
|
|
141
|
+
# Round-trips byte for byte.
|
|
142
|
+
parse_flat_path(golden).to_bytes() == golden # True
|
|
143
|
+
|
|
144
|
+
# Bridge to and from pathlib on a chosen platform flavor.
|
|
145
|
+
from_flat_path(golden, platform="windows") # PureWindowsPath('C:/file')
|
|
146
|
+
to_flat_path("C:/file", platform="windows") # the golden bytes
|
|
147
|
+
to_flat_path("/usr/local/bin", platform="posix") # a POSIX absolute flattened path
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Only the two documented path type codes are interpreted: `0` absolute and `1`
|
|
151
|
+
relative, exposed as `FlatPathType`. UNC and empty / not-a-path forms carry type
|
|
152
|
+
codes the public NI documentation does not state, so they are not guessed: the
|
|
153
|
+
low-level codec still round-trips their raw bytes faithfully through `FlatPath`,
|
|
154
|
+
while `to_flat_path` and `from_flat_path` refuse an un-interpretable type code
|
|
155
|
+
with a clear `FlatPathError` instead of inventing a meaning.
|
|
156
|
+
|
|
157
|
+
## Command line
|
|
158
|
+
|
|
159
|
+
Installing the package provides an `lvpath` command for the portable string form and
|
|
160
|
+
the flattened form.
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
# A stored LabVIEW path string to a native path, and back.
|
|
164
|
+
lvpath to-native --platform windows "/C/Program Files (x86)/Arduino"
|
|
165
|
+
# C:\Program Files (x86)\Arduino
|
|
166
|
+
lvpath to-labview --platform windows "C:\DIR1\DIR2"
|
|
167
|
+
# /C/DIR1/DIR2
|
|
168
|
+
|
|
169
|
+
# Decode the NI C:\File flattened example into a path.
|
|
170
|
+
lvpath decode --platform windows 505448300000000b0000000201430466696c65
|
|
171
|
+
# C:\file
|
|
172
|
+
|
|
173
|
+
# Encode a path into its flattened hex form.
|
|
174
|
+
lvpath encode --platform windows "C:\file"
|
|
175
|
+
# 505448300000000b0000000201430466696c65
|
|
176
|
+
|
|
177
|
+
lvpath --version
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`lvpath decode` accepts hex with or without spaces. `lvpath to-labview ""` prints an
|
|
181
|
+
empty line, the `<Not A Path>` value, while `lvpath to-native ""` is an error. Errors
|
|
182
|
+
are written to standard error and the command exits with status 1. Output is UTF-8.
|
|
183
|
+
A value that starts with `-` goes after `--`. Under Git Bash on Windows, set
|
|
184
|
+
`MSYS_NO_PATHCONV=1` so the shell passes POSIX-looking arguments such as `/C/x`
|
|
185
|
+
unchanged. Run `lvpath --help` for the full usage.
|
|
186
|
+
|
|
187
|
+
## Covered conventions
|
|
188
|
+
|
|
189
|
+
LabVIEW's `Path To String` and `String To Path` render and parse a path in the
|
|
190
|
+
standard textual form of a host platform. `to_os_path`, `to_lv_path` and `path_kind`
|
|
191
|
+
model that native textual form (so `to_os_path("/C/foo", platform="windows")` is the
|
|
192
|
+
rooted Windows path `\C\foo`; read stored values with `from_portable_path` instead):
|
|
193
|
+
|
|
194
|
+
- **Separators:** `\` on Windows, `/` on POSIX. Forward slashes are also accepted
|
|
195
|
+
when parsing a Windows path, matching LabVIEW's lenient reader.
|
|
196
|
+
- **Absolute, Windows:** drive-letter paths (`C:\dir\file`) and UNC paths
|
|
197
|
+
(`\\server\share\dir`).
|
|
198
|
+
- **Absolute, POSIX:** a leading `/` (modern LabVIEW on macOS and Linux).
|
|
199
|
+
- **Relative:** any other non-empty string, including up-level (`..\up`) and
|
|
200
|
+
drive-relative (`C:file`) forms.
|
|
201
|
+
- **Not-a-path:** the empty string, LabVIEW's `<Not A Path>` value, on every
|
|
202
|
+
platform. `None` serializes back to it.
|
|
203
|
+
|
|
204
|
+
### What it does not do
|
|
205
|
+
|
|
206
|
+
- It does not touch the filesystem, resolve symbolic links, or normalise `.`/`..`
|
|
207
|
+
beyond what `pathlib` does.
|
|
208
|
+
- It does not handle classic Mac OS colon-separated paths (pre-OS X). Modern
|
|
209
|
+
LabVIEW on macOS uses POSIX `/`.
|
|
210
|
+
- The flattened codec interprets only the documented absolute and relative path
|
|
211
|
+
type codes. It does not guess the undocumented UNC or not-a-path type codes, and
|
|
212
|
+
it does not touch the textual-form behaviour above.
|
|
213
|
+
- It does not expand LabVIEW symbolic path prefixes such as `<vilib>` or
|
|
214
|
+
`<instrlib>`, and it does not resolve a relative stored value against the file
|
|
215
|
+
it came from.
|
|
216
|
+
|
|
217
|
+
## Why
|
|
218
|
+
|
|
219
|
+
LabVIEW uses two text forms for a path: the native form of its host platform for
|
|
220
|
+
`Path To String` / `String To Path`, and a platform-independent form (`/C/DIR1/DIR2`
|
|
221
|
+
for `C:\DIR1\DIR2`) when it writes a path into configuration and project files. Read
|
|
222
|
+
with plain string handling, the stored form names the wrong file on Windows, and a
|
|
223
|
+
native string only means something together with the platform convention it was
|
|
224
|
+
written in, so round-tripping LabVIEW path data in Python needs explicit,
|
|
225
|
+
platform-aware handling.
|
|
226
|
+
|
|
227
|
+
The standard library already does most of this. `pathlib`'s `PureWindowsPath` and
|
|
228
|
+
`PurePosixPath` are built to manipulate foreign-platform paths with no I/O, so they
|
|
229
|
+
cover the bulk of the separator and anchor handling on their own, and this library is
|
|
230
|
+
a thin wrapper over them rather than a reimplementation. What they do not know is the
|
|
231
|
+
genuinely LabVIEW-specific residue: the stored drive and UNC form (`/C/foo` to
|
|
232
|
+
`C:\foo` and back, through `from_portable_path` and `to_portable_path`), the empty
|
|
233
|
+
string standing for `<Not A Path>`, and LabVIEW's exact serialization rules. No PyPI package targets those conventions. `lvpath` adds exactly
|
|
234
|
+
that layer on top of `pathlib`, with a small, typed, zero-dependency surface.
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT. See [LICENSE](LICENSE).
|
|
239
|
+
|
|
240
|
+
## Links
|
|
241
|
+
|
|
242
|
+
- Website: https://lianes8studio.com
|
|
243
|
+
- GitHub: https://github.com/JaviLianes8
|
|
244
|
+
- LinkedIn: https://www.linkedin.com/in/jlianes/
|