pylemetry 1.1.4__tar.gz → 1.2.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 (41) hide show
  1. {pylemetry-1.1.4 → pylemetry-1.2.0}/PKG-INFO +62 -13
  2. {pylemetry-1.1.4 → pylemetry-1.2.0}/README.md +61 -12
  3. {pylemetry-1.1.4 → pylemetry-1.2.0}/pyproject.toml +1 -1
  4. pylemetry-1.2.0/src/pylemetry/decorators/count.py +80 -0
  5. pylemetry-1.2.0/src/pylemetry/decorators/time.py +83 -0
  6. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/meters/counter.py +4 -2
  7. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/meters/gauge.py +4 -2
  8. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/meters/meter.py +13 -1
  9. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/meters/timer.py +14 -6
  10. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/registry.py +39 -24
  11. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/reporting/logging.py +11 -5
  12. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/reporting/reporter.py +18 -7
  13. pylemetry-1.2.0/src/pylemetry/utils/__init__.py +4 -0
  14. pylemetry-1.2.0/src/pylemetry/utils/timer_units.py +22 -0
  15. pylemetry-1.2.0/test/test_pylemetry/decorators/test_count.py +136 -0
  16. pylemetry-1.2.0/test/test_pylemetry/decorators/test_time.py +145 -0
  17. {pylemetry-1.1.4 → pylemetry-1.2.0}/test/test_pylemetry/meters/test_counter.py +5 -5
  18. {pylemetry-1.1.4 → pylemetry-1.2.0}/test/test_pylemetry/meters/test_gauge.py +7 -7
  19. {pylemetry-1.1.4 → pylemetry-1.2.0}/test/test_pylemetry/meters/test_timer.py +19 -7
  20. {pylemetry-1.1.4 → pylemetry-1.2.0}/test/test_pylemetry/reporting/test_logging.py +24 -24
  21. {pylemetry-1.1.4 → pylemetry-1.2.0}/test/test_pylemetry/reporting/test_reporter.py +45 -22
  22. {pylemetry-1.1.4 → pylemetry-1.2.0}/test/test_pylemetry/test_registry.py +39 -39
  23. pylemetry-1.2.0/test/test_pylemetry/utils/test_timer_units.py +20 -0
  24. {pylemetry-1.1.4 → pylemetry-1.2.0}/uv.lock +1 -1
  25. pylemetry-1.1.4/src/pylemetry/decorators/count.py +0 -39
  26. pylemetry-1.1.4/src/pylemetry/decorators/time.py +0 -38
  27. pylemetry-1.1.4/test/test_pylemetry/decorators/test_count.py +0 -48
  28. pylemetry-1.1.4/test/test_pylemetry/decorators/test_time.py +0 -51
  29. {pylemetry-1.1.4 → pylemetry-1.2.0}/.github/actions/configure-uv/action.yaml +0 -0
  30. {pylemetry-1.1.4 → pylemetry-1.2.0}/.github/workflows/ci.yaml +0 -0
  31. {pylemetry-1.1.4 → pylemetry-1.2.0}/.github/workflows/release.yaml +0 -0
  32. {pylemetry-1.1.4 → pylemetry-1.2.0}/.gitignore +0 -0
  33. {pylemetry-1.1.4 → pylemetry-1.2.0}/LICENSE +0 -0
  34. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/__init__.py +0 -0
  35. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/__init__.py +0 -0
  36. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/decorators/__init__.py +0 -0
  37. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/meters/__init__.py +0 -0
  38. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/py.typed +0 -0
  39. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/reporting/__init__.py +0 -0
  40. {pylemetry-1.1.4 → pylemetry-1.2.0}/src/pylemetry/reporting/reporting_type.py +0 -0
  41. {pylemetry-1.1.4 → pylemetry-1.2.0}/test/conftest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pylemetry
3
- Version: 1.1.4
3
+ Version: 1.2.0
4
4
  Summary: A Python metrics library
5
5
  Project-URL: Homepage, https://pypi.org/project/pylemetry/
6
6
  Project-URL: Repository, https://github.com/amurphy4/pylemetry
@@ -44,7 +44,7 @@ from pylemetry.meters import Counter
44
44
 
45
45
 
46
46
  def some_method() -> None:
47
- counter = Counter()
47
+ counter = Counter("example")
48
48
 
