nano-dev-utils 0.5.7__tar.gz → 0.5.8__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.

Potentially problematic release.


This version of nano-dev-utils might be problematic. Click here for more details.

Files changed (22) hide show
  1. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/.idea/nano_dev_utils.iml +3 -1
  2. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/.idea/workspace.xml +97 -5
  3. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/PKG-INFO +6 -4
  4. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/README.md +5 -3
  5. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/pyproject.toml +1 -1
  6. nano_dev_utils-0.5.8/src/nano_dev_utils/timers.py +49 -0
  7. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/tests/test_timer.py +30 -8
  8. nano_dev_utils-0.5.7/src/nano_dev_utils/timers.py +0 -38
  9. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/.gitignore +0 -0
  10. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/.idea/.gitignore +0 -0
  11. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  12. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/.idea/misc.xml +0 -0
  13. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/.idea/modules.xml +0 -0
  14. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/.idea/vcs.xml +0 -0
  15. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/LICENSE +0 -0
  16. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/src/nano_dev_utils/__init__.py +0 -0
  17. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/src/nano_dev_utils/dynamic_importer.py +0 -0
  18. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/src/nano_dev_utils/release_ports.py +0 -0
  19. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/tests/__init__.py +0 -0
  20. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/tests/test_dynamic_importer.py +0 -0
  21. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/tests/test_release_ports.py +0 -0
  22. {nano_dev_utils-0.5.7 → nano_dev_utils-0.5.8}/uv.lock +0 -0
@@ -1,7 +1,9 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <module type="PYTHON_MODULE" version="4">
3
3
  <component name="NewModuleRootManager">
4
- <content url="file://$MODULE_DIR$" />
4
+ <content url="file://$MODULE_DIR$">
5
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
6
+ </content>
5
7
  <orderEntry type="jdk" jdkName="Python 3.10 (nano_dev_utils)" jdkType="Python SDK" />
6
8
  <orderEntry type="sourceFolder" forTests="false" />
7
9
  </component>
@@ -4,8 +4,11 @@
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="removed [tool.hatch.build]">
7
+ <list default="true" id="1859e23b-7665-4b92-98cc-65e07a208923" name="Changes" comment="corrected return types - complete">
8
+ <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
8
9
  <change beforePath="$PROJECT_DIR$/pyproject.toml" beforeDir="false" afterPath="$PROJECT_DIR$/pyproject.toml" afterDir="false" />
10
+ <change beforePath="$PROJECT_DIR$/src/nano_dev_utils/timers.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/nano_dev_utils/timers.py" afterDir="false" />
11
+ <change beforePath="$PROJECT_DIR$/tests/test_timer.py" beforeDir="false" afterPath="$PROJECT_DIR$/tests/test_timer.py" afterDir="false" />
9
12
  </list>
10
13
  <option name="SHOW_DIALOG" value="false" />
11
14
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -15,7 +18,7 @@
15
18
  <component name="Git.Settings">
16
19
  <option name="RECENT_BRANCH_BY_REPOSITORY">
17
20
  <map>
18
- <entry key="$PROJECT_DIR$" value="yd_pre-commit" />
21
+ <entry key="$PROJECT_DIR$" value="FI" />
19
22
  </map>
20
23
  </option>
21
24
  <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
@@ -32,7 +35,9 @@
32
35
  </component>
