unctools 0.1.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unctools-0.2.2/PKG-INFO +226 -0
- unctools-0.2.2/README.md +177 -0
- unctools-0.2.2/docs/api-stability.md +51 -0
- {unctools-0.1.0 → unctools-0.2.2}/examples/basic_usage.py +16 -26
- {unctools-0.1.0 → unctools-0.2.2}/pyproject.toml +5 -1
- {unctools-0.1.0 → unctools-0.2.2}/tests/basic_functionality_test.py +8 -10
- {unctools-0.1.0 → unctools-0.2.2}/tests/test_converter.py +1 -19
- {unctools-0.1.0 → unctools-0.2.2}/tests/test_converter_v2.py +2 -7
- {unctools-0.1.0 → unctools-0.2.2}/tests/test_detector.py +15 -15
- {unctools-0.1.0 → unctools-0.2.2}/tests/test_framework.py +4 -0
- unctools-0.2.2/tests/test_import_stability.py +110 -0
- unctools-0.2.2/tests/test_operations.py +363 -0
- {unctools-0.1.0 → unctools-0.2.2}/tests/test_win32net_warning.py +48 -44
- {unctools-0.1.0 → unctools-0.2.2}/tests/test_windows_imports.py +4 -6
- unctools-0.2.2/tests/test_wnet_enrichment.py +101 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/__init__.py +15 -10
- unctools-0.2.2/unctools/_version.py +92 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/converter.py +163 -21
- {unctools-0.1.0 → unctools-0.2.2}/unctools/detector.py +153 -4
- unctools-0.2.2/unctools/operations.py +42 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/utils/compat.py +3 -71
- unctools-0.2.2/unctools.egg-info/PKG-INFO +226 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools.egg-info/SOURCES.txt +4 -1
- unctools-0.1.0/PKG-INFO +0 -189
- unctools-0.1.0/README.md +0 -140
- unctools-0.1.0/examples/batch_operations.py +0 -302
- unctools-0.1.0/tests/test_operations.py +0 -649
- unctools-0.1.0/unctools/operations.py +0 -562
- unctools-0.1.0/unctools.egg-info/PKG-INFO +0 -189
- {unctools-0.1.0 → unctools-0.2.2}/LICENSE +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/MANIFEST.in +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/docs/implementation-guide.md +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/docs/implementation-summary.md +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/docs/integration-guide.md +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/examples/windows_zone_fix.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/setup.cfg +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/setup.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/tests/__init__.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/tests/conftest.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/tests/test_windows.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/utils/__init__.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/utils/logger.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/utils/validation.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/windows/__init__.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/windows/network.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/windows/registry.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools/windows/security.py +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools.egg-info/dependency_links.txt +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools.egg-info/not-zip-safe +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools.egg-info/requires.txt +0 -0
- {unctools-0.1.0 → unctools-0.2.2}/unctools.egg-info/top_level.txt +0 -0
unctools-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: unctools
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: A comprehensive toolkit for handling UNC paths, network drives, and substituted drives
|
|
5
|
+
Home-page: https://github.com/djdacy/unctools
|
|
6
|
+
Author: Dustin Darcy
|
|
7
|
+
Author-email: Dustin Darcy <your.email@example.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/djdarcy/unctools
|
|
10
|
+
Project-URL: Bug Reports, https://github.com/djdarcy/unctools/issues
|
|
11
|
+
Project-URL: Source, https://github.com/djdarcy/unctools
|
|
12
|
+
Project-URL: Documentation, https://github.com/djdarcy/unctools#readme
|
|
13
|
+
Keywords: unc,network,windows,path,file,share,subst
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
24
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
25
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
26
|
+
Classifier: Topic :: System :: Filesystems
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
+
Requires-Python: >=3.6
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: pathlib; python_version < "3.4"
|
|
32
|
+
Provides-Extra: windows
|
|
33
|
+
Requires-Dist: pywin32>=223; extra == "windows"
|
|
34
|
+
Requires-Dist: pypiwin32>=223; extra == "windows"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=6.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-cov>=2.10.0; extra == "dev"
|
|
38
|
+
Requires-Dist: flake8>=3.8.0; extra == "dev"
|
|
39
|
+
Requires-Dist: black>=20.8b1; extra == "dev"
|
|
40
|
+
Requires-Dist: tox>=3.20.0; extra == "dev"
|
|
41
|
+
Provides-Extra: docs
|
|
42
|
+
Requires-Dist: sphinx>=4.0.0; extra == "docs"
|
|
43
|
+
Requires-Dist: sphinx-rtd-theme>=0.5.0; extra == "docs"
|
|
44
|
+
Requires-Dist: myst-parser>=0.15.0; extra == "docs"
|
|
45
|
+
Dynamic: author
|
|
46
|
+
Dynamic: home-page
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
Dynamic: requires-python
|
|
49
|
+
|
|
50
|
+
# UNCtools
|
|
51
|
+
|
|
52
|
+
[](https://pypi.org/project/unctools/)
|
|
53
|
+
[](https://github.com/DazzleLib/UNCtools/releases)
|
|
54
|
+
[](https://www.python.org/downloads/)
|
|
55
|
+
[](LICENSE)
|
|
56
|
+
[](#platform-compatibility)
|
|
57
|
+
|
|
58
|
+
**Windows UNC path handling and network drive utilities: convert between `\\server\share` and mapped drives, classify where paths come from, and probe what's actually reachable.**
|
|
59
|
+
|
|
60
|
+
## The Problem
|
|
61
|
+
|
|
62
|
+
On Windows, the same file often has two names: the UNC form (`\\server\share\folder\file.txt`) and a mapped-drive form (`Z:\folder\file.txt`). Tools break when handed the one they didn't expect -- network drives disconnect, `subst` drives masquerade as real ones, security zones silently block UNC access, and scripts that worked on one machine fail on another because the drive mappings differ.
|
|
63
|
+
|
|
64
|
+
**UNCtools** answers the identity questions: *is this path UNC / network / subst / local -- and what is its other name?* It converts between path forms using the system's live mappings, classifies path origins, probes accessibility across both name variants, and (on Windows) manages the security zones and drive mappings themselves.
|
|
65
|
+
|
|
66
|
+
> [!NOTE]
|
|
67
|
+
> UNCtools is the **L0 path-identity layer** of the [DazzleLib stack](https://github.com/DazzleLib/.github/blob/main/docs/STACK-MAP.md): it may probe the filesystem read-only to answer identity questions; it never mutates or transfers content (file operations live in [dazzle-filekit](https://github.com/DazzleLib/dazzle-filekit)). The public surface is locked and machine-checked -- see [docs/api-stability.md](docs/api-stability.md).
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install unctools # or equivalently: dazzle-unctools
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from unctools import convert_to_local, classify_path_origin
|
|
77
|
+
|
|
78
|
+
local = convert_to_local(r"\\server\share\project") # -> Z:\project (if mapped)
|
|
79
|
+
origin = classify_path_origin(local) # -> "network"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Features
|
|
83
|
+
|
|
84
|
+
- **Path conversion**: UNC ↔ mapped-drive translation from the system's live network mappings (win32net with `net use` fallback)
|
|
85
|
+
- **Origin classification**: `classify_path_origin` -- `unc` / `network` / `subst` / `local` / `removable` / `cdrom` / `ramdisk`
|
|
86
|
+
- **Identity probes**: existence and accessibility checks that try *both* name variants of a path
|
|
87
|
+
- **UNC path algebra**: parse and build `\\server\share\rest` components
|
|
88
|
+
- **Issue detection**: MAX_PATH violations, broken mappings, servers missing from the Intranet security zone
|
|
89
|
+
- **Windows management** (optional extra): security zones, share enumeration, drive mapping create/remove
|
|
90
|
+
- **Cross-platform safe**: imports everywhere; Windows-specific features degrade gracefully on Unix
|
|
91
|
+
|
|
92
|
+
## Installation
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install unctools # standard (or: dazzle-unctools)
|
|
96
|
+
pip install unctools[windows] # + pywin32 for the rich Windows APIs
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Development
|
|
101
|
+
git clone https://github.com/DazzleLib/UNCtools.git
|
|
102
|
+
cd UNCtools
|
|
103
|
+
pip install -e .[dev]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Usage
|
|
107
|
+
|
|
108
|
+
### Basic Path Conversion
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from unctools import convert_to_local, convert_to_unc
|
|
112
|
+
|
|
113
|
+
# Convert UNC path to local drive path
|
|
114
|
+
local_path = convert_to_local("\\\\server\\share\\folder\\file.txt")
|
|
115
|
+
# Result (if mapped): "Z:\\folder\\file.txt"
|
|
116
|
+
|
|
117
|
+
# Convert local drive path back to UNC
|
|
118
|
+
unc_path = convert_to_unc("Z:\\folder\\file.txt")
|
|
119
|
+
# Result: "\\\\server\\share\\folder\\file.txt"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Path Detection
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from unctools import is_unc_path, is_network_drive, is_subst_drive, classify_path_origin
|
|
126
|
+
|
|
127
|
+
# Check if a path is a UNC path
|
|
128
|
+
if is_unc_path("\\\\server\\share\\file.txt"):
|
|
129
|
+
print("This is a UNC path")
|
|
130
|
+
|
|
131
|
+
# Check if a drive is a network drive
|
|
132
|
+
if is_network_drive("Z:"):
|
|
133
|
+
print("Z: is a network drive")
|
|
134
|
+
|
|
135
|
+
# Classify WHERE a path comes from
|
|
136
|
+
origin = classify_path_origin("C:\\Users\\")
|
|
137
|
+
# Result: "local", "network", "subst", "unc", etc.
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Identity Probes & Batch Conversion
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from unctools import file_exists, find_accessible_path, batch_convert
|
|
144
|
+
|
|
145
|
+
# Does the file exist under EITHER of its names (UNC or mapped)?
|
|
146
|
+
if file_exists("\\\\server\\share\\file.txt"):
|
|
147
|
+
print("Reachable under at least one name")
|
|
148
|
+
|
|
149
|
+
# Which name variant actually works right now?
|
|
150
|
+
usable = find_accessible_path("\\\\server\\share\\file.txt")
|
|
151
|
+
# Result: Path("Z:\\file.txt"), Path("\\\\server\\share\\file.txt"), or None
|
|
152
|
+
|
|
153
|
+
# Convert multiple paths at once
|
|
154
|
+
paths = ["\\\\server\\share\\file1.txt", "\\\\server\\share\\file2.txt"]
|
|
155
|
+
converted = batch_convert(paths, to_unc=False)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### UNC Path Algebra
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from unctools import get_unc_path_elements, build_unc_path
|
|
162
|
+
|
|
163
|
+
server, share, rest = get_unc_path_elements("\\\\server\\share\\folder\\file.txt")
|
|
164
|
+
# Result: ("server", "share", "folder\\file.txt")
|
|
165
|
+
|
|
166
|
+
unc = build_unc_path("server", "share", "folder/file.txt")
|
|
167
|
+
# Result: "\\\\server\\share\\folder/file.txt"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Windows Security Zones
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from unctools.windows import fix_security_zone, add_to_intranet_zone
|
|
174
|
+
|
|
175
|
+
# Fix security zone issues for a server
|
|
176
|
+
fix_security_zone("server")
|
|
177
|
+
|
|
178
|
+
# Add a server to the Local Intranet zone
|
|
179
|
+
add_to_intranet_zone("server")
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Network Drive Management
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from unctools.windows import create_network_mapping, remove_network_mapping
|
|
186
|
+
|
|
187
|
+
# Create a network drive mapping
|
|
188
|
+
success, drive = create_network_mapping("\\\\server\\share", "Z:")
|
|
189
|
+
|
|
190
|
+
# Remove a network drive mapping
|
|
191
|
+
remove_network_mapping("Z:")
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Platform Compatibility
|
|
195
|
+
|
|
196
|
+
| Platform | Status |
|
|
197
|
+
|----------|--------|
|
|
198
|
+
| Windows | Full functionality: conversion, classification, probes, security zones, drive management |
|
|
199
|
+
| Linux / macOS | Path algebra and syntax checks work; mapping-dependent features degrade gracefully (no-ops / passthrough) |
|
|
200
|
+
|
|
201
|
+
## Requirements
|
|
202
|
+
|
|
203
|
+
- **Python 3.6+** (3.9-3.13 tested in CI)
|
|
204
|
+
- **pywin32** (optional, via `unctools[windows]`) for the rich Windows APIs -- core features fall back to `net use` parsing without it
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
python -m pytest tests/ # 50+ tests; includes the api-stability import canary
|
|
210
|
+
black unctools
|
|
211
|
+
flake8 unctools
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The public API surface is locked: see **[docs/api-stability.md](docs/api-stability.md)** for the policy, the active deprecation shims (`get_path_type` -> `classify_path_origin`, removed in 0.3.0), and known consumers. Changes follow the [stack's](https://github.com/DazzleLib/.github/blob/main/docs/STACK-MAP.md) noisy-shim deprecation policy -- never silent.
|
|
215
|
+
|
|
216
|
+
## Contributing
|
|
217
|
+
|
|
218
|
+
Contributions welcome! Please open an issue or submit a pull request.
|
|
219
|
+
|
|
220
|
+
Like the project?
|
|
221
|
+
|
|
222
|
+
[](https://www.buymeacoffee.com/djdarcy)
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
unctools-0.2.2/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# UNCtools
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/unctools/)
|
|
4
|
+
[](https://github.com/DazzleLib/UNCtools/releases)
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](#platform-compatibility)
|
|
8
|
+
|
|
9
|
+
**Windows UNC path handling and network drive utilities: convert between `\\server\share` and mapped drives, classify where paths come from, and probe what's actually reachable.**
|
|
10
|
+
|
|
11
|
+
## The Problem
|
|
12
|
+
|
|
13
|
+
On Windows, the same file often has two names: the UNC form (`\\server\share\folder\file.txt`) and a mapped-drive form (`Z:\folder\file.txt`). Tools break when handed the one they didn't expect -- network drives disconnect, `subst` drives masquerade as real ones, security zones silently block UNC access, and scripts that worked on one machine fail on another because the drive mappings differ.
|
|
14
|
+
|
|
15
|
+
**UNCtools** answers the identity questions: *is this path UNC / network / subst / local -- and what is its other name?* It converts between path forms using the system's live mappings, classifies path origins, probes accessibility across both name variants, and (on Windows) manages the security zones and drive mappings themselves.
|
|
16
|
+
|
|
17
|
+
> [!NOTE]
|
|
18
|
+
> UNCtools is the **L0 path-identity layer** of the [DazzleLib stack](https://github.com/DazzleLib/.github/blob/main/docs/STACK-MAP.md): it may probe the filesystem read-only to answer identity questions; it never mutates or transfers content (file operations live in [dazzle-filekit](https://github.com/DazzleLib/dazzle-filekit)). The public surface is locked and machine-checked -- see [docs/api-stability.md](docs/api-stability.md).
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install unctools # or equivalently: dazzle-unctools
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from unctools import convert_to_local, classify_path_origin
|
|
28
|
+
|
|
29
|
+
local = convert_to_local(r"\\server\share\project") # -> Z:\project (if mapped)
|
|
30
|
+
origin = classify_path_origin(local) # -> "network"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- **Path conversion**: UNC ↔ mapped-drive translation from the system's live network mappings (win32net with `net use` fallback)
|
|
36
|
+
- **Origin classification**: `classify_path_origin` -- `unc` / `network` / `subst` / `local` / `removable` / `cdrom` / `ramdisk`
|
|
37
|
+
- **Identity probes**: existence and accessibility checks that try *both* name variants of a path
|
|
38
|
+
- **UNC path algebra**: parse and build `\\server\share\rest` components
|
|
39
|
+
- **Issue detection**: MAX_PATH violations, broken mappings, servers missing from the Intranet security zone
|
|
40
|
+
- **Windows management** (optional extra): security zones, share enumeration, drive mapping create/remove
|
|
41
|
+
- **Cross-platform safe**: imports everywhere; Windows-specific features degrade gracefully on Unix
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install unctools # standard (or: dazzle-unctools)
|
|
47
|
+
pip install unctools[windows] # + pywin32 for the rich Windows APIs
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Development
|
|
52
|
+
git clone https://github.com/DazzleLib/UNCtools.git
|
|
53
|
+
cd UNCtools
|
|
54
|
+
pip install -e .[dev]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Basic Path Conversion
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from unctools import convert_to_local, convert_to_unc
|
|
63
|
+
|
|
64
|
+
# Convert UNC path to local drive path
|
|
65
|
+
local_path = convert_to_local("\\\\server\\share\\folder\\file.txt")
|
|
66
|
+
# Result (if mapped): "Z:\\folder\\file.txt"
|
|
67
|
+
|
|
68
|
+
# Convert local drive path back to UNC
|
|
69
|
+
unc_path = convert_to_unc("Z:\\folder\\file.txt")
|
|
70
|
+
# Result: "\\\\server\\share\\folder\\file.txt"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Path Detection
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from unctools import is_unc_path, is_network_drive, is_subst_drive, classify_path_origin
|
|
77
|
+
|
|
78
|
+
# Check if a path is a UNC path
|
|
79
|
+
if is_unc_path("\\\\server\\share\\file.txt"):
|
|
80
|
+
print("This is a UNC path")
|
|
81
|
+
|
|
82
|
+
# Check if a drive is a network drive
|
|
83
|
+
if is_network_drive("Z:"):
|
|
84
|
+
print("Z: is a network drive")
|
|
85
|
+
|
|
86
|
+
# Classify WHERE a path comes from
|
|
87
|
+
origin = classify_path_origin("C:\\Users\\")
|
|
88
|
+
# Result: "local", "network", "subst", "unc", etc.
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Identity Probes & Batch Conversion
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from unctools import file_exists, find_accessible_path, batch_convert
|
|
95
|
+
|
|
96
|
+
# Does the file exist under EITHER of its names (UNC or mapped)?
|
|
97
|
+
if file_exists("\\\\server\\share\\file.txt"):
|
|
98
|
+
print("Reachable under at least one name")
|
|
99
|
+
|
|
100
|
+
# Which name variant actually works right now?
|
|
101
|
+
usable = find_accessible_path("\\\\server\\share\\file.txt")
|
|
102
|
+
# Result: Path("Z:\\file.txt"), Path("\\\\server\\share\\file.txt"), or None
|
|
103
|
+
|
|
104
|
+
# Convert multiple paths at once
|
|
105
|
+
paths = ["\\\\server\\share\\file1.txt", "\\\\server\\share\\file2.txt"]
|
|
106
|
+
converted = batch_convert(paths, to_unc=False)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### UNC Path Algebra
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from unctools import get_unc_path_elements, build_unc_path
|
|
113
|
+
|
|
114
|
+
server, share, rest = get_unc_path_elements("\\\\server\\share\\folder\\file.txt")
|
|
115
|
+
# Result: ("server", "share", "folder\\file.txt")
|
|
116
|
+
|
|
117
|
+
unc = build_unc_path("server", "share", "folder/file.txt")
|
|
118
|
+
# Result: "\\\\server\\share\\folder/file.txt"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Windows Security Zones
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from unctools.windows import fix_security_zone, add_to_intranet_zone
|
|
125
|
+
|
|
126
|
+
# Fix security zone issues for a server
|
|
127
|
+
fix_security_zone("server")
|
|
128
|
+
|
|
129
|
+
# Add a server to the Local Intranet zone
|
|
130
|
+
add_to_intranet_zone("server")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Network Drive Management
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from unctools.windows import create_network_mapping, remove_network_mapping
|
|
137
|
+
|
|
138
|
+
# Create a network drive mapping
|
|
139
|
+
success, drive = create_network_mapping("\\\\server\\share", "Z:")
|
|
140
|
+
|
|
141
|
+
# Remove a network drive mapping
|
|
142
|
+
remove_network_mapping("Z:")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Platform Compatibility
|
|
146
|
+
|
|
147
|
+
| Platform | Status |
|
|
148
|
+
|----------|--------|
|
|
149
|
+
| Windows | Full functionality: conversion, classification, probes, security zones, drive management |
|
|
150
|
+
| Linux / macOS | Path algebra and syntax checks work; mapping-dependent features degrade gracefully (no-ops / passthrough) |
|
|
151
|
+
|
|
152
|
+
## Requirements
|
|
153
|
+
|
|
154
|
+
- **Python 3.6+** (3.9-3.13 tested in CI)
|
|
155
|
+
- **pywin32** (optional, via `unctools[windows]`) for the rich Windows APIs -- core features fall back to `net use` parsing without it
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
python -m pytest tests/ # 50+ tests; includes the api-stability import canary
|
|
161
|
+
black unctools
|
|
162
|
+
flake8 unctools
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The public API surface is locked: see **[docs/api-stability.md](docs/api-stability.md)** for the policy, the active deprecation shims (`get_path_type` -> `classify_path_origin`, removed in 0.3.0), and known consumers. Changes follow the [stack's](https://github.com/DazzleLib/.github/blob/main/docs/STACK-MAP.md) noisy-shim deprecation policy -- never silent.
|
|
166
|
+
|
|
167
|
+
## Contributing
|
|
168
|
+
|
|
169
|
+
Contributions welcome! Please open an issue or submit a pull request.
|
|
170
|
+
|
|
171
|
+
Like the project?
|
|
172
|
+
|
|
173
|
+
[](https://www.buymeacoffee.com/djdarcy)
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# API Stability
|
|
2
|
+
|
|
3
|
+
UNCtools is the L0 path-identity layer of the
|
|
4
|
+
[DazzleLib stack](https://github.com/DazzleLib/.github/blob/main/docs/STACK-MAP.md).
|
|
5
|
+
Its public surface is locked and machine-checked by
|
|
6
|
+
`tests/test_import_stability.py` -- if that canary fails, a consumer somewhere
|
|
7
|
+
breaks: follow the policy below, never silently fix the test.
|
|
8
|
+
|
|
9
|
+
## Policy
|
|
10
|
+
|
|
11
|
+
1. **Locked symbols never vanish silently.** Removal/rename ships a NOISY
|
|
12
|
+
`DeprecationWarning` shim naming the new home and removal version,
|
|
13
|
+
registered in the stack's alias register, removed on schedule.
|
|
14
|
+
2. **The layer charter is not negotiable** (STACK-MAP rule 3a): this library
|
|
15
|
+
may probe the filesystem read-only to answer identity questions; it never
|
|
16
|
+
mutates or transfers content. Functions that do are rejected, not deprecated.
|
|
17
|
+
3. **Name hygiene** (STACK-MAP rule 7): before exporting a public symbol,
|
|
18
|
+
grep the stack -- same-name-different-semantics requires a layer-teaching
|
|
19
|
+
rename (that is how `classify_path_origin` got its name).
|
|
20
|
+
|
|
21
|
+
## Locked surface (0.2.0)
|
|
22
|
+
|
|
23
|
+
| Module | Symbols |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `unctools` (top level) | `convert_to_local`, `convert_to_unc`, `batch_convert`, `get_unc_path_elements`, `build_unc_path`, `is_unc_path`, `is_network_drive`, `is_subst_drive`, `classify_path_origin`, `get_network_mappings`, `detect_path_issues`, `file_exists`, `is_path_accessible`, `find_accessible_path`, `configure_logging`, `get_version` |
|
|
26
|
+
| `unctools.converter` | `UNCConverter`, `convert_to_local`, `convert_to_unc`, `batch_convert`, `get_unc_path_elements`, `build_unc_path`, `refresh_mappings`, `get_mappings`, `parse_unc_path`, `join_unc_path` |
|
|
27
|
+
| `unctools.detector` | the origin classifiers + probes listed above, plus `get_drive_type`, `get_subst_target`, `get_network_target`, `is_server_in_intranet_zone` |
|
|
28
|
+
| `unctools.windows.*` | Windows-only security/network/registry surface (unchanged in 0.2.0) |
|
|
29
|
+
|
|
30
|
+
## Active deprecations
|
|
31
|
+
|
|
32
|
+
| Symbol | Replacement | Warns since | Removed in |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| `get_path_type` | `classify_path_origin` | 0.2.0 | 0.3.0 |
|
|
35
|
+
| `unctools.operations` (module facade) | top-level imports / `converter` / `detector` | 0.2.0 | 0.3.0 |
|
|
36
|
+
|
|
37
|
+
## Known consumers
|
|
38
|
+
|
|
39
|
+
| Consumer | Symbols | Notes |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| dazzlecmd (`safedel/_volumes.py`, `fixpath.py`) | `get_drive_type`, `is_network_drive`, `is_unc_path`, `convert_to_local` | optional imports today; harden in stack P4 |
|
|
42
|
+
| dazzlesum | top-level conversion/probe block | soft-imports today; harden in stack P4 |
|
|
43
|
+
| modified_datetime_fix | mixed (incl. a vendored copy to retire) | stack P4 |
|
|
44
|
+
| dazzle-filekit | `[unctools]` extra pin (no runtime import) | becomes a real edge only if/when filekit consumes identity at runtime |
|
|
45
|
+
|
|
46
|
+
## Consolidation candidates (0.3.0)
|
|
47
|
+
|
|
48
|
+
- `parse_unc_path`/`join_unc_path` vs `get_unc_path_elements`/`build_unc_path`
|
|
49
|
+
(near-duplicates; the latter preserve forward slashes in the relative part)
|
|
50
|
+
- dazzle-lib adoption: derive errors from `dazzle_lib.PathIdentityError`
|
|
51
|
+
(deliberately deferred from 0.2.0 to keep the surgery diff reviewable)
|
|
@@ -17,9 +17,9 @@ logging.basicConfig(level=logging.INFO,
|
|
|
17
17
|
# Import UNCtools
|
|
18
18
|
import unctools
|
|
19
19
|
from unctools import (
|
|
20
|
-
convert_to_local, convert_to_unc,
|
|
21
|
-
is_unc_path, is_network_drive, is_subst_drive,
|
|
22
|
-
|
|
20
|
+
convert_to_local, convert_to_unc,
|
|
21
|
+
is_unc_path, is_network_drive, is_subst_drive, classify_path_origin,
|
|
22
|
+
file_exists, find_accessible_path, batch_convert
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
def print_section(title):
|
|
@@ -43,7 +43,8 @@ def demonstrate_path_conversion():
|
|
|
43
43
|
# Show network mappings if on Windows
|
|
44
44
|
if os.name == 'nt':
|
|
45
45
|
print("\nNetwork Mappings:")
|
|
46
|
-
|
|
46
|
+
from unctools.converter import get_mappings
|
|
47
|
+
mappings = get_mappings()
|
|
47
48
|
for unc, drive in mappings.items():
|
|
48
49
|
print(f" {unc} -> {drive}")
|
|
49
50
|
|
|
@@ -52,10 +53,11 @@ def demonstrate_path_conversion():
|
|
|
52
53
|
print(f" UNC -> Local: {unc_path} -> {convert_to_local(unc_path)}")
|
|
53
54
|
print(f" Local -> UNC: {local_path} -> {convert_to_unc(local_path)}")
|
|
54
55
|
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
print(
|
|
58
|
-
print(f"
|
|
56
|
+
# Round-trip explicitly (normalize_path was removed in 0.2.0 -- the
|
|
57
|
+
# explicit converts ARE the API)
|
|
58
|
+
print("\nExplicit round-trip:")
|
|
59
|
+
print(f" To local: {convert_to_local(unc_path)}")
|
|
60
|
+
print(f" To UNC: {convert_to_unc(local_path)}")
|
|
59
61
|
|
|
60
62
|
def demonstrate_path_detection():
|
|
61
63
|
"""Demonstrate path detection functionality."""
|
|
@@ -72,7 +74,7 @@ def demonstrate_path_detection():
|
|
|
72
74
|
|
|
73
75
|
print("Path Type Detection:")
|
|
74
76
|
for path in paths:
|
|
75
|
-
path_type =
|
|
77
|
+
path_type = classify_path_origin(path)
|
|
76
78
|
is_unc = is_unc_path(path)
|
|
77
79
|
print(f" {path}:")
|
|
78
80
|
print(f" Type: {path_type}")
|
|
@@ -112,14 +114,11 @@ def demonstrate_file_operations():
|
|
|
112
114
|
with open(temp_file, "w") as f:
|
|
113
115
|
f.write("Hello, UNCtools!")
|
|
114
116
|
|
|
115
|
-
#
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
print(f" Content: {content}")
|
|
121
|
-
except Exception as e:
|
|
122
|
-
print(f" Error: {e}")
|
|
117
|
+
# Identity probes (safe_open was removed in 0.2.0 -- I/O lives in
|
|
118
|
+
# dazzle-filekit; unctools answers identity questions)
|
|
119
|
+
print("\nIdentity probes:")
|
|
120
|
+
print(f" file_exists: {file_exists(temp_file)}")
|
|
121
|
+
print(f" accessible variant: {find_accessible_path(temp_file)}")
|
|
123
122
|
|
|
124
123
|
# Batch operations
|
|
125
124
|
print("\nBatch Operations:")
|
|
@@ -129,15 +128,6 @@ def demonstrate_file_operations():
|
|
|
129
128
|
with open(os.path.join(temp_dir, f"temp_file_{i}.txt"), "w") as f:
|
|
130
129
|
f.write(f"Test file {i}")
|
|
131
130
|
|
|
132
|
-
# Process files
|
|
133
|
-
print(" Processing files:")
|
|
134
|
-
def process_callback(path):
|
|
135
|
-
return path.stat().st_size
|
|
136
|
-
|
|
137
|
-
results = process_files(temp_dir, process_callback, pattern="temp_file*.txt")
|
|
138
|
-
for path, size in results.items():
|
|
139
|
-
print(f" {path}: {size} bytes")
|
|
140
|
-
|
|
141
131
|
# Batch convert
|
|
142
132
|
print("\n Batch conversion:")
|
|
143
133
|
paths = [os.path.join(temp_dir, f"temp_file_{i}.txt") for i in range(3)]
|
|
@@ -63,7 +63,11 @@ include = ["unctools*"]
|
|
|
63
63
|
exclude = ["tests*"]
|
|
64
64
|
|
|
65
65
|
[tool.setuptools.dynamic]
|
|
66
|
-
version = {attr = "unctools.
|
|
66
|
+
version = {attr = "unctools._version.PIP_VERSION"}
|
|
67
|
+
|
|
68
|
+
[tool.repokit-common]
|
|
69
|
+
version-source = "unctools/_version.py"
|
|
70
|
+
repo-url = "https://github.com/DazzleLib/UNCtools"
|
|
67
71
|
|
|
68
72
|
[tool.setuptools.package-data]
|
|
69
73
|
unctools = ["py.typed"]
|
|
@@ -62,9 +62,9 @@ def run_tests():
|
|
|
62
62
|
print("\nTesting core module imports...")
|
|
63
63
|
try:
|
|
64
64
|
from unctools import (
|
|
65
|
-
convert_to_local, convert_to_unc,
|
|
65
|
+
convert_to_local, convert_to_unc, classify_path_origin,
|
|
66
66
|
is_unc_path, is_network_drive, is_subst_drive,
|
|
67
|
-
|
|
67
|
+
file_exists, batch_convert
|
|
68
68
|
)
|
|
69
69
|
check(True, "Import core functions")
|
|
70
70
|
except ImportError as e:
|
|
@@ -111,12 +111,12 @@ def run_tests():
|
|
|
111
111
|
test_unc_path = r"\\server\share\folder"
|
|
112
112
|
check(is_unc_path(test_unc_path), f"is_unc_path({test_unc_path}) should be True")
|
|
113
113
|
|
|
114
|
-
# Test
|
|
114
|
+
# Test origin classification (normalize_path removed in 0.2.0, D4)
|
|
115
115
|
try:
|
|
116
|
-
|
|
117
|
-
check(True, f"
|
|
116
|
+
origin = classify_path_origin(test_path)
|
|
117
|
+
check(True, f"classify_path_origin({test_path}) => {origin}")
|
|
118
118
|
except Exception as e:
|
|
119
|
-
check(False, f"
|
|
119
|
+
check(False, f"classify_path_origin({test_path}): {e}")
|
|
120
120
|
|
|
121
121
|
# Test path conversion (just ensure it doesn't error)
|
|
122
122
|
try:
|
|
@@ -157,10 +157,8 @@ def run_tests():
|
|
|
157
157
|
temp_filename = f.name
|
|
158
158
|
f.write("UNCtools test file")
|
|
159
159
|
|
|
160
|
-
# Test safe_open
|
|
161
|
-
|
|
162
|
-
content = f.read()
|
|
163
|
-
check(content == "UNCtools test file", f"safe_open() and read content: '{content}'")
|
|
160
|
+
# Test file_exists probe (safe_open removed in 0.2.0, D7)
|
|
161
|
+
check(file_exists(temp_filename), f"file_exists({temp_filename}) should be True")
|
|
164
162
|
|
|
165
163
|
# Clean up
|
|
166
164
|
os.unlink(temp_filename)
|
|
@@ -10,7 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
from unittest import mock
|
|
11
11
|
|
|
12
12
|
from unctools.converter import (
|
|
13
|
-
UNCConverter, convert_to_local, convert_to_unc,
|
|
13
|
+
UNCConverter, convert_to_local, convert_to_unc,
|
|
14
14
|
parse_unc_path, join_unc_path
|
|
15
15
|
)
|
|
16
16
|
|
|
@@ -180,24 +180,6 @@ class TestModuleFunctions:
|
|
|
180
180
|
assert result == Path(TEST_UNC_PATH)
|
|
181
181
|
mock_converter.convert_to_unc.assert_called_once_with(TEST_LOCAL_PATH)
|
|
182
182
|
|
|
183
|
-
def test_normalize_path(self):
|
|
184
|
-
"""Test normalize_path function."""
|
|
185
|
-
# Test with prefer_unc=False (default)
|
|
186
|
-
with mock.patch('unctools.converter.convert_to_local') as mock_convert_local:
|
|
187
|
-
mock_convert_local.return_value = Path(TEST_LOCAL_PATH)
|
|
188
|
-
|
|
189
|
-
result = normalize_path(TEST_UNC_PATH)
|
|
190
|
-
assert result == Path(TEST_LOCAL_PATH)
|
|
191
|
-
mock_convert_local.assert_called_once_with(Path(TEST_UNC_PATH))
|
|
192
|
-
|
|
193
|
-
# Test with prefer_unc=True
|
|
194
|
-
with mock.patch('unctools.converter.convert_to_unc') as mock_convert_unc:
|
|
195
|
-
mock_convert_unc.return_value = Path(TEST_UNC_PATH)
|
|
196
|
-
|
|
197
|
-
result = normalize_path(TEST_LOCAL_PATH, prefer_unc=True)
|
|
198
|
-
assert result == Path(TEST_UNC_PATH)
|
|
199
|
-
mock_convert_unc.assert_called_once_with(Path(TEST_LOCAL_PATH))
|
|
200
|
-
|
|
201
183
|
def test_parse_unc_path(self):
|
|
202
184
|
"""Test parse_unc_path function."""
|
|
203
185
|
# Test with a valid UNC path
|
|
@@ -23,7 +23,7 @@ from tests.test_framework import (
|
|
|
23
23
|
# Import UNCtools
|
|
24
24
|
import unctools
|
|
25
25
|
from unctools.converter import (
|
|
26
|
-
UNCConverter, convert_to_local, convert_to_unc,
|
|
26
|
+
UNCConverter, convert_to_local, convert_to_unc,
|
|
27
27
|
parse_unc_path, join_unc_path
|
|
28
28
|
)
|
|
29
29
|
|
|
@@ -155,12 +155,7 @@ def test_module_functions():
|
|
|
155
155
|
path = convert_to_unc(TEST_LOCAL_PATH)
|
|
156
156
|
assert_is_not_none(path)
|
|
157
157
|
|
|
158
|
-
#
|
|
159
|
-
path = normalize_path(TEST_UNC_PATH)
|
|
160
|
-
assert_is_not_none(path)
|
|
161
|
-
|
|
162
|
-
path = normalize_path(TEST_LOCAL_PATH, prefer_unc=True)
|
|
163
|
-
assert_is_not_none(path)
|
|
158
|
+
# normalize_path was removed in 0.2.0 (D4) -- the explicit converts ARE the API
|
|
164
159
|
|
|
165
160
|
def test_parse_unc_path():
|
|
166
161
|
"""Test parse_unc_path function."""
|