49
49
  for _ in range(100):
50
50
  counter.add() # counter += 1 is also supported
@@ -93,9 +93,9 @@ from pylemetry.meters import Gauge
93
93
 
94
94
 
95
95
  def some_method() -> None:
96
- gauge = Gauge()
96
+ gauge = Gauge("sample_gauge")
97
97
 
98
- registry.add_gauge("sample_gauge", gauge)
98
+ registry.add_gauge(gauge)
99
99
  ```
100
100
 
101
101
  The `Gauge` supports incrementing, decrementing, and setting a value directly
@@ -127,7 +127,7 @@ from pylemetry.meters import Timer
127
127
 
128
128
 
129
129
  def some_method() -> None:
130
- timer = Timer()
130
+ timer = Timer("example")
131
131
 
132
132
  for _ in range(100):
133
133
  with timer.time():
@@ -173,6 +173,54 @@ def main() -> None:
173
173
 
174
174
  When using this meter via a decorator, the meter gets added to the global `registry`, with the method name it's decorating as the meter name. Alternatively, you can provide a name for the meter as a parameter to the decorator
175
175
 
176
+ By default, timer meters will measure time in nanoseconds, this can be changed via the `unit` parameter using the `TimerUnits` enum in the utils module
177
+
178
+ ```python
179
+ import time
180
+
181
+ from pylemetry.meters import Timer
182
+ from pylemetry.utils import TimerUnits
183
+
184
+
185
+ timer_s = Timer("example_s", TimerUnits.SECONDS)
186
+ timer_ms = Timer("example_ms", TimerUnits.MILLISECONDS)
187
+
188
+ with timer_s.time():
189
+ time.sleep(1)
190
+
191
+ with timer_ms.time():
192
+ time.sleep(1)
193
+
194
+ timer_s.get_mean_tick_time() # 1
195
+ timer_ms.get_mean_tick_time() # 1000
196
+ ```
197
+
198
+ ## Tags
199
+
200
+ When creating a meter you can assign a set of tags to it as key-value pairs. The value must be one of either `str`, `int`, or `float`.
201
+ When using a decorator to create a meter, you can use a custom format for the value to extract values out of the method's args and kwargs
202
+
203
+ In order to allow for multiple meters with the same name and different tags, the name in the registry gets mangled with the tags to produce a unique name,
204
+ as a result when trying to get the meter from the registry you will need to provide both its name and its tags.
205
+
206
+ ```python
207
+ from pylemetry import registry
208
+ from pylemetry.decorators import time
209
+
210
+
211
+ @time("example_timer", tags={"tag_1": "args[0]", "tag_2": "kwargs[param_2]", "tag_3": "some value"})
212
+ def some_method(param_1: int, param_2: int) -> None:
213
+ ...
214
+
215
+
216
+ def main() -> None:
217
+ for _ in range(100):
218
+ some_method(1, param_2=2)
219
+
220
+ timer = registry.get_timer("example_timer", {"tag_1": 1, "tag_2": 2, "tag_3": "some value"})
221
+ timer.get_tags() # {"tag_1": 1, "tag_2": 2, "tag_3": "some value"}
222
+ ```
223
+
176
224
  ## The Registry
177
225
 
178
226
  Pylemetry maintains a global registry of meters, allowing you to share a meter across multiple files, or reference metrics from a central location.
@@ -183,13 +231,13 @@ from pylemetry import registry
183
231
  from pylemetry.meters import Counter, Gauge, Timer
184
232
 
185
233
 
186
- counter = Counter()
187
- gauge = Gauge()
188
- timer = Timer()
234
+ counter = Counter("example")
235
+ gauge = Gauge("example")
236
+ timer = Timer("example")
189
237
 
190
- registry.add_counter("example", counter)
191
- registry.add_gauge("example", gauge)
192
- registry.add_timer("example", timer)
238
+ registry.add_counter(counter)
239
+ registry.add_gauge(gauge)
240
+ registry.add_timer(timer)
193
241
  ```
194
242
 
195
243
  Each meter type has an `add_meter`, `get_meter` and `remove_meter` method to manage meters in the `registry`, each requiring a unique meter name.
@@ -200,9 +248,9 @@ from pylemetry import registry
200
248
  from pylemetry.meters import Counter, MeterType
201
249
 
202
250
 