33
36
  <component name="PropertiesComponent"><![CDATA[{
34
37
  "keyToString": {
38
+ "Python tests.Python tests in test_timer.py.executor": "Run",
35
39
  "Python tests.Python tests in tests.executor": "Run",
40
+ "Python.test_timer.executor": "Run",
36
41
  "RunOnceActivity.ShowReadmeOnStart": "true",
37
42
  "git-widget-placeholder": "master",
38
43
  "ignore.virus.scanning.warn.message": "true",
@@ -45,7 +50,44 @@
45
50
  <recent name="C:\GitHubWS\nano_dev_utils" />
46
51
  </key>
47
52
  </component>
48
- <component name="RunManager">
53
+ <component name="RunManager" selected="Python tests.Python tests in tests">
54
+ <configuration name="test_timer" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
55
+ <module name="nano_dev_utils" />
56
+ <option name="ENV_FILES" value="" />
57
+ <option name="INTERPRETER_OPTIONS" value="" />
58
+ <option name="PARENT_ENVS" value="true" />
59
+ <envs>
60
+ <env name="PYTHONUNBUFFERED" value="1" />
61
+ </envs>
62
+ <option name="SDK_HOME" value="" />
63
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/tests" />
64
+ <option name="IS_MODULE_SDK" value="true" />
65
+ <option name="ADD_CONTENT_ROOTS" value="true" />
66
+ <option name="ADD_SOURCE_ROOTS" value="true" />
67
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tests/test_timer.py" />
68
+ <option name="PARAMETERS" value="" />
69
+ <option name="SHOW_COMMAND_LINE" value="false" />
70
+ <option name="EMULATE_TERMINAL" value="false" />
71
+ <option name="MODULE_MODE" value="false" />
72
+ <option name="REDIRECT_INPUT" value="false" />
73
+ <option name="INPUT_FILE" value="" />
74
+ <method v="2" />
75
+ </configuration>
76
+ <configuration name="Python tests in test_timer.py" type="tests" factoryName="Autodetect" temporary="true" nameIsGenerated="true">
77
+ <module name="nano_dev_utils" />
78
+ <option name="ENV_FILES" value="" />
79
+ <option name="INTERPRETER_OPTIONS" value="" />
80
+ <option name="PARENT_ENVS" value="true" />
81
+ <option name="SDK_HOME" value="" />
82
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/tests" />
83
+ <option name="IS_MODULE_SDK" value="true" />
84
+ <option name="ADD_CONTENT_ROOTS" value="true" />
85
+ <option name="ADD_SOURCE_ROOTS" value="true" />
86
+ <option name="_new_additionalArguments" value="&quot;&quot;" />
87
+ <option name="_new_target" value="&quot;$PROJECT_DIR$/tests/test_timer.py&quot;" />
88
+ <option name="_new_targetType" value="&quot;PATH&quot;" />
89
+ <method v="2" />
90
+ </configuration>
49
91
  <configuration name="Python tests in tests" type="tests" factoryName="Autodetect" temporary="true" nameIsGenerated="true">
50
92
  <module name="nano_dev_utils" />
51
93
  <option name="ENV_FILES" value="" />
@@ -64,6 +106,9 @@
64
106
  <recent_temporary>
65
107
  <list>
66
108
  <item itemvalue="Python tests.Python tests in tests" />
109
+ <item itemvalue="Python tests.Python tests in tests" />
110
+ <item itemvalue="Python tests.Python tests in test_timer.py" />
111
+ <item itemvalue="Python.test_timer" />
67
112
  </list>
68
113
  </recent_temporary>
69
114
  </component>
@@ -139,9 +184,52 @@
139
184
  <option name="project" value="LOCAL" />
140
185
  <updated>1747321637844</updated>
141
186
  </task>
142
- <option name="localTasksCounter" value="8" />
187
+ <task id="LOCAL-00008" summary="+iterations parameter">
188
+ <option name="closed" value="true" />
189
+ <created>1747660086270</created>
190
+ <option name="number" value="00008" />
191
+ <option name="presentableId" value="LOCAL-00008" />
192
+ <option name="project" value="LOCAL" />
193
+ <updated>1747660086270</updated>
194
+ </task>
195
+ <task id="LOCAL-00009" summary="minor update">
196
+ <option name="closed" value="true" />
197
+ <created>1747661192289</created>
198
+ <option name="number" value="00009" />
199
+ <option name="presentableId" value="LOCAL-00009" />
200
+ <option name="project" value="LOCAL" />
201
+ <updated>1747661192289</updated>
202
+ </task>
203
+ <task id="LOCAL-00010" summary="corrected return type">
204
+ <option name="closed" value="true" />
205
+ <created>1747665494128</created>
206
+ <option name="number" value="00010" />
207
+ <option name="presentableId" value="LOCAL-00010" />
208
+ <option name="project" value="LOCAL" />
209
+ <updated>1747665494128</updated>
210
+ </task>
211
+ <task id="LOCAL-00011" summary="corrected return types - complete">
212
+ <option name="closed" value="true" />
213
+ <created>1747666293914</created>
214
+ <option name="number" value="00011" />
215
+ <option name="presentableId" value="LOCAL-00011" />
216
+ <option name="project" value="LOCAL" />
217
+ <updated>1747666293914</updated>
218
+ </task>
219
+ <option name="localTasksCounter" value="12" />
143
220
  <servers />
144
221
  </component>
222
+ <component name="Vcs.Log.Tabs.Properties">
223
+ <option name="TAB_STATES">
224
+ <map>
225
+ <entry key="MAIN">
226
+ <value>
227
+ <State />
228
+ </value>
229
+ </entry>
230
+ </map>
231
+ </option>
232
+ </component>
145
233
  <component name="VcsManagerConfiguration">
146
234
  <MESSAGE value="." />
147
235
  <MESSAGE value="license relative path" />
@@ -150,6 +238,10 @@
150
238
  <MESSAGE value="adding license url" />
151
239
  <MESSAGE value="+pre-commit config" />
152
240
  <MESSAGE value="removed [tool.hatch.build]" />
153
- <option name="LAST_COMMIT_MESSAGE" value="removed [tool.hatch.build]" />
241
+ <MESSAGE value="+iterations parameter" />
242
+ <MESSAGE value="minor update" />
243
+ <MESSAGE value="corrected return type" />
244
+ <MESSAGE value="corrected return types - complete" />
245
+ <option name="LAST_COMMIT_MESSAGE" value="corrected return types - complete" />
154
246
  </component>
155
247
  </project>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nano_dev_utils
3
- Version: 0.5.7
3
+ Version: 0.5.8
4
4
  Summary: A collection of small Python utilities for developers.
5
5
  Project-URL: Homepage, https://github.com/yaronday/nano_utils
6
6
  Project-URL: Issues, https://github.com/yaronday/nano_utils/issues
@@ -34,10 +34,12 @@ This module provides a `Timer` class for measuring the execution time of code bl
34
34
  * `verbose`: Optionally displays the function's positional arguments (args) and keyword arguments (kwargs).
35
35
  Defaults to `False`.
36
36
 
37
- * **`timeit(self, func: Callable[P, R]) -> Callable[P, R]`**:
37
+ * **`timeit(
38
+ self, iterations: int = 1
39
+ ) -> Callable[[Callable[P, R]], Callable[P, R | None]]`**:
38
40
  Decorator that times function execution with automatic unit scaling.
39
41
  * When the decorated function is called, this decorator records the start and end times,
40
- calculates the total execution time, prints the function name and execution
42
+ calculates the average execution time, prints the function name and execution
41
43
  time (optionally including arguments), and returns the result of the original function.
42
44
 
43
45
  #### Example Usage:
@@ -49,7 +51,7 @@ from nano_dev_utils.timers import Timer
49
51
  timer = Timer(precision=6, verbose=True)
50
52
 
51
53
 
52
- @timer.timeit
54
+ @timer.timeit()
53
55
  def my_function(a, b=10):
54
56
  """A sample function."""
55
57
  time.sleep(0.1)
@@ -16,10 +16,12 @@ This module provides a `Timer` class for measuring the execution time of code bl
16
16
  * `verbose`: Optionally displays the function's positional arguments (args) and keyword arguments (kwargs).
17
17
  Defaults to `False`.
18
18
 
19
- * **`timeit(self, func: Callable[P, R]) -> Callable[P, R]`**:
19
+ * **`timeit(
20
+ self, iterations: int = 1
21
+ ) -> Callable[[Callable[P, R]], Callable[P, R | None]]`**:
20
22
  Decorator that times function execution with automatic unit scaling.
21
23
  * When the decorated function is called, this decorator records the start and end times,
22
- calculates the total execution time, prints the function name and execution
24
+ calculates the average execution time, prints the function name and execution
23
25
  time (optionally including arguments), and returns the result of the original function.
24
26
 
25
27
  #### Example Usage:
@@ -31,7 +33,7 @@ from nano_dev_utils.timers import Timer
31
33
  timer = Timer(precision=6, verbose=True)
32
34
 
33
35
 
34
- @timer.timeit
36
+ @timer.timeit()
35
37
  def my_function(a, b=10):
36
38
  """A sample function."""
37
39
  time.sleep(0.1)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nano_dev_utils"
3
- version = "0.5.7"
3
+ version = "0.5.8"
4
4
 
5
5
  authors = [
6
6
  { name="Yaron Dayan", email="yaronday77@gmail.com" },
@@ -0,0 +1,49 @@
1
+ from functools import wraps
2
+ import time
3
+ from typing import Callable, ParamSpec, TypeVar
4
+
5
+ P = ParamSpec('P')
6
+ R = TypeVar('R')
7
+
8
+
9
+ class Timer:
10
+ def __init__(self, precision=4, verbose=False):
11
+ self.precision = precision
12
+ self.verbose = verbose
13
+ self.units = [(1e9, 's'), (1e6, 'ms'), (1e3, 'μs'), (1.0, 'ns')]
14
+
15
+ def timeit(
16
+ self, iterations: int = 1
17
+ ) -> Callable[[Callable[P, R]], Callable[P, R | None]]:
18
+ def decorator(func: Callable[P, R]) -> Callable[P, R | None]:
19
+ """Decorator that times function execution with automatic unit scaling and averaging."""
20
+
21
+ @wraps(func)
22
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | None:
23
+ total_elapsed = 0
24
+ result = None
25
+
26
+ for _ in range(iterations):
27
+ start = time.perf_counter_ns()
28
+ result = func(*args, **kwargs)
29
+ total_elapsed += time.perf_counter_ns() - start
30
+
31
+ avg_elapsed = total_elapsed / iterations
32
+ value = avg_elapsed
33
+ unit = 'ns'
34
+
35
+ for divisor, unit in self.units:
36
+ if avg_elapsed >= divisor or unit == 'ns':
37
+ value = avg_elapsed / divisor
38
+ break
39
+
40
+ extra_info = f'{args} {kwargs} ' if self.verbose else ''
41
+ iter_info = f' (avg over {iterations} runs)' if iterations > 1 else ''
42
+ print(
43
+ f'{func.__name__} {extra_info}took {value:.{self.precision}f} [{unit}]{iter_info}'
44
+ )
45
+ return result
46
+
47
+ return wrapper
48
+
49
+ return decorator
@@ -18,7 +18,7 @@ def test_timeit_simple(mocker):
18
18
  mock_time = mocker.patch('time.perf_counter_ns', side_effect=[0, 9.23467e5])
19
19
  timer = Timer(precision=2)
20
20
 
21
- @timer.timeit
21
+ @timer.timeit()
22
22
  def sample_function():
23
23
  return 'result'
24
24
 
@@ -33,7 +33,7 @@ def test_timeit_no_args_kwargs(mocker):
33
33
  mock_time = mocker.patch('time.perf_counter_ns', side_effect=[1.0, 1.5])
34
34
  timer = Timer(precision=2, verbose=True)
35
35
 
36
- @timer.timeit
36
+ @timer.timeit()
37
37
  def yet_another_function():
38
38
  return 'yet another result'
39
39
 
@@ -49,7 +49,7 @@ def test_multithreaded_timing(mocker):
49
49
  timer = Timer()
50
50
  results = []
51
51
 
52
- @timer.timeit
52
+ @timer.timeit()
53
53
  def threaded_operation():
54
54
  time.sleep(0.1)
55
55
  return threading.get_ident()
@@ -77,7 +77,7 @@ def test_verbose_mode(mocker):
77
77
  mocker.patch('time.perf_counter_ns', side_effect=[1e4, 5.23456e4])
78
78
  verbose_timer = Timer(verbose=True)
79
79
 
80
- @verbose_timer.timeit
80
+ @verbose_timer.timeit()
81
81
  def func_with_args(a, b, c=3):
82
82
  return a + b + c
83
83
 
@@ -96,9 +96,9 @@ def test_nested_timers(mocker):
96
96
  mock_print = mocker.patch('builtins.print')
97
97
  timer = Timer()
98
98
 
99
- @timer.timeit
99
+ @timer.timeit()
100
100
  def outer():
101
- @timer.timeit
101
+ @timer.timeit()
102
102
  def inner():
103
103
  time.sleep(0.1)
104
104
 
@@ -134,7 +134,7 @@ def test_unit_scaling(mocker):
134
134
  mocker.patch('time.perf_counter_ns', side_effect=[0, ns])
135
135
  timer = Timer(precision=2)
136
136
 
137
- @timer.timeit
137
+ @timer.timeit()
138
138
  def dummy():
139
139
  pass
140
140
 
@@ -150,10 +150,32 @@ def test_function_metadata_preserved():
150
150
  """Test that function metadata (name, docstring) is preserved"""
151
151
  timer = Timer(precision=3)
152
152
 
153
- @timer.timeit
153
+ @timer.timeit()
154
154
  def dummy_func():
155
155
  """Test docstring"""
156
156
  pass
157
157
 
158
158
  assert dummy_func.__name__ == 'dummy_func'
159
159
  assert dummy_func.__doc__ == 'Test docstring'
160
+
161
+
162
+ def test_timeit_with_iterations(mocker):
163
+ mock_print = mocker.patch('builtins.print')
164
+ mock_time = mocker.patch(
165
+ 'time.perf_counter_ns', side_effect=[0, 1000, 0, 2000, 0, 3000]
166
+ )
167
+
168
+ timer = Timer(precision=2)
169
+
170
+ @timer.timeit(iterations=3)
171
+ def sample_function():
172
+ return 'done'
173
+
174
+ result = sample_function()
175
+
176
+ assert result == 'done'
177
+ mock_time.assert_any_call()
178
+
179
+ mock_print.assert_called_once_with(
180
+ 'sample_function took 2.00 [μs] (avg over 3 runs)'
181
+ )
@@ -1,38 +0,0 @@
1
- from functools import wraps
2
- import time
3
- from typing import Callable, ParamSpec, TypeVar
4
-
5
- P = ParamSpec('P')
6
- R = TypeVar('R')
7
-
8
-
9
- class Timer:
10
- def __init__(self, precision=4, verbose=False):
11
- self.precision = precision
12
- self.verbose = verbose
13
- self.units = [(1e9, 's'), (1e6, 'ms'), (1e3, 'μs'), (1.0, 'ns')]
14
-
15
- def timeit(self, func: Callable[P, R]) -> Callable[P, R]:
16
- """Decorator that times function execution with automatic unit scaling."""
17
-
18
- @wraps(func)
19
- def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
20
- start = time.perf_counter_ns()
21
- result = func(*args, **kwargs)
22
- elapsed = time.perf_counter_ns() - start
23
-
24
- value = elapsed
25
- unit = 'ns'
26
-
27
- for divisor, unit in self.units:
28
- if elapsed >= divisor or unit == 'ns':
29
- value = elapsed / divisor
30
- break
31
-
32
- extra_info = f'{args} {kwargs} ' if self.verbose else ''
33
- print(
34
- f'{func.__name__} {extra_info}took {value:.{self.precision}f} [{unit}]'
35
- )
36
- return result
37
-
38
- return wrapper
File without changes
File without changes