unctools 0.2.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.0 → unctools-0.2.2}/examples/basic_usage.py +16 -26
- {unctools-0.2.0 → unctools-0.2.2}/pyproject.toml +5 -1
- {unctools-0.2.0 → unctools-0.2.2}/tests/test_detector.py +15 -15
- {unctools-0.2.0 → unctools-0.2.2}/tests/test_framework.py +4 -0
- {unctools-0.2.0 → unctools-0.2.2}/tests/test_import_stability.py +15 -2
- {unctools-0.2.0 → unctools-0.2.2}/tests/test_operations.py +2 -0
- {unctools-0.2.0 → unctools-0.2.2}/tests/test_win32net_warning.py +48 -44
- {unctools-0.2.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.2.0 → unctools-0.2.2}/unctools/__init__.py +1 -1
- unctools-0.2.2/unctools/_version.py +92 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/converter.py +66 -3
- unctools-0.2.2/unctools.egg-info/PKG-INFO +226 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools.egg-info/SOURCES.txt +2 -1
- unctools-0.2.0/PKG-INFO +0 -189
- unctools-0.2.0/README.md +0 -140
- unctools-0.2.0/examples/batch_operations.py +0 -302
- unctools-0.2.0/unctools.egg-info/PKG-INFO +0 -189
- {unctools-0.2.0 → unctools-0.2.2}/LICENSE +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/MANIFEST.in +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/docs/api-stability.md +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/docs/implementation-guide.md +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/docs/implementation-summary.md +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/docs/integration-guide.md +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/examples/windows_zone_fix.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/setup.cfg +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/setup.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/tests/__init__.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/tests/basic_functionality_test.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/tests/conftest.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/tests/test_converter.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/tests/test_converter_v2.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/tests/test_windows.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/detector.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/operations.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/utils/__init__.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/utils/compat.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/utils/logger.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/utils/validation.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/windows/__init__.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/windows/network.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/windows/registry.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools/windows/security.py +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools.egg-info/dependency_links.txt +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools.egg-info/not-zip-safe +0 -0
- {unctools-0.2.0 → unctools-0.2.2}/unctools.egg-info/requires.txt +0 -0
- {unctools-0.2.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.
|
|
@@ -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"]
|
|
@@ -26,7 +26,7 @@ from tests.test_framework import (
|
|
|
26
26
|
import unctools
|
|
27
27
|
from unctools.detector import (
|
|
28
28
|
is_unc_path, is_network_drive, is_subst_drive,
|
|
29
|
-
|
|
29
|
+
classify_path_origin, detect_path_issues, get_network_mappings,
|
|
30
30
|
get_subst_target, get_network_target, is_server_in_intranet_zone,
|
|
31
31
|
PATH_TYPE_UNC, PATH_TYPE_NETWORK, PATH_TYPE_SUBST,
|
|
32
32
|
PATH_TYPE_LOCAL, PATH_TYPE_UNKNOWN, PATH_TYPE_REMOVABLE,
|
|
@@ -177,8 +177,8 @@ def test_get_network_target():
|
|
|
177
177
|
assert_is_none(get_network_target("Q:"),
|
|
178
178
|
"Non-existent drive should return None")
|
|
179
179
|
|
|
180
|
-
def
|
|
181
|
-
"""Test
|
|
180
|
+
def test_classify_path_origin():
|
|
181
|
+
"""Test classify_path_origin function."""
|
|
182
182
|
# Mock the underlying functions
|
|
183
183
|
def mock_is_unc_path(path):
|
|
184
184
|
return str(path).startswith("\\\\") or str(path).startswith("//")
|
|
@@ -204,37 +204,37 @@ def test_get_path_type():
|
|
|
204
204
|
mock.patch('unctools.detector.get_drive_type', side_effect=mock_get_drive_type):
|
|
205
205
|
|
|
206
206
|
# Test with UNC path
|
|
207
|
-
assert_equal(
|
|
207
|
+
assert_equal(classify_path_origin(TEST_UNC_PATH), PATH_TYPE_UNC,
|
|
208
208
|
"UNC path should be detected as UNC type")
|
|
209
209
|
|
|
210
210
|
# Test with local path
|
|
211
|
-
assert_equal(
|
|
211
|
+
assert_equal(classify_path_origin(TEST_LOCAL_PATH), PATH_TYPE_LOCAL,
|
|
212
212
|
"Local path should be detected as local type")
|
|
213
213
|
|
|
214
214
|
# Test with network drive path
|
|
215
|
-
assert_equal(
|
|
215
|
+
assert_equal(classify_path_origin(TEST_NETWORK_PATH), PATH_TYPE_NETWORK,
|
|
216
216
|
"Network drive path should be detected as network type")
|
|
217
217
|
|
|
218
218
|
# Test with subst drive path
|
|
219
|
-
assert_equal(
|
|
219
|
+
assert_equal(classify_path_origin(TEST_SUBST_PATH), PATH_TYPE_SUBST,
|
|
220
220
|
"Subst drive path should be detected as subst type")
|
|
221
221
|
|
|
222
222
|
# Test with removable drive path
|
|
223
|
-
assert_equal(
|
|
223
|
+
assert_equal(classify_path_origin("E:\\file.txt"), PATH_TYPE_REMOVABLE,
|
|
224
224
|
"Removable drive path should be detected as removable type")
|
|
225
225
|
|
|
226
226
|
# Test with CD-ROM drive path
|
|
227
|
-
assert_equal(
|
|
227
|
+
assert_equal(classify_path_origin("D:\\file.txt"), PATH_TYPE_CDROM,
|
|
228
228
|
"CD-ROM drive path should be detected as cdrom type")
|
|
229
229
|
|
|
230
230
|
# Test with RAM disk path
|
|
231
|
-
assert_equal(
|
|
231
|
+
assert_equal(classify_path_origin("R:\\file.txt"), PATH_TYPE_RAMDISK,
|
|
232
232
|
"RAM disk path should be detected as ramdisk type")
|
|
233
233
|
|
|
234
234
|
# Test with invalid path
|
|
235
|
-
assert_equal(
|
|
235
|
+
assert_equal(classify_path_origin(""), PATH_TYPE_UNKNOWN,
|
|
236
236
|
"Empty string should be detected as unknown type")
|
|
237
|
-
assert_equal(
|
|
237
|
+
assert_equal(classify_path_origin(None), PATH_TYPE_UNKNOWN,
|
|
238
238
|
"None should be detected as unknown type")
|
|
239
239
|
|
|
240
240
|
@pytest.mark.skipif(os.name != 'nt', reason="Windows-specific test - tests security zone functionality")
|
|
@@ -244,7 +244,7 @@ def test_detect_path_issues():
|
|
|
244
244
|
def mock_is_unc_path(path):
|
|
245
245
|
return str(path).startswith("\\\\") or str(path).startswith("//")
|
|
246
246
|
|
|
247
|
-
def
|
|
247
|
+
def mock_classify_path_origin(path):
|
|
248
248
|
if mock_is_unc_path(path):
|
|
249
249
|
return PATH_TYPE_UNC
|
|
250
250
|
elif str(path).startswith("Z:"):
|
|
@@ -268,7 +268,7 @@ def test_detect_path_issues():
|
|
|
268
268
|
return None
|
|
269
269
|
|
|
270
270
|
with mock.patch('unctools.detector.is_unc_path', side_effect=mock_is_unc_path), \
|
|
271
|
-
mock.patch('unctools.detector.
|
|
271
|
+
mock.patch('unctools.detector.classify_path_origin', side_effect=mock_classify_path_origin), \
|
|
272
272
|
mock.patch('unctools.detector.is_server_in_intranet_zone', side_effect=mock_is_server_in_intranet_zone), \
|
|
273
273
|
mock.patch('unctools.detector.get_network_target', side_effect=mock_get_network_target), \
|
|
274
274
|
mock.patch('unctools.detector.get_subst_target', side_effect=mock_get_subst_target), \
|
|
@@ -373,7 +373,7 @@ def run_tests():
|
|
|
373
373
|
suite.add_test(test_is_subst_drive)
|
|
374
374
|
suite.add_test(test_get_subst_target)
|
|
375
375
|
suite.add_test(test_get_network_target)
|
|
376
|
-
suite.add_test(
|
|
376
|
+
suite.add_test(test_classify_path_origin)
|
|
377
377
|
suite.add_test(test_detect_path_issues)
|
|
378
378
|
suite.add_test(test_is_server_in_intranet_zone)
|
|
379
379
|
suite.add_test(test_get_network_mappings)
|
|
@@ -16,6 +16,8 @@ logging.basicConfig(level=logging.INFO,
|
|
|
16
16
|
|
|
17
17
|
class TestResult:
|
|
18
18
|
"""Container for test results."""
|
|
19
|
+
|
|
20
|
+
__test__ = False # custom-runner container, not a pytest test class
|
|
19
21
|
|
|
20
22
|
def __init__(self):
|
|
21
23
|
self.passed = []
|
|
@@ -59,6 +61,8 @@ class TestResult:
|
|
|
59
61
|
|
|
60
62
|
class TestSuite:
|
|
61
63
|
"""A suite of tests."""
|
|
64
|
+
|
|
65
|
+
__test__ = False # custom standalone runner, not a pytest test class
|
|
62
66
|
|
|
63
67
|
def __init__(self, name: str):
|
|
64
68
|
self.name = name
|
|
@@ -92,6 +92,19 @@ def test_operations_facade_warns_on_import():
|
|
|
92
92
|
import unctools.operations # noqa: F401
|
|
93
93
|
|
|
94
94
|
|
|
95
|
-
def
|
|
95
|
+
def test_version_is_wired_and_consistent():
|
|
96
|
+
# Version plumbing is correct WITHOUT pinning a literal version -- a bump
|
|
97
|
+
# (editing MAJOR/MINOR/PATCH in _version.py) never requires touching this
|
|
98
|
+
# test. We assert the constants drive a well-formed base and that every
|
|
99
|
+
# derived value agrees with it. This still catches a DESYNCED __version__
|
|
100
|
+
# (e.g. constants bumped but the autobump hook hasn't regenerated the
|
|
101
|
+
# string), which is the real failure mode worth guarding.
|
|
96
102
|
import unctools
|
|
97
|
-
|
|
103
|
+
from unctools import _version
|
|
104
|
+
assert isinstance(_version.MAJOR, int)
|
|
105
|
+
assert isinstance(_version.MINOR, int)
|
|
106
|
+
assert isinstance(_version.PATCH, int)
|
|
107
|
+
base = f"{_version.MAJOR}.{_version.MINOR}.{_version.PATCH}"
|
|
108
|
+
assert _version.get_base_version().startswith(base)
|
|
109
|
+
assert _version.PIP_VERSION.startswith(base)
|
|
110
|
+
assert unctools.__version__.startswith(base)
|
|
@@ -44,6 +44,8 @@ TEST_LOCAL_PATH = "C:\\Users\\username\\Documents\\file.txt"
|
|
|
44
44
|
|
|
45
45
|
class TestEnvironment:
|
|
46
46
|
"""Manages a temporary test environment with files."""
|
|
47
|
+
|
|
48
|
+
__test__ = False # fixture helper (see conftest `env`), not a pytest test class
|
|
47
49
|
|
|
48
50
|
def __init__(self):
|
|
49
51
|
"""Initialize the test environment."""
|