203
- counter = Counter()
251
+ counter = Counter("example")
204
252
 
205
- registry.add_meter("example", counter, MeterType.COUNTER)
253
+ registry.add_meter(counter, MeterType.COUNTER)
206
254
  registry.get_meter("example", MeterType.COUNTER)
207
255
  registry.remove_meter("example", MeterType.COUNTER)
208
256
  registry.get_meter("example", MeterType.COUNTER) # None
@@ -226,6 +274,7 @@ The message format allows for substitutions for metric values with the following
226
274
  | max | Maximum value of the meter, equivalent to the `value` substitution for `Counter` and `Gauge` meters, `max_tick_time` for `Timer` meters |
227
275
  | avg | Mean average value of the meter, equivalent to the `value` substitution for `Counter` and `Gauge` meters, `mean_tick_time` for `Timer` meters |
228
276
  | type | Type of the meter (`counter`, `gauge`, or `timer`) |
277
+ | tags | The tags associated with the meter |
229
278
 
230
279
  As an example, a `Counter` meter named `sample_counter` with a value of 10
231
280
  ```python
@@ -21,7 +21,7 @@ from pylemetry.meters import Counter
21
21
 
22
22
 
23
23
  def some_method() -> None:
24
- counter = Counter()
24
+ counter = Counter("example")
25
25
 
26
26
  for _ in range(100):
27
27
  counter.add() # counter += 1 is also supported
@@ -70,9 +70,9 @@ from pylemetry.meters import Gauge
70
70
 
71
71
 
72
72
  def some_method() -> None:
73
- gauge = Gauge()
73
+ gauge = Gauge("sample_gauge")
74
74
 
75
- registry.add_gauge("sample_gauge", gauge)
75
+ registry.add_gauge(gauge)
76
76
  ```
77
77
 
78
78
  The `Gauge` supports incrementing, decrementing, and setting a value directly
@@ -104,7 +104,7 @@ from pylemetry.meters import Timer
104
104
 
105
105
 
106
106
  def some_method() -> None:
107
- timer = Timer()
107
+ timer = Timer("example")
108
108
 
109
109
  for _ in range(100):
110
110
  with timer.time():
@@ -150,6 +150,54 @@ def main() -> None:
150
150
 
151
151
  When using this meter via a decorator, the meter gets added to the global `registry`, with the method name it's decorating as the meter name. Alternatively, you can provide a name for the meter as a parameter to the decorator
152
152
 
153
+ By default, timer meters will measure time in nanoseconds, this can be changed via the `unit` parameter using the `TimerUnits` enum in the utils module
154
+
155
+ ```python
156
+ import time
157
+
158
+ from pylemetry.meters import Timer
159
+ from pylemetry.utils import TimerUnits
160
+
161
+
162
+ timer_s = Timer("example_s", TimerUnits.SECONDS)
163
+ timer_ms = Timer("example_ms", TimerUnits.MILLISECONDS)
164
+
165
+ with timer_s.time():
166
+ time.sleep(1)
167
+
168
+ with timer_ms.time():
169
+ time.sleep(1)
170
+
171
+ timer_s.get_mean_tick_time() # 1
172
+ timer_ms.get_mean_tick_time() # 1000
173
+ ```
174
+
175
+ ## Tags
176
+
177
+ When creating a meter you can assign a set of tags to it as key-value pairs. The value must be one of either `str`, `int`, or `float`.
178
+ When using a decorator to create a meter, you can use a custom format for the value to extract values out of the method's args and kwargs
179
+
180
+ In order to allow for multiple meters with the same name and different tags, the name in the registry gets mangled with the tags to produce a unique name,
181
+ as a result when trying to get the meter from the registry you will need to provide both its name and its tags.
182
+
183
+ ```python
184
+ from pylemetry import registry
185
+ from pylemetry.decorators import time
186
+
187
+
188
+ @time("example_timer", tags={"tag_1": "args[0]", "tag_2": "kwargs[param_2]", "tag_3": "some value"})
189
+ def some_method(param_1: int, param_2: int) -> None:
190
+ ...
191
+
192
+
193
+ def main() -> None:
194
+ for _ in range(100):
195
+ some_method(1, param_2=2)
196
+
197
+ timer = registry.get_timer("example_timer", {"tag_1": 1, "tag_2": 2, "tag_3": "some value"})
198
+ timer.get_tags() # {"tag_1": 1, "tag_2": 2, "tag_3": "some value"}
199
+ ```
200
+
153
201
  ## The Registry
