nano-dev-utils 1.6.2__tar.gz → 1.7.0__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.
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.gitignore +3 -1
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/workspace.xml +24 -34
- nano_dev_utils-1.7.0/PKG-INFO +180 -0
- nano_dev_utils-1.7.0/README.md +164 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/pyproject.toml +0 -3
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/__init__.py +0 -5
- nano_dev_utils-1.7.0/src/nano_dev_utils/__version__.py +1 -0
- nano_dev_utils-1.7.0/src/nano_dev_utils/_constants.py +1 -0
- nano_dev_utils-1.6.2/PKG-INFO +0 -372
- nano_dev_utils-1.6.2/README.md +0 -356
- nano_dev_utils-1.6.2/benchmark/benchtest.py +0 -52
- nano_dev_utils-1.6.2/benchmark/benchtest_bkp.py +0 -91
- nano_dev_utils-1.6.2/benchmark/win_tree_wrapper.py +0 -52
- nano_dev_utils-1.6.2/src/nano_dev_utils/__main__.py +0 -129
- nano_dev_utils-1.6.2/src/nano_dev_utils/__version__.py +0 -1
- nano_dev_utils-1.6.2/src/nano_dev_utils/_constants.py +0 -2
- nano_dev_utils-1.6.2/src/nano_dev_utils/file_tree_display.py +0 -325
- nano_dev_utils-1.6.2/tests/test_file_tree_display.py +0 -256
- nano_dev_utils-1.6.2/tests/test_timers/__init__.py +0 -3
- nano_dev_utils-1.6.2/update_package.py +0 -19
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/.gitignore +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/encodings.xml +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/misc.xml +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/modules.xml +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/nano_dev_utils.iml +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/vcs.xml +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.pre-commit-config.yaml +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/LICENSE +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/ruff.toml +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/common.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/dynamic_importer.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/release_ports.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/timers.py +0 -0
- {nano_dev_utils-1.6.2/benchmark → nano_dev_utils-1.7.0/tests}/__init__.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_common.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_dynamic_importer.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_release_ports.py +0 -0
- {nano_dev_utils-1.6.2/tests → nano_dev_utils-1.7.0/tests/test_timers}/__init__.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_timers/conftest.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_timers/test_duration_formatter.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_timers/test_timer.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_timers/test_timers_async.py +0 -0
- {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/uv.lock +0 -0
|
@@ -4,9 +4,16 @@
|
|
|
4
4
|
<option name="autoReloadType" value="SELECTIVE" />
|
|
5
5
|
</component>
|
|
6
6
|
<component name="ChangeListManager">
|
|
7
|
-
<list default="true" id="1859e23b-7665-4b92-98cc-65e07a208923" name="Changes" comment="
|
|
8
|
-
<change beforePath="$PROJECT_DIR
|
|
7
|
+
<list default="true" id="1859e23b-7665-4b92-98cc-65e07a208923" name="Changes" comment="minor bugfix (cfg); Bumped minor version v1.6.2">
|
|
8
|
+
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
|
9
|
+
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
|
10
|
+
<change beforePath="$PROJECT_DIR$/pyproject.toml" beforeDir="false" afterPath="$PROJECT_DIR$/pyproject.toml" afterDir="false" />
|
|
11
|
+
<change beforePath="$PROJECT_DIR$/src/nano_dev_utils/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/nano_dev_utils/__init__.py" afterDir="false" />
|
|
12
|
+
<change beforePath="$PROJECT_DIR$/src/nano_dev_utils/__main__.py" beforeDir="false" />
|
|
9
13
|
<change beforePath="$PROJECT_DIR$/src/nano_dev_utils/__version__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/nano_dev_utils/__version__.py" afterDir="false" />
|
|
14
|
+
<change beforePath="$PROJECT_DIR$/src/nano_dev_utils/_constants.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/nano_dev_utils/_constants.py" afterDir="false" />
|
|
15
|
+
<change beforePath="$PROJECT_DIR$/src/nano_dev_utils/file_tree_display.py" beforeDir="false" />
|
|
16
|
+
<change beforePath="$PROJECT_DIR$/tests/test_file_tree_display.py" beforeDir="false" />
|
|
10
17
|
</list>
|
|
11
18
|
<option name="SHOW_DIALOG" value="false" />
|
|
12
19
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
@@ -24,7 +31,7 @@
|
|
|
24
31
|
<option name="PUSH_AUTO_UPDATE" value="true" />
|
|
25
32
|
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
|
26
33
|
<map>
|
|
27
|
-
<entry key="$PROJECT_DIR$" value="
|
|
34
|
+
<entry key="$PROJECT_DIR$" value="master" />
|
|
28
35
|
</map>
|
|
29
36
|
</option>
|
|
30
37
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
@@ -59,7 +66,7 @@
|
|
|
59
66
|
"Python.test_timer.executor": "Run",
|
|
60
67
|
"Python.update_package.executor": "Run",
|
|
61
68
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
62
|
-
"git-widget-placeholder": "
|
|
69
|
+
"git-widget-placeholder": "FI-CLI",
|
|
63
70
|
"ignore.virus.scanning.warn.message": "true",
|
|
64
71
|
"last_opened_file_path": "C:/GitHubWS/nano_dev_utils/src",
|
|
65
72
|
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
|
|
@@ -81,7 +88,7 @@
|
|
|
81
88
|
<recent name="C:\GitHubWS\nano_dev_utils\tests" />
|
|
82
89
|
</key>
|
|
83
90
|
</component>
|
|
84
|
-
<component name="RunManager" selected="Python.
|
|
91
|
+
<component name="RunManager" selected="Python tests.Python tests in tests">
|
|
85
92
|
<configuration name="benchtest_bkp" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
|
86
93
|
<module name="nano_dev_utils" />
|
|
87
94
|
<option name="ENV_FILES" value="" />
|
|
@@ -126,21 +133,6 @@
|
|
|
126
133
|
<option name="INPUT_FILE" value="" />
|
|
127
134
|
<method v="2" />
|
|
128
135
|
</configuration>
|
|
129
|
-
<configuration name="Python tests in test_file_tree_display.py" type="tests" factoryName="Autodetect" temporary="true" nameIsGenerated="true">
|
|
130
|
-
<module name="nano_dev_utils" />
|
|
131
|
-
<option name="ENV_FILES" value="" />
|
|
132
|
-
<option name="INTERPRETER_OPTIONS" value="" />
|
|
133
|
-
<option name="PARENT_ENVS" value="true" />
|
|
134
|
-
<option name="SDK_HOME" value="" />
|
|
135
|
-
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/tests" />
|
|
136
|
-
<option name="IS_MODULE_SDK" value="true" />
|
|
137
|
-
<option name="ADD_CONTENT_ROOTS" value="true" />
|
|
138
|
-
<option name="ADD_SOURCE_ROOTS" value="true" />
|
|
139
|
-
<option name="_new_additionalArguments" value="""" />
|
|
140
|
-
<option name="_new_target" value=""$PROJECT_DIR$/tests/test_file_tree_display.py"" />
|
|
141
|
-
<option name="_new_targetType" value=""PATH"" />
|
|
142
|
-
<method v="2" />
|
|
143
|
-
</configuration>
|
|
144
136
|
<configuration name="Python tests in tests" type="tests" factoryName="Autodetect" temporary="true" nameIsGenerated="true">
|
|
145
137
|
<module name="nano_dev_utils" />
|
|
146
138
|
<option name="ENV_FILES" value="" />
|
|
@@ -159,15 +151,13 @@
|
|
|
159
151
|
<list>
|
|
160
152
|
<item itemvalue="Python.update_package" />
|
|
161
153
|
<item itemvalue="Python.benchtest_bkp" />
|
|
162
|
-
<item itemvalue="Python tests.Python tests in test_file_tree_display.py" />
|
|
163
154
|
<item itemvalue="Python tests.Python tests in tests" />
|
|
164
155
|
</list>
|
|
165
156
|
<recent_temporary>
|
|
166
157
|
<list>
|
|
167
|
-
<item itemvalue="Python.update_package" />
|
|
168
158
|
<item itemvalue="Python tests.Python tests in tests" />
|
|
159
|
+
<item itemvalue="Python.update_package" />
|
|
169
160
|
<item itemvalue="Python.benchtest_bkp" />
|
|
170
|
-
<item itemvalue="Python tests.Python tests in test_file_tree_display.py" />
|
|
171
161
|
</list>
|
|
172
162
|
</recent_temporary>
|
|
173
163
|
</component>
|
|
@@ -187,14 +177,6 @@
|
|
|
187
177
|
<option name="presentableId" value="Default" />
|
|
188
178
|
<updated>1746126505743</updated>
|
|
189
179
|
</task>
|
|
190
|
-
<task id="LOCAL-00098" summary="Readme updates">
|
|
191
|
-
<option name="closed" value="true" />
|
|
192
|
-
<created>1762078800875</created>
|
|
193
|
-
<option name="number" value="00098" />
|
|
194
|
-
<option name="presentableId" value="LOCAL-00098" />
|
|
195
|
-
<option name="project" value="LOCAL" />
|
|
196
|
-
<updated>1762078800875</updated>
|
|
197
|
-
</task>
|
|
198
180
|
<task id="LOCAL-00099" summary="+res_formatter, a wrapper for the private static _duration_formatter.">
|
|
199
181
|
<option name="closed" value="true" />
|
|
200
182
|
<created>1762081915651</created>
|
|
@@ -579,7 +561,15 @@
|
|
|
579
561
|
<option name="project" value="LOCAL" />
|
|
580
562
|
<updated>1762712425856</updated>
|
|
581
563
|
</task>
|
|
582
|
-
<
|
|
564
|
+
<task id="LOCAL-00147" summary="minor bugfix (cfg); Bumped minor version v1.6.2">
|
|
565
|
+
<option name="closed" value="true" />
|
|
566
|
+
<created>1762716808091</created>
|
|
567
|
+
<option name="number" value="00147" />
|
|
568
|
+
<option name="presentableId" value="LOCAL-00147" />
|
|
569
|
+
<option name="project" value="LOCAL" />
|
|
570
|
+
<updated>1762716808091</updated>
|
|
571
|
+
</task>
|
|
572
|
+
<option name="localTasksCounter" value="148" />
|
|
583
573
|
<servers />
|
|
584
574
|
</component>
|
|
585
575
|
<component name="Vcs.Log.Tabs.Properties">
|
|
@@ -636,7 +626,6 @@
|
|
|
636
626
|
</option>
|
|
637
627
|
</component>
|
|
638
628
|
<component name="VcsManagerConfiguration">
|
|
639
|
-
<MESSAGE value="upgrading connector style (WIP)" />
|
|
640
629
|
<MESSAGE value="optimizing performance: -reduced alloc & overhead; -precomputed style dict and sorting outside the recursive _build_tree; -improved string ops: used StringIO for better memory efficiency -improved traversal Unit tests updates" />
|
|
641
630
|
<MESSAGE value="-removing single char old style fallback + unit test for custom styles dynamically added by user" />
|
|
642
631
|
<MESSAGE value="README updates related to FTD" />
|
|
@@ -661,6 +650,7 @@
|
|
|
661
650
|
<MESSAGE value="+ftd cli version" />
|
|
662
651
|
<MESSAGE value="pyproject.toml: dynamic version; ftd cli shortcut; hatch.version path according to __version__ v1.6.0" />
|
|
663
652
|
<MESSAGE value="versioning updates; load_cfg_file minor update; v1.6.1" />
|
|
664
|
-
<
|
|
653
|
+
<MESSAGE value="minor bugfix (cfg); Bumped minor version v1.6.2" />
|
|
654
|
+
<option name="LAST_COMMIT_MESSAGE" value="minor bugfix (cfg); Bumped minor version v1.6.2" />
|
|
665
655
|
</component>
|
|
666
656
|
</project>
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nano_dev_utils
|
|
3
|
+
Version: 1.7.0
|
|
4
|
+
Summary: A collection of small Python utilities for developers.
|
|
5
|
+
Project-URL: Homepage, https://github.com/yaronday/nano_utils
|
|
6
|
+
Project-URL: Issues, https://github.com/yaronday/nano_utils/issues
|
|
7
|
+
Project-URL: license, https://github.com/yaronday/nano_dev_utils/blob/master/LICENSE
|
|
8
|
+
Author-email: Yaron Dayan <yaronday77@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: build>=1.3.0
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# nano_dev_utils
|
|
18
|
+
|
|
19
|
+
A collection of small Python utilities for developers.
|
|
20
|
+
[PYPI package: nano-dev-utils](https://pypi.org/project/nano-dev-utils)
|
|
21
|
+
|
|
22
|
+
## Modules
|
|
23
|
+
|
|
24
|
+
### `timers.py`
|
|
25
|
+
|
|
26
|
+
This module provides a `Timer` class for measuring the execution time of code blocks and functions with additional features like timeout control and multi-iteration averaging.
|
|
27
|
+
|
|
28
|
+
#### `Timer` Class
|
|
29
|
+
|
|
30
|
+
* **`__init__(self, precision: int = 4, verbose: bool = False, printout: bool = False)`**: Initializes a `Timer` instance.
|
|
31
|
+
* `precision`: The number of decimal places to record and display time durations. Defaults to 4.
|
|
32
|
+
* `verbose`: Optionally displays the function's positional arguments (args) and keyword arguments (kwargs). Defaults to `False`.
|
|
33
|
+
* `printout`: Allows printing to console.
|
|
34
|
+
|
|
35
|
+
* **`def timeit(
|
|
36
|
+
self,
|
|
37
|
+
iterations: int = 1,
|
|
38
|
+
timeout: float | None = None,
|
|
39
|
+
per_iteration: bool = False,
|
|
40
|
+
) -> Callable[[Callable[P, Any]], Callable[P, Any]]:`**:
|
|
41
|
+
Decorator that times either **sync** or **async** function execution with advanced features:
|
|
42
|
+
* `iterations`: Number of times to run the function (for averaging). Defaults to 1.
|
|
43
|
+
* `timeout`: Maximum allowed execution time in seconds. When exceeded:
|
|
44
|
+
* Raises `TimeoutError` immediately
|
|
45
|
+
* **Warning:** The function execution will be aborted mid-operation
|
|
46
|
+
* No return value will be available if timeout occurs
|
|
47
|
+
* `per_iteration`: If True, applies timeout check to each iteration; otherwise checks total time across all iterations.
|
|
48
|
+
* Features:
|
|
49
|
+
* Records execution times
|
|
50
|
+
* Handles timeout conditions
|
|
51
|
+
* Calculates average execution time across iterations
|
|
52
|
+
* Logs the function name and execution time (with optional arguments)
|
|
53
|
+
* Returns the result of the original function (unless timeout occurs)
|
|
54
|
+
|
|
55
|
+
#### Example Usage:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import time
|
|
59
|
+
import logging
|
|
60
|
+
from nano_dev_utils import timer
|
|
61
|
+
|
|
62
|
+
# if printout is not enabled, a logger must be configured in order to see timing results
|
|
63
|
+
logging.basicConfig(filename='timer example.log',
|
|
64
|
+
level=logging.INFO, # DEBUG, WARNING, ERROR, CRITICAL
|
|
65
|
+
format='%(asctime)s - %(levelname)s: %(message)s',
|
|
66
|
+
datefmt='%d-%m-%Y %H:%M:%S')
|
|
67
|
+
|
|
68
|
+
# Basic timing
|
|
69
|
+
@timer.timeit()
|
|
70
|
+
def my_function(a, b=10):
|
|
71
|
+
"""A sample function."""
|
|
72
|
+
time.sleep(0.1)
|
|
73
|
+
return a + b
|
|
74
|
+
|
|
75
|
+
timer.init(precision=6, verbose=True)
|
|
76
|
+
'''Alternative options:
|
|
77
|
+
timer.update({'precision': 6, 'verbose': True}) # 1. Using update method
|
|
78
|
+
|
|
79
|
+
from nano_dev_utils.timers import Timer # 2. explicit instantiation
|
|
80
|
+
timer = Timer(precision=6, verbose=True)
|
|
81
|
+
'''
|
|
82
|
+
|
|
83
|
+
timer.update({'printout': True}) # allow printing to console
|
|
84
|
+
|
|
85
|
+
# Advanced usage with timeout and iterations
|
|
86
|
+
@timer.timeit(iterations=5, timeout=0.5, per_iteration=True)
|
|
87
|
+
def critical_function(x):
|
|
88
|
+
"""Function with timeout check per iteration."""
|
|
89
|
+
time.sleep(0.08)
|
|
90
|
+
return x * 2
|
|
91
|
+
|
|
92
|
+
result1 = my_function(5, b=20) # Shows args/kwargs and timing
|
|
93
|
+
result2 = critical_function(10) # Runs 5 times with per-iteration timeout
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `dynamic_importer.py`
|
|
97
|
+
|
|
98
|
+
This module provides an `Importer` class for lazy loading and caching module imports.
|
|
99
|
+
|
|
100
|
+
#### `Importer` Class
|
|
101
|
+
|
|
102
|
+
* **`__init__(self)`**: Initializes an `Importer` instance with an empty dictionary `imported_modules` to cache imported modules.
|
|
103
|
+
|
|
104
|
+
* **`import_mod_from_lib(self, library: str, module_name: str) -> ModuleType | Any`**: Lazily imports a module from a specified library and caches it.
|
|
105
|
+
* `library` (str): The name of the library (e.g., "os", "requests").
|
|
106
|
+
* `module_name` (str): The name of the module to import within the library (e.g., "path", "get").
|
|
107
|
+
* Returns the imported module. If the module has already been imported, it returns a cached instance.
|
|
108
|
+
* Raises `ImportError` if the module cannot be found.
|
|
109
|
+
|
|
110
|
+
#### Example Usage:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from nano_dev_utils import importer
|
|
114
|
+
|
|
115
|
+
os_path = importer.import_mod_from_lib("os", "path")
|
|
116
|
+
print(f"Imported os.path: {os_path}")
|
|
117
|
+
|
|
118
|
+
requests_get = importer.import_mod_from_lib("requests", "get")
|
|
119
|
+
print(f"Imported requests.get: {requests_get}")
|
|
120
|
+
|
|
121
|
+
# Subsequent calls will return the cached module
|
|
122
|
+
os_path_again = importer.import_mod_from_lib("os", "path")
|
|
123
|
+
print(f"Imported os.path again (cached): {os_path_again}")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `release_ports.py`
|
|
127
|
+
|
|
128
|
+
This module provides a `PortsRelease` class to identify and release processes
|
|
129
|
+
listening on specified TCP ports.
|
|
130
|
+
It supports Windows, Linux, and macOS.
|
|
131
|
+
|
|
132
|
+
#### `PortsRelease` Class
|
|
133
|
+
|
|
134
|
+
* **`__init__(self, default_ports: list[int] | None = None)`**:
|
|
135
|
+
* Initializes a `PortsRelease` instance.
|
|
136
|
+
* `default_ports`: A list of default ports to manage. If not provided, it defaults to `[6277, 6274]`.
|
|
137
|
+
|
|
138
|
+
* **`get_pid_by_port(self, port: int) -> int | None`**: A static method that attempts to find
|
|
139
|
+
a process ID (PID) listening on a given `port`.
|
|
140
|
+
* It uses platform-specific commands (`netstat`, `ss`, `lsof`).
|
|
141
|
+
* Returns the PID if found, otherwise `None`.
|
|
142
|
+
|
|
143
|
+
* **`kill_process(self, pid: int) -> bool`**: A static method that attempts to kill the process
|
|
144
|
+
with the given `pid`.
|
|
145
|
+
* It uses platform-specific commands (`taskkill`, `kill -9`).
|
|
146
|
+
* Returns `True` if the process was successfully killed, `False` otherwise.
|
|
147
|
+
|
|
148
|
+
* **`release_all(self, ports: list[int] | None = None) -> None`**: Releases all processes listening on the specified `ports`.
|
|
149
|
+
* `ports`: A list of ports to release.
|
|
150
|
+
* If `None`, it uses the `default_ports` defined during initialization.
|
|
151
|
+
* For each port, it first tries to get the PID and then attempts to kill the process.
|
|
152
|
+
* It logs the actions and any errors encountered. Invalid port numbers in the provided list are skipped.
|
|
153
|
+
|
|
154
|
+
#### Example Usage:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import logging
|
|
158
|
+
from nano_dev_utils import ports_release, PortsRelease
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
logging.basicConfig(filename='port release.log',
|
|
162
|
+
level=logging.INFO, # DEBUG, WARNING, ERROR, CRITICAL
|
|
163
|
+
format='%(asctime)s - %(levelname)s: %(message)s',
|
|
164
|
+
datefmt='%d-%m-%Y %H:%M:%S')
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
ports_release.release_all()
|
|
168
|
+
|
|
169
|
+
# Create an instance with custom ports
|
|
170
|
+
custom_ports_releaser = PortsRelease(default_ports=[8080, 9000, 6274])
|
|
171
|
+
custom_ports_releaser.release_all(ports=[8080, 9000])
|
|
172
|
+
|
|
173
|
+
# Release only the default ports
|
|
174
|
+
ports_release.release_all()
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
This project is licensed under the MIT License.
|
|
180
|
+
See [LICENSE](https://github.com/yaronday/nano_dev_utils/blob/master/LICENSE) for details.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# nano_dev_utils
|
|
2
|
+
|
|
3
|
+
A collection of small Python utilities for developers.
|
|
4
|
+
[PYPI package: nano-dev-utils](https://pypi.org/project/nano-dev-utils)
|
|
5
|
+
|
|
6
|
+
## Modules
|
|
7
|
+
|
|
8
|
+
### `timers.py`
|
|
9
|
+
|
|
10
|
+
This module provides a `Timer` class for measuring the execution time of code blocks and functions with additional features like timeout control and multi-iteration averaging.
|
|
11
|
+
|
|
12
|
+
#### `Timer` Class
|
|
13
|
+
|
|
14
|
+
* **`__init__(self, precision: int = 4, verbose: bool = False, printout: bool = False)`**: Initializes a `Timer` instance.
|
|
15
|
+
* `precision`: The number of decimal places to record and display time durations. Defaults to 4.
|
|
16
|
+
* `verbose`: Optionally displays the function's positional arguments (args) and keyword arguments (kwargs). Defaults to `False`.
|
|
17
|
+
* `printout`: Allows printing to console.
|
|
18
|
+
|
|
19
|
+
* **`def timeit(
|
|
20
|
+
self,
|
|
21
|
+
iterations: int = 1,
|
|
22
|
+
timeout: float | None = None,
|
|
23
|
+
per_iteration: bool = False,
|
|
24
|
+
) -> Callable[[Callable[P, Any]], Callable[P, Any]]:`**:
|
|
25
|
+
Decorator that times either **sync** or **async** function execution with advanced features:
|
|
26
|
+
* `iterations`: Number of times to run the function (for averaging). Defaults to 1.
|
|
27
|
+
* `timeout`: Maximum allowed execution time in seconds. When exceeded:
|
|
28
|
+
* Raises `TimeoutError` immediately
|
|
29
|
+
* **Warning:** The function execution will be aborted mid-operation
|
|
30
|
+
* No return value will be available if timeout occurs
|
|
31
|
+
* `per_iteration`: If True, applies timeout check to each iteration; otherwise checks total time across all iterations.
|
|
32
|
+
* Features:
|
|
33
|
+
* Records execution times
|
|
34
|
+
* Handles timeout conditions
|
|
35
|
+
* Calculates average execution time across iterations
|
|
36
|
+
* Logs the function name and execution time (with optional arguments)
|
|
37
|
+
* Returns the result of the original function (unless timeout occurs)
|
|
38
|
+
|
|
39
|
+
#### Example Usage:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import time
|
|
43
|
+
import logging
|
|
44
|
+
from nano_dev_utils import timer
|
|
45
|
+
|
|
46
|
+
# if printout is not enabled, a logger must be configured in order to see timing results
|
|
47
|
+
logging.basicConfig(filename='timer example.log',
|
|
48
|
+
level=logging.INFO, # DEBUG, WARNING, ERROR, CRITICAL
|
|
49
|
+
format='%(asctime)s - %(levelname)s: %(message)s',
|
|
50
|
+
datefmt='%d-%m-%Y %H:%M:%S')
|
|
51
|
+
|
|
52
|
+
# Basic timing
|
|
53
|
+
@timer.timeit()
|
|
54
|
+
def my_function(a, b=10):
|
|
55
|
+
"""A sample function."""
|
|
56
|
+
time.sleep(0.1)
|
|
57
|
+
return a + b
|
|
58
|
+
|
|
59
|
+
timer.init(precision=6, verbose=True)
|
|
60
|
+
'''Alternative options:
|
|
61
|
+
timer.update({'precision': 6, 'verbose': True}) # 1. Using update method
|
|
62
|
+
|
|
63
|
+
from nano_dev_utils.timers import Timer # 2. explicit instantiation
|
|
64
|
+
timer = Timer(precision=6, verbose=True)
|
|
65
|
+
'''
|
|
66
|
+
|
|
67
|
+
timer.update({'printout': True}) # allow printing to console
|
|
68
|
+
|
|
69
|
+
# Advanced usage with timeout and iterations
|
|
70
|
+
@timer.timeit(iterations=5, timeout=0.5, per_iteration=True)
|
|
71
|
+
def critical_function(x):
|
|
72
|
+
"""Function with timeout check per iteration."""
|
|
73
|
+
time.sleep(0.08)
|
|
74
|
+
return x * 2
|
|
75
|
+
|
|
76
|
+
result1 = my_function(5, b=20) # Shows args/kwargs and timing
|
|
77
|
+
result2 = critical_function(10) # Runs 5 times with per-iteration timeout
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `dynamic_importer.py`
|
|
81
|
+
|
|
82
|
+
This module provides an `Importer` class for lazy loading and caching module imports.
|
|
83
|
+
|
|
84
|
+
#### `Importer` Class
|
|
85
|
+
|
|
86
|
+
* **`__init__(self)`**: Initializes an `Importer` instance with an empty dictionary `imported_modules` to cache imported modules.
|
|
87
|
+
|
|
88
|
+
* **`import_mod_from_lib(self, library: str, module_name: str) -> ModuleType | Any`**: Lazily imports a module from a specified library and caches it.
|
|
89
|
+
* `library` (str): The name of the library (e.g., "os", "requests").
|
|
90
|
+
* `module_name` (str): The name of the module to import within the library (e.g., "path", "get").
|
|
91
|
+
* Returns the imported module. If the module has already been imported, it returns a cached instance.
|
|
92
|
+
* Raises `ImportError` if the module cannot be found.
|
|
93
|
+
|
|
94
|
+
#### Example Usage:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from nano_dev_utils import importer
|
|
98
|
+
|
|
99
|
+
os_path = importer.import_mod_from_lib("os", "path")
|
|
100
|
+
print(f"Imported os.path: {os_path}")
|
|
101
|
+
|
|
102
|
+
requests_get = importer.import_mod_from_lib("requests", "get")
|
|
103
|
+
print(f"Imported requests.get: {requests_get}")
|
|
104
|
+
|
|
105
|
+
# Subsequent calls will return the cached module
|
|
106
|
+
os_path_again = importer.import_mod_from_lib("os", "path")
|
|
107
|
+
print(f"Imported os.path again (cached): {os_path_again}")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `release_ports.py`
|
|
111
|
+
|
|
112
|
+
This module provides a `PortsRelease` class to identify and release processes
|
|
113
|
+
listening on specified TCP ports.
|
|
114
|
+
It supports Windows, Linux, and macOS.
|
|
115
|
+
|
|
116
|
+
#### `PortsRelease` Class
|
|
117
|
+
|
|
118
|
+
* **`__init__(self, default_ports: list[int] | None = None)`**:
|
|
119
|
+
* Initializes a `PortsRelease` instance.
|
|
120
|
+
* `default_ports`: A list of default ports to manage. If not provided, it defaults to `[6277, 6274]`.
|
|
121
|
+
|
|
122
|
+
* **`get_pid_by_port(self, port: int) -> int | None`**: A static method that attempts to find
|
|
123
|
+
a process ID (PID) listening on a given `port`.
|
|
124
|
+
* It uses platform-specific commands (`netstat`, `ss`, `lsof`).
|
|
125
|
+
* Returns the PID if found, otherwise `None`.
|
|
126
|
+
|
|
127
|
+
* **`kill_process(self, pid: int) -> bool`**: A static method that attempts to kill the process
|
|
128
|
+
with the given `pid`.
|
|
129
|
+
* It uses platform-specific commands (`taskkill`, `kill -9`).
|
|
130
|
+
* Returns `True` if the process was successfully killed, `False` otherwise.
|
|
131
|
+
|
|
132
|
+
* **`release_all(self, ports: list[int] | None = None) -> None`**: Releases all processes listening on the specified `ports`.
|
|
133
|
+
* `ports`: A list of ports to release.
|
|
134
|
+
* If `None`, it uses the `default_ports` defined during initialization.
|
|
135
|
+
* For each port, it first tries to get the PID and then attempts to kill the process.
|
|
136
|
+
* It logs the actions and any errors encountered. Invalid port numbers in the provided list are skipped.
|
|
137
|
+
|
|
138
|
+
#### Example Usage:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
import logging
|
|
142
|
+
from nano_dev_utils import ports_release, PortsRelease
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
logging.basicConfig(filename='port release.log',
|
|
146
|
+
level=logging.INFO, # DEBUG, WARNING, ERROR, CRITICAL
|
|
147
|
+
format='%(asctime)s - %(levelname)s: %(message)s',
|
|
148
|
+
datefmt='%d-%m-%Y %H:%M:%S')
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
ports_release.release_all()
|
|
152
|
+
|
|
153
|
+
# Create an instance with custom ports
|
|
154
|
+
custom_ports_releaser = PortsRelease(default_ports=[8080, 9000, 6274])
|
|
155
|
+
custom_ports_releaser.release_all(ports=[8080, 9000])
|
|
156
|
+
|
|
157
|
+
# Release only the default ports
|
|
158
|
+
ports_release.release_all()
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
This project is licensed under the MIT License.
|
|
164
|
+
See [LICENSE](https://github.com/yaronday/nano_dev_utils/blob/master/LICENSE) for details.
|
|
@@ -21,9 +21,6 @@ Homepage = "https://github.com/yaronday/nano_utils"
|
|
|
21
21
|
Issues = "https://github.com/yaronday/nano_utils/issues"
|
|
22
22
|
license = "https://github.com/yaronday/nano_dev_utils/blob/master/LICENSE"
|
|
23
23
|
|
|
24
|
-
[project.scripts]
|
|
25
|
-
ftd = "nano_dev_utils.__main__:main"
|
|
26
|
-
|
|
27
24
|
[project.files]
|
|
28
25
|
license = { path = "LICENSE" }
|
|
29
26
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Copyright (c) 2025 Yaron Dayan
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from pathlib import Path
|
|
6
5
|
from importlib.metadata import version
|
|
7
6
|
from .dynamic_importer import Importer
|
|
8
7
|
from .timers import Timer
|
|
@@ -15,14 +14,12 @@ from .common import (
|
|
|
15
14
|
FilterSet,
|
|
16
15
|
load_cfg_file,
|
|
17
16
|
)
|
|
18
|
-
from .file_tree_display import FileTreeDisplay
|
|
19
17
|
|
|
20
18
|
from ._constants import PKG_NAME
|
|
21
19
|
|
|
22
20
|
timer = Timer()
|
|
23
21
|
ports_release = PortsRelease()
|
|
24
22
|
importer = Importer()
|
|
25
|
-
filetree_display = FileTreeDisplay(root_dir=str(Path.cwd()))
|
|
26
23
|
predicate_builder = PredicateBuilder
|
|
27
24
|
|
|
28
25
|
__version__ = version(PKG_NAME)
|
|
@@ -42,7 +39,5 @@ __all__ = [
|
|
|
42
39
|
'timer',
|
|
43
40
|
'ports_release',
|
|
44
41
|
'importer',
|
|
45
|
-
'filetree_display',
|
|
46
|
-
'FileTreeDisplay',
|
|
47
42
|
'load_cfg_file',
|
|
48
43
|
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '1.7.0'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
PKG_NAME = 'nano-dev-utils'
|