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.
Files changed (44) hide show
  1. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.gitignore +3 -1
  2. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/workspace.xml +24 -34
  3. nano_dev_utils-1.7.0/PKG-INFO +180 -0
  4. nano_dev_utils-1.7.0/README.md +164 -0
  5. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/pyproject.toml +0 -3
  6. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/__init__.py +0 -5
  7. nano_dev_utils-1.7.0/src/nano_dev_utils/__version__.py +1 -0
  8. nano_dev_utils-1.7.0/src/nano_dev_utils/_constants.py +1 -0
  9. nano_dev_utils-1.6.2/PKG-INFO +0 -372
  10. nano_dev_utils-1.6.2/README.md +0 -356
  11. nano_dev_utils-1.6.2/benchmark/benchtest.py +0 -52
  12. nano_dev_utils-1.6.2/benchmark/benchtest_bkp.py +0 -91
  13. nano_dev_utils-1.6.2/benchmark/win_tree_wrapper.py +0 -52
  14. nano_dev_utils-1.6.2/src/nano_dev_utils/__main__.py +0 -129
  15. nano_dev_utils-1.6.2/src/nano_dev_utils/__version__.py +0 -1
  16. nano_dev_utils-1.6.2/src/nano_dev_utils/_constants.py +0 -2
  17. nano_dev_utils-1.6.2/src/nano_dev_utils/file_tree_display.py +0 -325
  18. nano_dev_utils-1.6.2/tests/test_file_tree_display.py +0 -256
  19. nano_dev_utils-1.6.2/tests/test_timers/__init__.py +0 -3
  20. nano_dev_utils-1.6.2/update_package.py +0 -19
  21. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/.gitignore +0 -0
  22. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/encodings.xml +0 -0
  23. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  24. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/misc.xml +0 -0
  25. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/modules.xml +0 -0
  26. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/nano_dev_utils.iml +0 -0
  27. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.idea/vcs.xml +0 -0
  28. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/.pre-commit-config.yaml +0 -0
  29. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/LICENSE +0 -0
  30. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/ruff.toml +0 -0
  31. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/common.py +0 -0
  32. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/dynamic_importer.py +0 -0
  33. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/release_ports.py +0 -0
  34. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/src/nano_dev_utils/timers.py +0 -0
  35. {nano_dev_utils-1.6.2/benchmark → nano_dev_utils-1.7.0/tests}/__init__.py +0 -0
  36. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_common.py +0 -0
  37. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_dynamic_importer.py +0 -0
  38. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_release_ports.py +0 -0
  39. {nano_dev_utils-1.6.2/tests → nano_dev_utils-1.7.0/tests/test_timers}/__init__.py +0 -0
  40. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_timers/conftest.py +0 -0
  41. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_timers/test_duration_formatter.py +0 -0
  42. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_timers/test_timer.py +0 -0
  43. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/tests/test_timers/test_timers_async.py +0 -0
  44. {nano_dev_utils-1.6.2 → nano_dev_utils-1.7.0}/uv.lock +0 -0
@@ -174,4 +174,6 @@ cython_debug/
174
174
  .pypirc
175
175
 
176
176
  /src/*/debug.py
177
- /src/*/*.log
177
+ /src/*/*.log
178
+
179
+ update_package.py
@@ -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="versioning updates;&#10;load_cfg_file minor update;&#10;v1.6.1">
8
- <change beforePath="$PROJECT_DIR$/src/nano_dev_utils/__main__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/nano_dev_utils/__main__.py" afterDir="false" />
7
+ <list default="true" id="1859e23b-7665-4b92-98cc-65e07a208923" name="Changes" comment="minor bugfix (cfg); &#10;Bumped minor version&#10;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="FI-CLI" />
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
  &quot;Python.test_timer.executor&quot;: &quot;Run&quot;,
60
67
  &quot;Python.update_package.executor&quot;: &quot;Run&quot;,
61
68
  &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
62
- &quot;git-widget-placeholder&quot;: &quot;master&quot;,
69
+ &quot;git-widget-placeholder&quot;: &quot;FI-CLI&quot;,
63
70
  &quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
64
71
  &quot;last_opened_file_path&quot;: &quot;C:/GitHubWS/nano_dev_utils/src&quot;,
65
72
  &quot;settings.editor.selected.configurable&quot;: &quot;com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable&quot;,
@@ -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.benchtest_bkp">
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="&quot;&quot;" />
140
- <option name="_new_target" value="&quot;$PROJECT_DIR$/tests/test_file_tree_display.py&quot;" />
141
- <option name="_new_targetType" value="&quot;PATH&quot;" />
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
- <option name="localTasksCounter" value="147" />
564
+ <task id="LOCAL-00147" summary="minor bugfix (cfg); &#10;Bumped minor version&#10;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:&#10;-reduced alloc &amp; overhead;&#10;-precomputed style dict and sorting outside the recursive _build_tree;&#10;-improved string ops:&#10;used StringIO for better memory efficiency&#10;-improved traversal&#10;&#10;Unit tests updates" />
641
630
  <MESSAGE value="-removing single char old style fallback &#10;+ 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: &#10;dynamic version; &#10;ftd cli shortcut;&#10;hatch.version path according to __version__ &#10;v1.6.0" />
663
652
  <MESSAGE value="versioning updates;&#10;load_cfg_file minor update;&#10;v1.6.1" />
664
- <option name="LAST_COMMIT_MESSAGE" value="versioning updates;&#10;load_cfg_file minor update;&#10;v1.6.1" />
653
+ <MESSAGE value="minor bugfix (cfg); &#10;Bumped minor version&#10;v1.6.2" />
654
+ <option name="LAST_COMMIT_MESSAGE" value="minor bugfix (cfg); &#10;Bumped minor version&#10;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'