154
202
 
155
203
  Pylemetry maintains a global registry of meters, allowing you to share a meter across multiple files, or reference metrics from a central location.
@@ -160,13 +208,13 @@ from pylemetry import registry
160
208
  from pylemetry.meters import Counter, Gauge, Timer
161
209
 
162
210
 
163
- counter = Counter()
164
- gauge = Gauge()
165
- timer = Timer()
211
+ counter = Counter("example")
212
+ gauge = Gauge("example")
213
+ timer = Timer("example")
166
214
 
167
- registry.add_counter("example", counter)
168
- registry.add_gauge("example", gauge)
169
- registry.add_timer("example", timer)
215
+ registry.add_counter(counter)
216
+ registry.add_gauge(gauge)
217
+ registry.add_timer(timer)
170
218
  ```
171
219
 
172
220
  Each meter type has an `add_meter`, `get_meter` and `remove_meter` method to manage meters in the `registry`, each requiring a unique meter name.
@@ -177,9 +225,9 @@ from pylemetry import registry
177
225
  from pylemetry.meters import Counter, MeterType
178
226
 
179
227
 
180
- counter = Counter()
228
+ counter = Counter("example")
181
229
 
182
- registry.add_meter("example", counter, MeterType.COUNTER)
230
+ registry.add_meter(counter, MeterType.COUNTER)
183
231
  registry.get_meter("example", MeterType.COUNTER)
184
232
  registry.remove_meter("example", MeterType.COUNTER)
185
233
  registry.get_meter("example", MeterType.COUNTER) # None
@@ -203,6 +251,7 @@ The message format allows for substitutions for metric values with the following
203
251
  | max | Maximum value of the meter, equivalent to the `value` substitution for `Counter` and `Gauge` meters, `max_tick_time` for `Timer` meters |
204
252
  | avg | Mean average value of the meter, equivalent to the `value` substitution for `Counter` and `Gauge` meters, `mean_tick_time` for `Timer` meters |
205
253
  | type | Type of the meter (`counter`, `gauge`, or `timer`) |
254
+ | tags | The tags associated with the meter |
206
255
 
207
256
  As an example, a `Counter` meter named `sample_counter` with a value of 10
208
257
  ```python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pylemetry"
3
- version = "1.1.4"
3
+ version = "1.2.0"
4
4
  description = "A Python metrics library"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -0,0 +1,80 @@
