splurge-dsv 2025.1.4__py3-none-any.whl → 2025.2.0__py3-none-any.whl
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.
- splurge_dsv/__init__.py +70 -58
- splurge_dsv/__main__.py +10 -6
- splurge_dsv/cli.py +56 -46
- splurge_dsv/dsv.py +256 -0
- splurge_dsv/dsv_helper.py +63 -52
- splurge_dsv/exceptions.py +92 -75
- splurge_dsv/path_validator.py +49 -36
- splurge_dsv/safe_text_file_reader.py +177 -0
- splurge_dsv/safe_text_file_writer.py +136 -0
- splurge_dsv/string_tokenizer.py +34 -26
- splurge_dsv/text_file_helper.py +96 -177
- splurge_dsv-2025.2.0.dist-info/METADATA +217 -0
- splurge_dsv-2025.2.0.dist-info/RECORD +17 -0
- splurge_dsv-2025.2.0.dist-info/entry_points.txt +2 -0
- splurge_dsv/resource_manager.py +0 -371
- splurge_dsv-2025.1.4.dist-info/METADATA +0 -263
- splurge_dsv-2025.1.4.dist-info/RECORD +0 -14
- {splurge_dsv-2025.1.4.dist-info → splurge_dsv-2025.2.0.dist-info}/WHEEL +0 -0
- {splurge_dsv-2025.1.4.dist-info → splurge_dsv-2025.2.0.dist-info}/licenses/LICENSE +0 -0
- {splurge_dsv-2025.1.4.dist-info → splurge_dsv-2025.2.0.dist-info}/top_level.txt +0 -0
splurge_dsv/resource_manager.py
DELETED
@@ -1,371 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Resource management utilities with context managers.
|
3
|
-
|
4
|
-
This module provides context managers and resource management utilities
|
5
|
-
for safe handling of file operations, streams, and other resources.
|
6
|
-
|
7
|
-
Copyright (c) 2025 Jim Schilling
|
8
|
-
|
9
|
-
Please preserve this header and all related material when sharing!
|
10
|
-
|
11
|
-
This module is licensed under the MIT License.
|
12
|
-
"""
|
13
|
-
|
14
|
-
# Standard library imports
|
15
|
-
from collections.abc import Iterator
|
16
|
-
from contextlib import contextmanager
|
17
|
-
from pathlib import Path
|
18
|
-
from typing import IO, Any
|
19
|
-
|
20
|
-
# Local imports
|
21
|
-
from splurge_dsv.exceptions import (
|
22
|
-
SplurgeFileEncodingError,
|
23
|
-
SplurgeFileNotFoundError,
|
24
|
-
SplurgeFilePermissionError,
|
25
|
-
SplurgeResourceAcquisitionError,
|
26
|
-
SplurgeResourceReleaseError,
|
27
|
-
)
|
28
|
-
from splurge_dsv.path_validator import PathValidator
|
29
|
-
|
30
|
-
# Module-level constants for resource management
|
31
|
-
DEFAULT_BUFFERING = -1 # Default buffering for file operations
|
32
|
-
DEFAULT_ENCODING = "utf-8" # Default text encoding
|
33
|
-
DEFAULT_MODE = "r" # Default file mode for reading
|
34
|
-
|
35
|
-
|
36
|
-
def _safe_open_file(
|
37
|
-
file_path: Path,
|
38
|
-
*,
|
39
|
-
mode: str,
|
40
|
-
encoding: str | None = None,
|
41
|
-
errors: str | None = None,
|
42
|
-
newline: str | None = None,
|
43
|
-
buffering: int = DEFAULT_BUFFERING,
|
44
|
-
) -> IO[Any]:
|
45
|
-
"""
|
46
|
-
Safely open a file with proper error handling.
|
47
|
-
|
48
|
-
This function provides centralized file opening with consistent error handling
|
49
|
-
that converts standard file operation exceptions to custom exceptions.
|
50
|
-
|
51
|
-
Args:
|
52
|
-
file_path: Path to the file
|
53
|
-
mode: File open mode
|
54
|
-
encoding: Text encoding (for text mode)
|
55
|
-
errors: Error handling for encoding
|
56
|
-
newline: Newline handling
|
57
|
-
buffering: Buffer size
|
58
|
-
|
59
|
-
Returns:
|
60
|
-
File handle
|
61
|
-
|
62
|
-
Raises:
|
63
|
-
SplurgeFileNotFoundError: If file is not found
|
64
|
-
SplurgeFilePermissionError: If permission is denied
|
65
|
-
SplurgeFileEncodingError: If encoding error occurs
|
66
|
-
SplurgeResourceAcquisitionError: If other file operation fails
|
67
|
-
"""
|
68
|
-
try:
|
69
|
-
if "b" in mode:
|
70
|
-
# Binary mode
|
71
|
-
return open(file_path, mode=mode, buffering=buffering)
|
72
|
-
else:
|
73
|
-
# Text mode
|
74
|
-
return open(file_path, mode=mode, encoding=encoding, errors=errors, newline=newline, buffering=buffering)
|
75
|
-
except FileNotFoundError as e:
|
76
|
-
raise SplurgeFileNotFoundError(f"File not found: {file_path}", details=str(e)) from e
|
77
|
-
except PermissionError as e:
|
78
|
-
raise SplurgeFilePermissionError(f"Permission denied: {file_path}", details=str(e)) from e
|
79
|
-
except UnicodeDecodeError as e:
|
80
|
-
raise SplurgeFileEncodingError(f"Encoding error reading file: {file_path}", details=str(e)) from e
|
81
|
-
except OSError as e:
|
82
|
-
raise SplurgeResourceAcquisitionError(f"Failed to open file: {file_path}", details=str(e)) from e
|
83
|
-
|
84
|
-
|
85
|
-
class ResourceManager:
|
86
|
-
"""
|
87
|
-
Generic resource manager that implements the ResourceManagerProtocol.
|
88
|
-
|
89
|
-
This class provides a base implementation for resource management
|
90
|
-
with acquire/release semantics.
|
91
|
-
"""
|
92
|
-
|
93
|
-
def __init__(self) -> None:
|
94
|
-
"""Initialize the resource manager."""
|
95
|
-
self._resource: Any | None = None
|
96
|
-
self._is_acquired_flag: bool = False
|
97
|
-
|
98
|
-
def acquire(self) -> Any:
|
99
|
-
"""
|
100
|
-
Acquire the managed resource.
|
101
|
-
|
102
|
-
Returns:
|
103
|
-
The acquired resource
|
104
|
-
|
105
|
-
Raises:
|
106
|
-
NotImplementedError: If _create_resource is not implemented by subclass
|
107
|
-
SplurgeResourceAcquisitionError: If resource cannot be acquired
|
108
|
-
"""
|
109
|
-
if self._is_acquired_flag:
|
110
|
-
raise SplurgeResourceAcquisitionError(
|
111
|
-
"Resource is already acquired", details="Cannot acquire resource that is already in use"
|
112
|
-
)
|
113
|
-
|
114
|
-
try:
|
115
|
-
self._resource = self._create_resource()
|
116
|
-
self._is_acquired_flag = True
|
117
|
-
return self._resource
|
118
|
-
except NotImplementedError:
|
119
|
-
# Re-raise NotImplementedError without wrapping it
|
120
|
-
raise
|
121
|
-
except Exception as e:
|
122
|
-
raise SplurgeResourceAcquisitionError("Failed to acquire resource", details=str(e)) from e
|
123
|
-
|
124
|
-
def release(self) -> None:
|
125
|
-
"""
|
126
|
-
Release the managed resource.
|
127
|
-
|
128
|
-
Raises:
|
129
|
-
SplurgeResourceReleaseError: If resource cannot be released
|
130
|
-
"""
|
131
|
-
if not self._is_acquired_flag:
|
132
|
-
return # Nothing to release
|
133
|
-
|
134
|
-
try:
|
135
|
-
self._cleanup_resource()
|
136
|
-
self._resource = None
|
137
|
-
self._is_acquired_flag = False
|
138
|
-
except Exception as e:
|
139
|
-
raise SplurgeResourceReleaseError("Failed to release resource", details=str(e)) from e
|
140
|
-
|
141
|
-
def is_acquired(self) -> bool:
|
142
|
-
"""
|
143
|
-
Check if the resource is currently acquired.
|
144
|
-
|
145
|
-
Returns:
|
146
|
-
True if resource is acquired, False otherwise
|
147
|
-
"""
|
148
|
-
return self._is_acquired_flag
|
149
|
-
|
150
|
-
def _create_resource(self) -> Any:
|
151
|
-
"""
|
152
|
-
Create the resource to be managed.
|
153
|
-
|
154
|
-
This method should be overridden by subclasses to provide
|
155
|
-
specific resource creation logic.
|
156
|
-
|
157
|
-
Returns:
|
158
|
-
The created resource
|
159
|
-
|
160
|
-
Raises:
|
161
|
-
NotImplementedError: If not overridden by subclass
|
162
|
-
"""
|
163
|
-
raise NotImplementedError("Subclasses must implement _create_resource")
|
164
|
-
|
165
|
-
def _cleanup_resource(self) -> None:
|
166
|
-
"""
|
167
|
-
Clean up the managed resource.
|
168
|
-
|
169
|
-
This method should be overridden by subclasses to provide
|
170
|
-
specific resource cleanup logic.
|
171
|
-
"""
|
172
|
-
if self._resource is not None and hasattr(self._resource, "close"):
|
173
|
-
self._resource.close()
|
174
|
-
|
175
|
-
|
176
|
-
class FileResourceManager:
|
177
|
-
"""
|
178
|
-
Context manager for safe file operations with automatic cleanup.
|
179
|
-
|
180
|
-
This class provides context managers for reading and writing files
|
181
|
-
with proper error handling and resource cleanup.
|
182
|
-
"""
|
183
|
-
|
184
|
-
def __init__(
|
185
|
-
self,
|
186
|
-
file_path: str | Path,
|
187
|
-
*,
|
188
|
-
mode: str = DEFAULT_MODE,
|
189
|
-
encoding: str | None = DEFAULT_ENCODING,
|
190
|
-
errors: str | None = None,
|
191
|
-
newline: str | None = None,
|
192
|
-
buffering: int = DEFAULT_BUFFERING,
|
193
|
-
) -> None:
|
194
|
-
"""
|
195
|
-
Initialize FileResourceManager.
|
196
|
-
|
197
|
-
Args:
|
198
|
-
file_path: Path to the file
|
199
|
-
mode: File open mode ('r', 'w', 'a', etc.)
|
200
|
-
encoding: Text encoding (for text mode)
|
201
|
-
errors: Error handling for encoding
|
202
|
-
newline: Newline handling
|
203
|
-
buffering: Buffer size
|
204
|
-
|
205
|
-
Raises:
|
206
|
-
SplurgePathValidationError: If file path is invalid
|
207
|
-
SplurgeResourceAcquisitionError: If file cannot be opened
|
208
|
-
"""
|
209
|
-
self._file_path = PathValidator.validate_path(
|
210
|
-
file_path, must_exist=(mode in ["r", "rb"]), must_be_file=True, must_be_readable=(mode in ["r", "rb"])
|
211
|
-
)
|
212
|
-
self.mode = mode
|
213
|
-
self.encoding = encoding
|
214
|
-
self.errors = errors
|
215
|
-
self.newline = newline
|
216
|
-
self.buffering = buffering
|
217
|
-
self._file_handle: IO[Any] | None = None
|
218
|
-
|
219
|
-
def __enter__(self) -> IO[Any]:
|
220
|
-
"""
|
221
|
-
Open the file and return the file handle.
|
222
|
-
|
223
|
-
Returns:
|
224
|
-
File handle
|
225
|
-
|
226
|
-
Raises:
|
227
|
-
SplurgeFileNotFoundError: If file is not found
|
228
|
-
SplurgeFilePermissionError: If permission is denied
|
229
|
-
SplurgeFileEncodingError: If encoding error occurs
|
230
|
-
SplurgeResourceAcquisitionError: If other file operation fails
|
231
|
-
"""
|
232
|
-
self._file_handle = _safe_open_file(
|
233
|
-
self.file_path,
|
234
|
-
mode=self.mode,
|
235
|
-
encoding=self.encoding,
|
236
|
-
errors=self.errors,
|
237
|
-
newline=self.newline,
|
238
|
-
buffering=self.buffering,
|
239
|
-
)
|
240
|
-
return self._file_handle
|
241
|
-
|
242
|
-
def __exit__(self, exc_type: type | None, exc_val: Exception | None, exc_tb: Any | None) -> None:
|
243
|
-
"""
|
244
|
-
Close the file handle and cleanup resources.
|
245
|
-
|
246
|
-
Args:
|
247
|
-
exc_type: Exception type if an exception occurred
|
248
|
-
exc_val: Exception value if an exception occurred
|
249
|
-
exc_tb: Exception traceback if an exception occurred
|
250
|
-
"""
|
251
|
-
if self._file_handle is not None:
|
252
|
-
try:
|
253
|
-
self._file_handle.close()
|
254
|
-
except OSError as e:
|
255
|
-
raise SplurgeResourceReleaseError(f"Failed to close file: {self.file_path}", details=str(e)) from e
|
256
|
-
finally:
|
257
|
-
self._file_handle = None
|
258
|
-
|
259
|
-
@property
|
260
|
-
def file_path(self) -> Path | None:
|
261
|
-
"""Get the path of the temporary file."""
|
262
|
-
return self._file_path
|
263
|
-
|
264
|
-
|
265
|
-
class StreamResourceManager:
|
266
|
-
"""
|
267
|
-
Context manager for stream operations.
|
268
|
-
|
269
|
-
This class provides context managers for managing data streams
|
270
|
-
with proper cleanup and error handling.
|
271
|
-
"""
|
272
|
-
|
273
|
-
def __init__(self, stream: Iterator[Any], *, auto_close: bool = True) -> None:
|
274
|
-
"""
|
275
|
-
Initialize StreamResourceManager.
|
276
|
-
|
277
|
-
Args:
|
278
|
-
stream: Iterator to manage
|
279
|
-
auto_close: Whether to automatically close the stream
|
280
|
-
"""
|
281
|
-
self.stream = stream
|
282
|
-
self.auto_close = auto_close
|
283
|
-
self._is_closed = False
|
284
|
-
|
285
|
-
def __enter__(self) -> Iterator[Any]:
|
286
|
-
"""
|
287
|
-
Return the stream.
|
288
|
-
|
289
|
-
Returns:
|
290
|
-
Stream iterator
|
291
|
-
"""
|
292
|
-
return self.stream
|
293
|
-
|
294
|
-
def __exit__(self, exc_type: type | None, exc_val: Exception | None, exc_tb: Any | None) -> None:
|
295
|
-
"""
|
296
|
-
Clean up the stream.
|
297
|
-
|
298
|
-
Args:
|
299
|
-
exc_type: Exception type if an exception occurred
|
300
|
-
exc_val: Exception value if an exception occurred
|
301
|
-
exc_tb: Exception traceback if an exception occurred
|
302
|
-
"""
|
303
|
-
if self.auto_close and hasattr(self.stream, "close"):
|
304
|
-
try:
|
305
|
-
self.stream.close()
|
306
|
-
except Exception as e:
|
307
|
-
raise SplurgeResourceReleaseError("Failed to close stream", details=str(e)) from e
|
308
|
-
|
309
|
-
# Mark as closed after context manager exits, regardless of close method
|
310
|
-
self._is_closed = True
|
311
|
-
|
312
|
-
@property
|
313
|
-
def is_closed(self) -> bool:
|
314
|
-
"""Check if the stream is closed."""
|
315
|
-
return self._is_closed
|
316
|
-
|
317
|
-
|
318
|
-
@contextmanager
|
319
|
-
def safe_file_operation(
|
320
|
-
file_path: str | Path,
|
321
|
-
*,
|
322
|
-
mode: str = DEFAULT_MODE,
|
323
|
-
encoding: str | None = DEFAULT_ENCODING,
|
324
|
-
errors: str | None = None,
|
325
|
-
newline: str | None = None,
|
326
|
-
buffering: int = DEFAULT_BUFFERING,
|
327
|
-
) -> Iterator[IO[Any]]:
|
328
|
-
"""
|
329
|
-
Context manager for safe file operations.
|
330
|
-
|
331
|
-
Args:
|
332
|
-
file_path: Path to the file
|
333
|
-
mode: File open mode
|
334
|
-
encoding: Text encoding (for text mode)
|
335
|
-
errors: Error handling for encoding
|
336
|
-
newline: Newline handling
|
337
|
-
buffering: Buffer size
|
338
|
-
|
339
|
-
Yields:
|
340
|
-
File handle
|
341
|
-
|
342
|
-
Raises:
|
343
|
-
SplurgePathValidationError: If file path is invalid
|
344
|
-
SplurgeResourceAcquisitionError: If file cannot be opened
|
345
|
-
SplurgeResourceReleaseError: If file cannot be closed
|
346
|
-
"""
|
347
|
-
manager = FileResourceManager(
|
348
|
-
file_path, mode=mode, encoding=encoding, errors=errors, newline=newline, buffering=buffering
|
349
|
-
)
|
350
|
-
with manager as file_handle:
|
351
|
-
yield file_handle
|
352
|
-
|
353
|
-
|
354
|
-
@contextmanager
|
355
|
-
def safe_stream_operation(stream: Iterator[Any], *, auto_close: bool = True) -> Iterator[Iterator[Any]]:
|
356
|
-
"""
|
357
|
-
Context manager for safe stream operations.
|
358
|
-
|
359
|
-
Args:
|
360
|
-
stream: Iterator to manage
|
361
|
-
auto_close: Whether to automatically close the stream
|
362
|
-
|
363
|
-
Yields:
|
364
|
-
Stream iterator
|
365
|
-
|
366
|
-
Raises:
|
367
|
-
SplurgeResourceReleaseError: If stream cannot be closed
|
368
|
-
"""
|
369
|
-
manager = StreamResourceManager(stream, auto_close=auto_close)
|
370
|
-
with manager as stream_handle:
|
371
|
-
yield stream_handle
|
@@ -1,263 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: splurge-dsv
|
3
|
-
Version: 2025.1.4
|
4
|
-
Summary: A utility library for working with DSV (Delimited String Values) files
|
5
|
-
Author: Jim Schilling
|
6
|
-
License-Expression: MIT
|
7
|
-
Project-URL: Homepage, https://github.com/jim-schilling/splurge-dsv
|
8
|
-
Project-URL: Repository, https://github.com/jim-schilling/splurge-dsv
|
9
|
-
Project-URL: Documentation, https://github.com/jim-schilling/splurge-dsv#readme
|
10
|
-
Project-URL: Bug Tracker, https://github.com/jim-schilling/splurge-dsv/issues
|
11
|
-
Keywords: dsv,csv,tsv,delimited,parsing,file-processing
|
12
|
-
Classifier: Development Status :: 3 - Alpha
|
13
|
-
Classifier: Intended Audience :: Developers
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
15
|
-
Classifier: Programming Language :: Python :: 3.10
|
16
|
-
Classifier: Programming Language :: Python :: 3.11
|
17
|
-
Classifier: Programming Language :: Python :: 3.12
|
18
|
-
Classifier: Programming Language :: Python :: 3.13
|
19
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
20
|
-
Classifier: Topic :: Text Processing :: Filters
|
21
|
-
Requires-Python: >=3.10
|
22
|
-
Description-Content-Type: text/markdown
|
23
|
-
License-File: LICENSE
|
24
|
-
Provides-Extra: dev
|
25
|
-
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
26
|
-
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
27
|
-
Requires-Dist: pytest-xdist>=3.0.0; extra == "dev"
|
28
|
-
Dynamic: license-file
|
29
|
-
|
30
|
-
# splurge-dsv
|
31
|
-
|
32
|
-
A robust Python library for parsing and processing delimited-separated value (DSV) files with advanced features for data validation, streaming, and error handling.
|
33
|
-
|
34
|
-
## Features
|
35
|
-
|
36
|
-
### 🔧 Core Functionality
|
37
|
-
- **Multi-format DSV Support**: Parse CSV, TSV, pipe-delimited, semicolon-delimited, and custom delimiter files
|
38
|
-
- **Flexible Parsing Options**: Configurable whitespace handling, bookend removal, and encoding support
|
39
|
-
- **Memory-Efficient Streaming**: Process large files without loading entire content into memory
|
40
|
-
- **Header/Footer Skipping**: Skip specified numbers of rows from start or end of files
|
41
|
-
- **Unicode Support**: Full Unicode character and delimiter support
|
42
|
-
|
43
|
-
### 🛡️ Security & Validation
|
44
|
-
- **Path Validation**: Comprehensive file path security validation with traversal attack prevention
|
45
|
-
- **File Permission Checks**: Automatic file accessibility and permission validation
|
46
|
-
- **Encoding Validation**: Robust encoding error detection and handling
|
47
|
-
- **Resource Management**: Automatic file handle cleanup and resource management
|
48
|
-
|
49
|
-
### 📊 Advanced Processing
|
50
|
-
- **Chunked Processing**: Configurable chunk sizes for streaming large datasets
|
51
|
-
- **Mixed Content Handling**: Support for quoted and unquoted values in the same file
|
52
|
-
- **Line Ending Flexibility**: Automatic handling of different line ending formats
|
53
|
-
- **Error Recovery**: Graceful error handling with detailed error messages
|
54
|
-
|
55
|
-
### 🧪 Testing & Quality
|
56
|
-
- **Comprehensive Test Suite**: 250+ tests with 85%+ coverage gate
|
57
|
-
- **Cross-Platform Support**: Tested on Windows, and should pass on Linux and macOS
|
58
|
-
- **Type Safety**: Full type annotations and validation
|
59
|
-
- **Documentation**: Complete API documentation with examples
|
60
|
-
|
61
|
-
## Installation
|
62
|
-
|
63
|
-
```bash
|
64
|
-
pip install splurge-dsv
|
65
|
-
```
|
66
|
-
|
67
|
-
## Quick Start
|
68
|
-
|
69
|
-
### Basic CSV Parsing
|
70
|
-
|
71
|
-
```python
|
72
|
-
from splurge_dsv import DsvHelper
|
73
|
-
|
74
|
-
# Parse a simple CSV string
|
75
|
-
data = DsvHelper.parse("a,b,c", delimiter=",")
|
76
|
-
print(data) # ['a', 'b', 'c']
|
77
|
-
|
78
|
-
# Parse a CSV file
|
79
|
-
rows = DsvHelper.parse_file("data.csv", delimiter=",")
|
80
|
-
for row in rows:
|
81
|
-
print(row) # ['col1', 'col2', 'col3']
|
82
|
-
```
|
83
|
-
|
84
|
-
### Streaming Large Files
|
85
|
-
|
86
|
-
```python
|
87
|
-
from splurge_dsv import DsvHelper
|
88
|
-
|
89
|
-
# Stream a large CSV file in chunks
|
90
|
-
for chunk in DsvHelper.parse_stream("large_file.csv", delimiter=",", chunk_size=1000):
|
91
|
-
for row in chunk:
|
92
|
-
process_row(row)
|
93
|
-
```
|
94
|
-
|
95
|
-
### Advanced Parsing Options
|
96
|
-
|
97
|
-
```python
|
98
|
-
from splurge_dsv import DsvHelper
|
99
|
-
|
100
|
-
# Parse with custom options
|
101
|
-
data = DsvHelper.parse(
|
102
|
-
'"a","b","c"',
|
103
|
-
delimiter=",",
|
104
|
-
bookend='"',
|
105
|
-
strip=True,
|
106
|
-
bookend_strip=True
|
107
|
-
)
|
108
|
-
print(data) # ['a', 'b', 'c']
|
109
|
-
|
110
|
-
# Skip header and footer rows
|
111
|
-
rows = DsvHelper.parse_file(
|
112
|
-
"data.csv",
|
113
|
-
delimiter=",",
|
114
|
-
skip_header_rows=1,
|
115
|
-
skip_footer_rows=2
|
116
|
-
)
|
117
|
-
```
|
118
|
-
|
119
|
-
### Text File Operations
|
120
|
-
|
121
|
-
```python
|
122
|
-
from splurge_dsv import TextFileHelper
|
123
|
-
|
124
|
-
# Count lines in a file
|
125
|
-
line_count = TextFileHelper.line_count("data.txt")
|
126
|
-
|
127
|
-
# Preview first N lines
|
128
|
-
preview = TextFileHelper.preview("data.txt", max_lines=10)
|
129
|
-
|
130
|
-
# Read entire file with options
|
131
|
-
lines = TextFileHelper.read(
|
132
|
-
"data.txt",
|
133
|
-
strip=True,
|
134
|
-
skip_header_rows=1,
|
135
|
-
skip_footer_rows=1
|
136
|
-
)
|
137
|
-
|
138
|
-
# Stream file content
|
139
|
-
for chunk in TextFileHelper.read_as_stream("large_file.txt", chunk_size=500):
|
140
|
-
process_chunk(chunk)
|
141
|
-
```
|
142
|
-
|
143
|
-
### Path Validation
|
144
|
-
|
145
|
-
```python
|
146
|
-
from splurge_dsv import PathValidator
|
147
|
-
|
148
|
-
# Validate a file path
|
149
|
-
valid_path = PathValidator.validate_path(
|
150
|
-
"data.csv",
|
151
|
-
must_exist=True,
|
152
|
-
must_be_file=True,
|
153
|
-
must_be_readable=True
|
154
|
-
)
|
155
|
-
|
156
|
-
# Check if path is safe
|
157
|
-
is_safe = PathValidator.is_safe_path("user_input_path.txt")
|
158
|
-
```
|
159
|
-
|
160
|
-
## API Reference
|
161
|
-
|
162
|
-
### DsvHelper
|
163
|
-
|
164
|
-
Main class for DSV parsing operations.
|
165
|
-
|
166
|
-
#### Methods
|
167
|
-
|
168
|
-
- `parse(content, delimiter, strip=True, bookend=None, bookend_strip=True)` - Parse a single string
|
169
|
-
- `parses(content_list, delimiter, strip=True, bookend=None, bookend_strip=True)` - Parse multiple strings
|
170
|
-
- `parse_file(file_path, delimiter, strip=True, bookend=None, bookend_strip=True, skip_header_rows=0, skip_footer_rows=0, encoding='utf-8')` - Parse a file
|
171
|
-
- `parse_stream(file_path, delimiter, strip=True, bookend=None, bookend_strip=True, skip_header_rows=0, skip_footer_rows=0, encoding='utf-8', chunk_size=500)` - Stream parse a file
|
172
|
-
|
173
|
-
### TextFileHelper
|
174
|
-
|
175
|
-
Utility class for text file operations.
|
176
|
-
|
177
|
-
#### Methods
|
178
|
-
|
179
|
-
- `line_count(file_path, encoding='utf-8')` - Count lines in a file
|
180
|
-
- `preview(file_path, max_lines=100, strip=True, encoding='utf-8', skip_header_rows=0)` - Preview file content
|
181
|
-
- `read(file_path, strip=True, encoding='utf-8', skip_header_rows=0, skip_footer_rows=0)` - Read entire file
|
182
|
-
- `read_as_stream(file_path, strip=True, encoding='utf-8', skip_header_rows=0, skip_footer_rows=0, chunk_size=500)` - Stream read file
|
183
|
-
|
184
|
-
### PathValidator
|
185
|
-
|
186
|
-
Security-focused path validation utilities.
|
187
|
-
|
188
|
-
#### Methods
|
189
|
-
|
190
|
-
- `validate_path(file_path, must_exist=False, must_be_file=False, must_be_readable=False, allow_relative=False, base_directory=None)` - Validate file path
|
191
|
-
- `is_safe_path(file_path)` - Check if path is safe
|
192
|
-
- `sanitize_filename(filename, default_name='file')` - Sanitize filename
|
193
|
-
|
194
|
-
### ResourceManager
|
195
|
-
|
196
|
-
Context managers for safe resource handling.
|
197
|
-
|
198
|
-
#### Classes
|
199
|
-
|
200
|
-
- `FileResourceManager` - Context manager for file operations
|
201
|
-
- `StreamResourceManager` - Context manager for stream operations
|
202
|
-
|
203
|
-
#### Functions
|
204
|
-
|
205
|
-
- `safe_file_operation(file_path, mode='r', encoding='utf-8', ...)` - Safe file operation context manager
|
206
|
-
- `safe_stream_operation(stream, auto_close=True)` - Safe stream operation context manager
|
207
|
-
|
208
|
-
## Error Handling
|
209
|
-
|
210
|
-
The library provides comprehensive error handling with custom exception classes:
|
211
|
-
|
212
|
-
- `SplurgeParameterError` - Invalid parameter values
|
213
|
-
- `SplurgeFileNotFoundError` - File not found
|
214
|
-
- `SplurgeFilePermissionError` - File permission issues
|
215
|
-
- `SplurgeFileEncodingError` - File encoding problems
|
216
|
-
- `SplurgePathValidationError` - Path validation failures
|
217
|
-
- `SplurgeResourceAcquisitionError` - Resource acquisition failures
|
218
|
-
- `SplurgeResourceReleaseError` - Resource cleanup failures
|
219
|
-
|
220
|
-
## Development
|
221
|
-
|
222
|
-
### Running Tests
|
223
|
-
|
224
|
-
```bash
|
225
|
-
# Run all tests
|
226
|
-
pytest tests/ -v
|
227
|
-
|
228
|
-
# Run with coverage
|
229
|
-
pytest tests/ --cov=splurge_dsv --cov-report=html
|
230
|
-
|
231
|
-
# Run specific test file
|
232
|
-
pytest tests/test_dsv_helper.py -v
|
233
|
-
```
|
234
|
-
|
235
|
-
### Code Quality
|
236
|
-
|
237
|
-
The project follows strict coding standards:
|
238
|
-
- PEP 8 compliance
|
239
|
-
- Type annotations for all functions
|
240
|
-
- Google-style docstrings
|
241
|
-
- 85%+ coverage gate enforced via CI
|
242
|
-
- Comprehensive error handling
|
243
|
-
|
244
|
-
## Changelog
|
245
|
-
|
246
|
-
See the [CHANGELOG](CHANGELOG.md) for full release notes.
|
247
|
-
|
248
|
-
## License
|
249
|
-
|
250
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
251
|
-
|
252
|
-
## More Documentation
|
253
|
-
|
254
|
-
- Detailed docs: [docs/README-details.md](docs/README-details.md)
|
255
|
-
- E2E testing coverage: [docs/e2e_testing_coverage.md](docs/e2e_testing_coverage.md)
|
256
|
-
|
257
|
-
## Contributing
|
258
|
-
|
259
|
-
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
260
|
-
|
261
|
-
## Support
|
262
|
-
|
263
|
-
For support, please open an issue on the GitHub repository or contact the maintainers.
|
@@ -1,14 +0,0 @@
|
|
1
|
-
splurge_dsv/__init__.py,sha256=I_BAM3zHWRZLyY5ndCweBCg1w_cbBDZhdKlxUp6Sx8Q,2415
|
2
|
-
splurge_dsv/__main__.py,sha256=CX8zVMaK6vaaCaC396FyXA1E_-jqKz4zh3CVW8mGKac,359
|
3
|
-
splurge_dsv/cli.py,sha256=hcoF2OC3gHrN4SegSUFQeGxHuWWIBAJn-fTNW19KnyA,7261
|
4
|
-
splurge_dsv/dsv_helper.py,sha256=VRq2ejx6y-JBtFKJdAaK-GD4V0eoxZZfmoX0I3CSbDI,9428
|
5
|
-
splurge_dsv/exceptions.py,sha256=cu9Jd2pGhy7GBbbngH6zs0lfZzLp_OvGwbnsG5khp80,3035
|
6
|
-
splurge_dsv/path_validator.py,sha256=RuRFjtHWE1Z5-DlSBBZMoHJegQEFYEQ0HJgN7ndre2k,9969
|
7
|
-
splurge_dsv/resource_manager.py,sha256=g4igv1hhJdPtw4A1P-WdaNKcYMfEGCtZ4xaU4ynVbKw,12045
|
8
|
-
splurge_dsv/string_tokenizer.py,sha256=wBKWdi68rreTqf3RF-8Oxh1nz6QdnbOyWflw2x8pGWY,4022
|
9
|
-
splurge_dsv/text_file_helper.py,sha256=vdhEv9uK0hsNXvTO5SoWwFsI9fPkhdIUXuaJs80DVDA,13573
|
10
|
-
splurge_dsv-2025.1.4.dist-info/licenses/LICENSE,sha256=fPgtg-tIFHinQvJH0arRfv50AuxikD5eHw6rrPy2A5w,1091
|
11
|
-
splurge_dsv-2025.1.4.dist-info/METADATA,sha256=sxdtsP6DfbVRkdVx6LcgcH23DEguS01gniAzN9ELlZM,8573
|
12
|
-
splurge_dsv-2025.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
13
|
-
splurge_dsv-2025.1.4.dist-info/top_level.txt,sha256=D6Si3FTfpRYqH7kzM7tSQAyaKbbraO6UPLpcqcY4XXM,12
|
14
|
-
splurge_dsv-2025.1.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|