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.
@@ -0,0 +1,12 @@
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/
@@ -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/