1
+ import re
2
+
3
+ from typing import Callable, Optional, ParamSpec, TypeVar, Union
4
+
5
+ from functools import wraps
6
+
7
+ from pylemetry import registry
8
+ from pylemetry.meters import Counter
9
+
10
+
11
+ P = ParamSpec("P")
12
+ R = TypeVar("R", covariant=True)
13
+
14
+
15
+ def count(
16
+ name: Optional[str] = None, tags: Optional[dict[str, Union[str, int, float]]] = None
17
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
18
+ """
19
+ Decorator to count the number of invocations of a given callable. Creates a Counter meter in the Registry
20
+ with either the provided name or the fully qualified name of the callable object as the metric name.
21
+
22
+ :param name: Name of the meter to create, if None the function name is used
23
+ :param tags: Optional key-value pairs of tags for this meter
24
+ :return: Result of the wrapped function
25
+ """
26
+
27
+ def decorator(f: Callable[P, R]) -> Callable[P, R]:
28
+ @wraps(f)
29
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
30
+ counter_name = f.__qualname__ if name is None else name
31
+
32
+ _tags: dict[str, Union[str, int, float]] = {}
33
+
34
+ if tags:
35
+ for key, value in tags.items():
36
+ args_match = re.search(r"^args\[[0-9]+]", str(value))
37
+ kwargs_match = re.search(r"^kwargs\[[a-zA-Z0-9_]+]", str(value))
38
+
39
+ if args_match:
40
+ args_index = int(args_match.group().replace("args[", "").replace("]", ""))
41
+
42
+ if args_index >= len(args):
43
+ raise IndexError(f"args index {args_index} out of range, args has length {len(args)}")
44
+
45
+ if not isinstance(args[args_index], (str, int, float)):
46
+ raise ValueError(
47
+ f"Only args of types (str | int | float) may be added as tags. "
48
+ f"Got {args[args_index].__class__}"
49
+ )
50
+
51
+ _tags[key] = args[args_index] # type: ignore
52
+ elif kwargs_match:
53
+ kwargs_key = kwargs_match.group().replace("kwargs[", "").replace("]", "")
54
+
55
+ if kwargs_key not in kwargs:
56
+ raise KeyError(f"Key '{kwargs_key}' not present in kwargs")
57
+
58
+ if not isinstance(kwargs[kwargs_key], (str, int, float)):
59
+ raise ValueError(
60
+ f"Only kwargs of types (str | int | float) may be added as tags. "
61
+ f"Got {kwargs[kwargs_key].__class__}"
62
+ )
63
+
64
+ _tags[key] = kwargs[kwargs_key] # type: ignore
65
+ else:
66
+ _tags[key] = value
67
+
68
+ counter = registry.get_counter(counter_name, tags=_tags)
69
+
70
+ if not counter:
71
+ counter = Counter(name=counter_name, tags=_tags)
72
+ registry.add_counter(counter)
73
+
74
+ counter += 1
75
+
76
+ return f(*args, **kwargs)
77
+
78
+ return wrapper
79
+
80
+ return decorator
@@ -0,0 +1,83 @@
1
+ import re
2
+
3
+ from typing import Callable, Optional, ParamSpec, TypeVar, Union
4
+
5
+ from functools import wraps
6
+
7
+ from pylemetry import registry
8
+ from pylemetry.meters import Timer
9
+ from pylemetry.utils import TimerUnits
10
+
11
+
12
+ P = ParamSpec("P")
13
+ R = TypeVar("R", covariant=True)
14
+
15
+
16
+ def time(
17
+ name: Optional[str] = None,
18
+ unit: TimerUnits = TimerUnits.NANOSECONDS,
19
+ tags: Optional[dict[str, Union[str, int, float]]] = None,
20
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
21
+ """
22
+ Decorator to time the invocations of a given callable. Creates a Timer meter in the Registry with either the
23
+ provided name or the fully qualified name of the callable object as the metric name.
24
+
25
+ :param name: Name of the meter to create, if None the function name is used
26
+ :param unit: Unit of time to measure in
27
+ :param tags: Optional key-value pairs of tags for this meter
28
+ :return: Result of the wrapped function
29
+ """
30
+
31
+ def decorator(f: Callable[P, R]) -> Callable[P, R]:
32
+ @wraps(f)
33
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
34
+ time_name = f.__qualname__ if name is None else name
35
+
36
+ _tags: dict[str, Union[str, int, float]] = {}
37
+
38
+ if tags:
39
+ for key, value in tags.items():
40
+ args_match = re.search(r"^args\[[0-9]+]", str(value))
41
+ kwargs_match = re.search(r"^kwargs\[[a-zA-Z0-9_]+]", str(value))
42
+
43
+ if args_match:
44
+ args_index = int(args_match.group().replace("args[", "").replace("]", ""))
45
+
46
+ if args_index >= len(args):
47
+ raise IndexError(f"args index {args_index} out of range, args has length {len(args)}")
48
+
49
+ if not isinstance(args[args_index], (str, int, float)):
50
+ raise ValueError(
51
+ f"Only args of types (str | int | float) may be added as tags. "
52
+ f"Got {args[args_index].__class__}"
53
+ )
54
+
55
+ _tags[key] = args[args_index] # type: ignore
56
+ elif kwargs_match:
57
+ kwargs_key = kwargs_match.group().replace("kwargs[", "").replace("]", "")
58
+
59
+ if kwargs_key not in kwargs:
60
+ raise KeyError(f"Key '{kwargs_key}' not present in kwargs")
61
+
62
+ if not isinstance(kwargs[kwargs_key], (str, int, float)):
63
+ raise ValueError(
64
+ f"Only kwargs of types (str | int | float) may be added as tags. "
65
+ f"Got {kwargs[kwargs_key].__class__}"
66
+ )
67
+
68
+ _tags[key] = kwargs[kwargs_key] # type: ignore
69
+ else:
70
+ _tags[key] = value
71
+
72
+ _timer = registry.get_timer(time_name, tags=_tags)
73
+
74
+ if not _timer:
75
+ _timer = Timer(time_name, unit=unit, tags=_tags)
76
+ registry.add_timer(_timer)
77
+
78
+ with _timer.time():
79
+ return f(*args, **kwargs)
80
+
81
+ return wrapper
82
+
83
+ return decorator
@@ -1,9 +1,11 @@
1
+ from typing import Union, Optional
2
+
1
3
  from pylemetry.meters.meter import Meter, MeterType
2
4
 
3
5
 
4
6
  class Counter(Meter):
5
- def __init__(self) -> None:
6
- super().__init__(MeterType.COUNTER)
7
+ def __init__(self, name: str, tags: Optional[dict[str, Union[str, int, float]]] = None) -> None:
8
+ super().__init__(MeterType.COUNTER, name, tags)
7
9
 
8
10
  def add(self, value: int = 1) -> None:
9
11
  """
@@ -1,9 +1,11 @@
1
+ from typing import Union, Optional
2
+
1
3
  from pylemetry.meters.meter import Meter, MeterType
2
4
 
3
5
 
4
6
  class Gauge(Meter):
5
- def __init__(self) -> None:
6
- super().__init__(MeterType.GAUGE)
7
+ def __init__(self, name: str, tags: Optional[dict[str, Union[str, int, float]]] = None) -> None:
8
+ super().__init__(MeterType.GAUGE, name, tags)
7
9
 
8
10
  def set_value(self, value: float):
9
11
  """
@@ -1,5 +1,6 @@
1
1
  from enum import Enum
2
2
  from threading import Lock
3
+ from typing import Union, Optional
3
4
 
4
5
 
5
6
  class MeterType(Enum):
@@ -9,11 +10,19 @@ class MeterType(Enum):
9
10
 
10
11
 
11
12
  class Meter:
12
- def __init__(self, meter_type: MeterType) -> None:
13
+ def __init__(
14
+ self, meter_type: MeterType, name: str, tags: Optional[dict[str, Union[str, int, float]]] = None
15
+ ) -> None:
13
16
  self.lock = Lock()
14
17
  self.value = 0.0
15
18
  self.last_interval_value = 0.0
16
19
  self.meter_type = meter_type
20
+ self.name = name
21
+
22
+ if not tags:
23
+ tags = {}
24
+
25
+ self.__tags = tags
17
26
 
18
27
  def get_value(self, since_last_interval: bool = False) -> float:
19
28
  """
@@ -36,3 +45,6 @@ class Meter:
36
45
 
37
46
  with self.lock:
38
47
  self.last_interval_value = self.value
48
+
49
+ def get_tags(self) -> dict[str, Union[str, int, float]]:
50
+ return self.__tags
@@ -1,15 +1,23 @@
1
- from typing import Generator
1
+ from typing import Generator, Union, Optional
2
2
 
3
3
  import time
4
4
 
5
5
  from contextlib import contextmanager
6
6
 
7
7
  from pylemetry.meters.meter import Meter, MeterType
8
+ from pylemetry.utils import TimerUnits
8
9
 
9
10
 
10
11
  class Timer(Meter):
11
- def __init__(self) -> None:
12
- super().__init__(MeterType.TIMER)
12
+ def __init__(
13
+ self,
14
+ name: str,
15
+ unit: TimerUnits = TimerUnits.NANOSECONDS,
16
+ tags: Optional[dict[str, Union[str, int, float]]] = None,
17
+ ) -> None:
18
+ super().__init__(MeterType.TIMER, name, tags)
19
+
20
+ self.unit = unit
13
21
 
14
22
  self.ticks: list[float] = []
15
23
 
@@ -29,14 +37,14 @@ class Timer(Meter):
29
37
  Context manager to time in seconds a code block and add the result to the internal ticks list
30
38
  """
31
39
 
32
- start_time = time.perf_counter()
40
+ start_time = time.perf_counter_ns()
33
41
 
34
42
  try:
35
43
  yield
36
44
  finally:
37
- end_time = time.perf_counter()
45
+ end_time = time.perf_counter_ns()
38
46
 
39
- self.tick(end_time - start_time)
47
+ self.tick((end_time - start_time) / self.unit.conversion_from_ns())
40
48
 
41
49
  def get_value(self, since_last_interval: bool = False) -> int:
42
50
  """