winiutils 1.1.4__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.
- winiutils-1.1.4/LICENSE +21 -0
- winiutils-1.1.4/PKG-INFO +443 -0
- winiutils-1.1.4/README.md +422 -0
- winiutils-1.1.4/pyproject.toml +74 -0
- winiutils-1.1.4/winiutils/__init__.py +1 -0
- winiutils-1.1.4/winiutils/dev/__init__.py +1 -0
- winiutils-1.1.4/winiutils/dev/artifacts/__init__.py +1 -0
- winiutils-1.1.4/winiutils/dev/artifacts/builder/__init__.py +1 -0
- winiutils-1.1.4/winiutils/dev/artifacts/builder/builder.py +4 -0
- winiutils-1.1.4/winiutils/dev/cli/__init__.py +1 -0
- winiutils-1.1.4/winiutils/dev/cli/subcommands.py +6 -0
- winiutils-1.1.4/winiutils/dev/configs/__init__.py +1 -0
- winiutils-1.1.4/winiutils/dev/configs/configs.py +4 -0
- winiutils-1.1.4/winiutils/main.py +19 -0
- winiutils-1.1.4/winiutils/py.typed +0 -0
- winiutils-1.1.4/winiutils/src/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/data/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/data/dataframe/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/data/dataframe/cleaning.py +620 -0
- winiutils-1.1.4/winiutils/src/data/structures/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/data/structures/dicts.py +16 -0
- winiutils-1.1.4/winiutils/src/data/structures/text/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/data/structures/text/string.py +100 -0
- winiutils-1.1.4/winiutils/src/iterating/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/iterating/concurrent/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/iterating/concurrent/concurrent.py +251 -0
- winiutils-1.1.4/winiutils/src/iterating/concurrent/multiprocessing.py +129 -0
- winiutils-1.1.4/winiutils/src/iterating/concurrent/multithreading.py +93 -0
- winiutils-1.1.4/winiutils/src/iterating/iterate.py +29 -0
- winiutils-1.1.4/winiutils/src/oop/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/oop/mixins/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/oop/mixins/meta.py +171 -0
- winiutils-1.1.4/winiutils/src/oop/mixins/mixin.py +26 -0
- winiutils-1.1.4/winiutils/src/resources/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/delete_garbage_can.svg +2 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/download_arrow.svg +3 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/exit_fullscreen_icon.svg +7 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/fullscreen_icon.svg +4 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/menu_icon.svg +4 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/pause_icon.svg +4 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/play_icon.svg +17 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/plus_icon.svg +24 -0
- winiutils-1.1.4/winiutils/src/resources/svgs/svg.py +15 -0
- winiutils-1.1.4/winiutils/src/security/__init__.py +1 -0
- winiutils-1.1.4/winiutils/src/security/cryptography.py +29 -0
- winiutils-1.1.4/winiutils/src/security/keyring.py +70 -0
winiutils-1.1.4/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Winipedia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
winiutils-1.1.4/PKG-INFO
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: winiutils
|
|
3
|
+
Version: 1.1.4
|
|
4
|
+
Summary: A package with many utility functions
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Winipedia
|
|
8
|
+
Author-email: win.steveker@gmx.de
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Dist: cryptography
|
|
15
|
+
Requires-Dist: defusedxml
|
|
16
|
+
Requires-Dist: keyring
|
|
17
|
+
Requires-Dist: polars
|
|
18
|
+
Requires-Dist: pyrig
|
|
19
|
+
Requires-Dist: tqdm
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# winiutils
|
|
23
|
+
|
|
24
|
+
(This project uses [pyrig](https://github.com/Winipedia/pyrig))
|
|
25
|
+
|
|
26
|
+
## Overview
|
|
27
|
+
|
|
28
|
+
**winiutils** is a comprehensive Python utility library providing production-ready tools for data processing, concurrent execution, security, and object-oriented programming patterns. Built with strict type safety and code quality standards, it offers reusable components for common development tasks.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
### DataFrame Cleaning Pipeline
|
|
33
|
+
|
|
34
|
+
The `CleaningDF` abstract base class provides a production-ready, extensible framework for cleaning and standardizing Polars DataFrames. It implements a comprehensive 8-step cleaning pipeline:
|
|
35
|
+
|
|
36
|
+
**Pipeline Stages:**
|
|
37
|
+
1. **Column Renaming** - Standardize column names from raw input
|
|
38
|
+
2. **Column Dropping** - Remove columns not in schema
|
|
39
|
+
3. **Null Filling** - Fill null values with configurable defaults
|
|
40
|
+
4. **Type Conversion** - Convert to correct data types with custom transformations
|
|
41
|
+
5. **Null Subset Dropping** - Remove rows where specified column groups are all null
|
|
42
|
+
6. **Duplicate Handling** - Aggregate duplicate rows and sum specified columns
|
|
43
|
+
7. **Sorting** - Multi-column sorting with per-column direction control
|
|
44
|
+
8. **Validation** - Enforce data quality (correct dtypes, no nulls in required columns, no NaN values)
|
|
45
|
+
|
|
46
|
+
**Key Features:**
|
|
47
|
+
- **Abstract Configuration**: 10 abstract methods for complete customization
|
|
48
|
+
- `get_rename_map()` - Column name standardization
|
|
49
|
+
- `get_col_dtype_map()` - Type schema definition
|
|
50
|
+
- `get_fill_null_map()` - Null value defaults
|
|
51
|
+
- `get_col_converter_map()` - Custom column transformations
|
|
52
|
+
- `get_drop_null_subsets()` - Row deletion rules
|
|
53
|
+
- `get_unique_subsets()` - Duplicate detection criteria
|
|
54
|
+
- `get_add_on_duplicate_cols()` - Columns to aggregate on duplicates
|
|
55
|
+
- `get_sort_cols()` - Sort order specification
|
|
56
|
+
- `get_no_null_cols()` - Required non-null columns
|
|
57
|
+
- `get_col_precision_map()` - Float rounding precision
|
|
58
|
+
|
|
59
|
+
- **Advanced Features**:
|
|
60
|
+
- **Kahan Summation**: Compensated rounding for floats to prevent accumulation errors
|
|
61
|
+
- **Automatic Logging**: Built-in method logging via `ABCLoggingMixin`
|
|
62
|
+
- **Type Safety**: Full Polars type enforcement with validation
|
|
63
|
+
- **NaN Handling**: Automatic NaN to null conversion
|
|
64
|
+
- **Duplicate Aggregation**: Sum values when merging duplicate rows
|
|
65
|
+
- **Standard Conversions**: Auto-strip strings, auto-round floats
|
|
66
|
+
|
|
67
|
+
**Usage Pattern:**
|
|
68
|
+
```python
|
|
69
|
+
class MyDataCleaner(CleaningDF):
|
|
70
|
+
# Define column constants
|
|
71
|
+
USER_ID = "user_id"
|
|
72
|
+
EMAIL = "email"
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def get_rename_map(cls) -> dict[str, str]:
|
|
76
|
+
return {cls.USER_ID: "UserId", cls.EMAIL: "Email_Address"}
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def get_col_dtype_map(cls) -> dict[str, type[pl.DataType]]:
|
|
80
|
+
return {cls.USER_ID: pl.Int64, cls.EMAIL: pl.Utf8}
|
|
81
|
+
|
|
82
|
+
# ... implement other abstract methods
|
|
83
|
+
|
|
84
|
+
# Use it
|
|
85
|
+
cleaned_data = MyDataCleaner(raw_dataframe)
|
|
86
|
+
result_df = cleaned_data.df
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Best For:**
|
|
90
|
+
- ETL pipelines requiring consistent data quality
|
|
91
|
+
- Data standardization before database loading
|
|
92
|
+
- Building composable data cleaning workflows
|
|
93
|
+
- Projects requiring audit trails (automatic logging)
|
|
94
|
+
|
|
95
|
+
### Concurrent Processing
|
|
96
|
+
|
|
97
|
+
A unified, intelligent framework for parallel execution supporting both multiprocessing (CPU-bound) and multithreading (I/O-bound) tasks with automatic resource optimization.
|
|
98
|
+
|
|
99
|
+
**Core Functions:**
|
|
100
|
+
|
|
101
|
+
**`multiprocess_loop()`** - CPU-bound parallel processing
|
|
102
|
+
- Uses `multiprocessing.Pool` with spawn context for true parallelism
|
|
103
|
+
- Bypasses Python's GIL for CPU-intensive tasks
|
|
104
|
+
- Automatic process pool sizing based on CPU count and active processes
|
|
105
|
+
- Deep-copy support for mutable static arguments
|
|
106
|
+
|
|
107
|
+
**`multithread_loop()`** - I/O-bound parallel processing
|
|
108
|
+
- Uses `ThreadPoolExecutor` for concurrent I/O operations
|
|
109
|
+
- Efficient for network requests, file I/O, database queries
|
|
110
|
+
- Automatic thread pool sizing (CPU count × 4)
|
|
111
|
+
- Safe for mutable objects (shared memory)
|
|
112
|
+
|
|
113
|
+
**`cancel_on_timeout()`** - Timeout enforcement
|
|
114
|
+
- Decorator/wrapper for functions that may hang
|
|
115
|
+
- Uses multiprocessing to forcefully terminate on timeout
|
|
116
|
+
- Proper cleanup with process termination and joining
|
|
117
|
+
- Works with pickle-able functions
|
|
118
|
+
|
|
119
|
+
**Key Features:**
|
|
120
|
+
|
|
121
|
+
- **Automatic Worker Optimization**: Calculates optimal pool size based on:
|
|
122
|
+
- Available CPU cores
|
|
123
|
+
- Currently active processes/threads
|
|
124
|
+
- Number of tasks to process
|
|
125
|
+
- Ensures at least 1 worker, prevents oversubscription
|
|
126
|
+
|
|
127
|
+
- **Progress Tracking**: Built-in tqdm integration
|
|
128
|
+
- Real-time progress bars for all parallel operations
|
|
129
|
+
- Descriptive labels showing function name and worker type
|
|
130
|
+
- Accurate task counting
|
|
131
|
+
|
|
132
|
+
- **Order Preservation**: Results returned in original input order
|
|
133
|
+
- Uses internal ordering system with `imap_unordered`
|
|
134
|
+
- Efficient unordered processing with ordered output
|
|
135
|
+
- No manual result sorting required
|
|
136
|
+
|
|
137
|
+
- **Flexible Argument Handling**:
|
|
138
|
+
- `process_args`: Variable arguments per task (iterable of iterables)
|
|
139
|
+
- `process_args_static`: Shared arguments across all tasks
|
|
140
|
+
- `deepcopy_static_args`: Arguments deep-copied per process (for mutables)
|
|
141
|
+
- `process_args_len`: Optional length hint for optimization
|
|
142
|
+
|
|
143
|
+
- **Smart Execution**: Single unified `concurrent_loop()` backend
|
|
144
|
+
- Automatically selects map/imap_unordered based on task count
|
|
145
|
+
- Handles both Pool and ThreadPoolExecutor transparently
|
|
146
|
+
- Consistent API regardless of concurrency type
|
|
147
|
+
|
|
148
|
+
**Usage Examples:**
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from winiutils.src.iterating.concurrent.multiprocessing import multiprocess_loop
|
|
152
|
+
from winiutils.src.iterating.concurrent.multithreading import multithread_loop
|
|
153
|
+
from winiutils.src.iterating.concurrent.multiprocessing import cancel_on_timeout
|
|
154
|
+
|
|
155
|
+
# CPU-bound: Process large datasets in parallel
|
|
156
|
+
def process_data(data_chunk, config):
|
|
157
|
+
# Heavy computation
|
|
158
|
+
return analyzed_data
|
|
159
|
+
|
|
160
|
+
results = multiprocess_loop(
|
|
161
|
+
process_function=process_data,
|
|
162
|
+
process_args=[(chunk,) for chunk in data_chunks],
|
|
163
|
+
process_args_static=(config,),
|
|
164
|
+
process_args_len=len(data_chunks)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# I/O-bound: Fetch multiple URLs concurrently
|
|
168
|
+
def fetch_url(url, headers):
|
|
169
|
+
return requests.get(url, headers=headers)
|
|
170
|
+
|
|
171
|
+
responses = multithread_loop(
|
|
172
|
+
process_function=fetch_url,
|
|
173
|
+
process_args=[(url,) for url in urls],
|
|
174
|
+
process_args_static=(headers,),
|
|
175
|
+
process_args_len=len(urls)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Timeout enforcement for blocking operations
|
|
179
|
+
@cancel_on_timeout(seconds=5, message="User input timeout")
|
|
180
|
+
def get_user_input():
|
|
181
|
+
return input("Enter value: ")
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
user_value = get_user_input()
|
|
185
|
+
except multiprocessing.TimeoutError:
|
|
186
|
+
user_value = "default"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Architecture Highlights:**
|
|
190
|
+
|
|
191
|
+
- **Spawn Context**: Uses `spawn` instead of `fork` for safer multiprocessing
|
|
192
|
+
- **Context Managers**: Proper resource cleanup with `with` statements
|
|
193
|
+
- **Type Safety**: Full type hints for all functions and parameters
|
|
194
|
+
- **Logging**: Integrated logging for pool size decisions and execution flow
|
|
195
|
+
- **Error Handling**: Graceful handling of timeouts and process failures
|
|
196
|
+
|
|
197
|
+
**Best For:**
|
|
198
|
+
- Parallel data processing pipelines
|
|
199
|
+
- Batch API requests or database queries
|
|
200
|
+
- CPU-intensive computations (image processing, ML inference)
|
|
201
|
+
- Operations requiring timeout enforcement
|
|
202
|
+
- Applications needing automatic resource management
|
|
203
|
+
|
|
204
|
+
### Object-Oriented Programming Utilities
|
|
205
|
+
|
|
206
|
+
Advanced metaclasses and mixins for automatic method instrumentation and class composition using the mixin pattern.
|
|
207
|
+
|
|
208
|
+
**Core Components:**
|
|
209
|
+
|
|
210
|
+
**`ABCLoggingMeta`** - Metaclass for automatic method logging
|
|
211
|
+
- Extends `ABCMeta` to combine abstract class enforcement with logging
|
|
212
|
+
- Automatically wraps all non-magic methods with logging decorators
|
|
213
|
+
- Supports `classmethod`, `staticmethod`, and instance methods
|
|
214
|
+
- Zero boilerplate - just use as metaclass
|
|
215
|
+
|
|
216
|
+
**`ABCLoggingMixin`** - Ready-to-use mixin class
|
|
217
|
+
- Pre-configured with `ABCLoggingMeta` metaclass
|
|
218
|
+
- Inherit to add automatic logging to any class
|
|
219
|
+
- Combines well with other mixins and base classes
|
|
220
|
+
|
|
221
|
+
**Logging Features:**
|
|
222
|
+
|
|
223
|
+
- **Automatic Instrumentation**: All methods automatically logged without decorators
|
|
224
|
+
- **Performance Tracking**: Measures and logs execution time for each method call
|
|
225
|
+
- **Argument Logging**: Captures and logs method arguments (truncated for readability)
|
|
226
|
+
- **Return Value Logging**: Logs method return values (truncated)
|
|
227
|
+
- **Rate Limiting**: Intelligent throttling to prevent log spam
|
|
228
|
+
- Only logs if >1 second since last call to same method
|
|
229
|
+
- Prevents flooding logs in tight loops
|
|
230
|
+
- Per-method tracking (not global)
|
|
231
|
+
- **Truncation**: Arguments and returns truncated to 20 characters max
|
|
232
|
+
- **Magic Method Exclusion**: Skips `__init__`, `__str__`, etc. to avoid noise
|
|
233
|
+
|
|
234
|
+
**How It Works:**
|
|
235
|
+
|
|
236
|
+
The metaclass intercepts class creation and wraps methods at definition time:
|
|
237
|
+
1. Iterates through all class attributes during `__new__`
|
|
238
|
+
2. Identifies callable, non-magic methods
|
|
239
|
+
3. Wraps each with a logging decorator that:
|
|
240
|
+
- Tracks call times per method
|
|
241
|
+
- Logs method name, class name, arguments, kwargs
|
|
242
|
+
- Executes the original method
|
|
243
|
+
- Logs execution duration and return value
|
|
244
|
+
- Updates last call time for rate limiting
|
|
245
|
+
|
|
246
|
+
**Usage Examples:**
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from winiutils.src.oop.mixins.mixin import ABCLoggingMixin
|
|
250
|
+
from abc import abstractmethod
|
|
251
|
+
|
|
252
|
+
# Option 1: Use the mixin
|
|
253
|
+
class MyService(ABCLoggingMixin):
|
|
254
|
+
def process_data(self, data: list) -> dict:
|
|
255
|
+
# Automatically logged with timing
|
|
256
|
+
return {"processed": len(data)}
|
|
257
|
+
|
|
258
|
+
@classmethod
|
|
259
|
+
def validate(cls, value: str) -> bool:
|
|
260
|
+
# Classmethods also logged
|
|
261
|
+
return len(value) > 0
|
|
262
|
+
|
|
263
|
+
# Option 2: Use the metaclass directly
|
|
264
|
+
from winiutils.src.oop.mixins.meta import ABCLoggingMeta
|
|
265
|
+
|
|
266
|
+
class MyAbstractService(metaclass=ABCLoggingMeta):
|
|
267
|
+
@abstractmethod
|
|
268
|
+
def execute(self) -> None:
|
|
269
|
+
pass
|
|
270
|
+
|
|
271
|
+
class ConcreteService(MyAbstractService):
|
|
272
|
+
def execute(self) -> None:
|
|
273
|
+
# Automatically logged
|
|
274
|
+
print("Executing...")
|
|
275
|
+
|
|
276
|
+
# Usage - logging happens automatically
|
|
277
|
+
service = MyService()
|
|
278
|
+
result = service.process_data([1, 2, 3])
|
|
279
|
+
# Logs: "MyService - Calling process_data with ([1, 2, 3],) and {}"
|
|
280
|
+
# Logs: "MyService - process_data finished with 0.001 seconds -> returning {'processed': 3}"
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Log Output Example:**
|
|
284
|
+
```
|
|
285
|
+
INFO - MyService - Calling process_data with ([1, 2, 3],) and {}
|
|
286
|
+
INFO - MyService - process_data finished with 0.001234 seconds -> returning {'processed': 3}
|
|
287
|
+
INFO - MyService - Calling validate with ('test',) and {}
|
|
288
|
+
INFO - MyService - validate finished with 0.000123 seconds -> returning True
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Technical Details:**
|
|
292
|
+
|
|
293
|
+
- **Metaclass Inheritance**: Properly extends `ABCMeta` for abstract class support
|
|
294
|
+
- **Decorator Preservation**: Uses `@wraps` to maintain function metadata
|
|
295
|
+
- **Performance**: Minimal overhead - caches `time.time` function reference
|
|
296
|
+
- **Thread Safety**: Each method has independent call time tracking
|
|
297
|
+
- **Memory Efficient**: Call times stored in closure, not instance attributes
|
|
298
|
+
|
|
299
|
+
**Integration with Other Utilities:**
|
|
300
|
+
|
|
301
|
+
The `CleaningDF` class uses `ABCLoggingMixin` to automatically log all cleaning operations:
|
|
302
|
+
```python
|
|
303
|
+
class CleaningDF(ABCLoggingMixin):
|
|
304
|
+
# All methods (rename_cols, fill_nulls, etc.) automatically logged
|
|
305
|
+
# Provides audit trail of data cleaning pipeline
|
|
306
|
+
pass
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Best For:**
|
|
310
|
+
- Debugging complex class hierarchies
|
|
311
|
+
- Performance profiling during development
|
|
312
|
+
- Audit trails for data processing pipelines
|
|
313
|
+
- Monitoring service method execution
|
|
314
|
+
- Classes with abstract methods requiring implementation tracking
|
|
315
|
+
- Reducing logging boilerplate in large codebases
|
|
316
|
+
|
|
317
|
+
### Security Utilities
|
|
318
|
+
|
|
319
|
+
Production-ready cryptography and secure credential storage utilities built on industry-standard libraries (`cryptography` and `keyring`).
|
|
320
|
+
|
|
321
|
+
**Cryptography Module** (`winiutils.src.security.cryptography`)
|
|
322
|
+
|
|
323
|
+
**AES-GCM Encryption/Decryption** - Authenticated encryption with proper IV handling
|
|
324
|
+
|
|
325
|
+
**`encrypt_with_aes_gcm(aes_gcm, data, aad=None)`**
|
|
326
|
+
- Encrypts data using AES-GCM (Galois/Counter Mode)
|
|
327
|
+
- Generates random 12-byte IV for each encryption
|
|
328
|
+
- Prepends IV to ciphertext for easy decryption
|
|
329
|
+
- Optional Additional Authenticated Data (AAD) support
|
|
330
|
+
- Returns: `IV + encrypted_data` as single bytes object
|
|
331
|
+
|
|
332
|
+
**`decrypt_with_aes_gcm(aes_gcm, data, aad=None)`**
|
|
333
|
+
- Decrypts AES-GCM encrypted data
|
|
334
|
+
- Automatically extracts IV from first 12 bytes
|
|
335
|
+
- Validates authentication tag (prevents tampering)
|
|
336
|
+
- Optional AAD support (must match encryption AAD)
|
|
337
|
+
- Returns: Original plaintext as bytes
|
|
338
|
+
|
|
339
|
+
**Key Features:**
|
|
340
|
+
- **Authenticated Encryption**: AES-GCM provides both confidentiality and integrity
|
|
341
|
+
- **Random IVs**: Each encryption uses a unique, cryptographically random IV
|
|
342
|
+
- **No IV Management**: IV automatically prepended/extracted - no separate storage needed
|
|
343
|
+
- **AAD Support**: Authenticate additional data without encrypting it
|
|
344
|
+
- **Standard Compliance**: Uses `cryptography` library's AEAD implementation
|
|
345
|
+
|
|
346
|
+
**Keyring Module** (`winiutils.src.security.keyring`)
|
|
347
|
+
|
|
348
|
+
**Secure Key Storage** - System keyring integration for credential management
|
|
349
|
+
|
|
350
|
+
**`get_or_create_fernet(service_name, username)`**
|
|
351
|
+
- Retrieves or generates Fernet symmetric encryption key
|
|
352
|
+
- Stores key securely in system keyring (OS-level security)
|
|
353
|
+
- Returns: `(Fernet instance, raw_key_bytes)` tuple
|
|
354
|
+
- Automatic base64 encoding for keyring storage
|
|
355
|
+
|
|
356
|
+
**`get_or_create_aes_gcm(service_name, username)`**
|
|
357
|
+
- Retrieves or generates 256-bit AES-GCM key
|
|
358
|
+
- Stores key securely in system keyring
|
|
359
|
+
- Returns: `(AESGCM instance, raw_key_bytes)` tuple
|
|
360
|
+
- Uses `AESGCM.generate_key(bit_length=256)`
|
|
361
|
+
|
|
362
|
+
**`get_or_create_key(service_name, username, key_class, generate_key_func)`**
|
|
363
|
+
- Generic key retrieval/creation function
|
|
364
|
+
- Supports any key type with custom generation function
|
|
365
|
+
- Service name automatically modified with key class name
|
|
366
|
+
- Base64 encoding for safe string storage
|
|
367
|
+
- Type-safe with generic type parameter
|
|
368
|
+
|
|
369
|
+
**Key Features:**
|
|
370
|
+
- **System Keyring**: Uses OS-native credential storage (Keychain on macOS, Credential Manager on Windows, Secret Service on Linux)
|
|
371
|
+
- **Automatic Generation**: Creates keys on first use if not found
|
|
372
|
+
- **Type Safety**: Generic type parameters for key class
|
|
373
|
+
- **Service Namespacing**: Prevents key collisions with class-based naming
|
|
374
|
+
- **Base64 Encoding**: Safe storage of binary keys as strings
|
|
375
|
+
- **Lazy Initialization**: Keys only generated when needed
|
|
376
|
+
|
|
377
|
+
**Usage Examples:**
|
|
378
|
+
|
|
379
|
+
```python
|
|
380
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
381
|
+
from winiutils.src.security.keyring import get_or_create_aes_gcm
|
|
382
|
+
from winiutils.src.security.cryptography import encrypt_with_aes_gcm, decrypt_with_aes_gcm
|
|
383
|
+
|
|
384
|
+
# Get or create encryption key (stored in system keyring)
|
|
385
|
+
aes_gcm, raw_key = get_or_create_aes_gcm(
|
|
386
|
+
service_name="my_app",
|
|
387
|
+
username="user@example.com"
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Encrypt sensitive data
|
|
391
|
+
plaintext = b"Secret message"
|
|
392
|
+
aad = b"metadata" # Optional authenticated data
|
|
393
|
+
encrypted = encrypt_with_aes_gcm(aes_gcm, plaintext, aad)
|
|
394
|
+
|
|
395
|
+
# Decrypt data
|
|
396
|
+
decrypted = decrypt_with_aes_gcm(aes_gcm, encrypted, aad)
|
|
397
|
+
assert decrypted == plaintext
|
|
398
|
+
|
|
399
|
+
# Using Fernet (simpler, includes timestamp)
|
|
400
|
+
from cryptography.fernet import Fernet
|
|
401
|
+
from winiutils.src.security.keyring import get_or_create_fernet
|
|
402
|
+
|
|
403
|
+
fernet, key = get_or_create_fernet("my_app", "user@example.com")
|
|
404
|
+
token = fernet.encrypt(b"Secret data")
|
|
405
|
+
original = fernet.decrypt(token)
|
|
406
|
+
|
|
407
|
+
# Custom key type
|
|
408
|
+
from winiutils.src.security.keyring import get_or_create_key
|
|
409
|
+
|
|
410
|
+
custom_cipher, key = get_or_create_key(
|
|
411
|
+
service_name="my_app",
|
|
412
|
+
username="admin",
|
|
413
|
+
key_class=AESGCM,
|
|
414
|
+
generate_key_func=lambda: AESGCM.generate_key(bit_length=128)
|
|
415
|
+
)
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Security Best Practices:**
|
|
419
|
+
|
|
420
|
+
- **Key Storage**: Never hardcode keys - always use keyring
|
|
421
|
+
- **IV Uniqueness**: Never reuse IVs with the same key (handled automatically)
|
|
422
|
+
- **AAD Usage**: Use AAD for context binding (e.g., user ID, timestamp)
|
|
423
|
+
- **Key Rotation**: Periodically regenerate keys and re-encrypt data
|
|
424
|
+
- **Access Control**: Limit keyring access to authorized users only
|
|
425
|
+
|
|
426
|
+
**Architecture Highlights:**
|
|
427
|
+
|
|
428
|
+
- **Service Name Modification**: Appends key class name to prevent collisions
|
|
429
|
+
- `"my_app"` + `Fernet` → `"my_app_Fernet"`
|
|
430
|
+
- `"my_app"` + `AESGCM` → `"my_app_AESGCM"`
|
|
431
|
+
- **Idempotent**: Multiple calls return same key (no regeneration)
|
|
432
|
+
- **Cross-Platform**: Works on Windows, macOS, Linux via `keyring` library
|
|
433
|
+
- **No Database**: Keys stored in OS credential manager, not files
|
|
434
|
+
- **Separation of Concerns**: Cryptography operations separate from key management
|
|
435
|
+
|
|
436
|
+
**Best For:**
|
|
437
|
+
- Encrypting sensitive application data (passwords, tokens, PII)
|
|
438
|
+
- Secure configuration file encryption
|
|
439
|
+
- Database credential protection
|
|
440
|
+
- API key storage and retrieval
|
|
441
|
+
- Multi-user applications requiring per-user encryption
|
|
442
|
+
- Applications requiring OS-level security integration
|
|
443
|
+
- Compliance with data protection regulations (GDPR, HIPAA)
|