gadget-timer 0.1.0__py3-none-any.whl

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.
gadget.py ADDED
@@ -0,0 +1,103 @@
1
+ import inspect
2
+ import time
3
+ import sys
4
+ import os
5
+ import shutil
6
+ import re
7
+
8
+ __version__ = "0.1.0"
9
+
10
+ class Gadget:
11
+ def __init__(self, verbose=True):
12
+ self.verbose = verbose
13
+ self.t0 = None
14
+ self.group_times = {}
15
+
16
+ def __call__(self, s='', group=None, _caller_frame=None):
17
+ """Make the instance callable like a function."""
18
+ if not self.verbose:
19
+ return
20
+
21
+ current_time = time.time()
22
+ if self.t0 is None:
23
+ self.t0 = current_time
24
+ elapsed = current_time - self.t0
25
+ self.t0 = time.time()
26
+
27
+ # Use provided caller frame or get it from stack
28
+ if _caller_frame is None:
29
+ caller_frame = inspect.currentframe().f_back
30
+ else:
31
+ caller_frame = _caller_frame
32
+ frame_info = inspect.getframeinfo(caller_frame)
33
+
34
+ line_number = frame_info.lineno
35
+
36
+ try:
37
+ with open(frame_info.filename, 'r') as f:
38
+ lines = f.readlines()
39
+ line_content = lines[line_number - 2].rstrip()
40
+ except:
41
+ line_content = ""
42
+
43
+ green_color = "\033[32m"
44
+ reset_color = "\033[0m"
45
+
46
+ # Shorten the file path - make it relative to cwd or just use filename
47
+ try:
48
+ short_path = os.path.relpath(frame_info.filename)
49
+ except:
50
+ short_path = os.path.basename(frame_info.filename)
51
+
52
+ # Clickable file path for VS Code
53
+ file_link = f"{short_path}:{line_number}"
54
+
55
+ # Handle group tracking
56
+ group_info = ""
57
+ if group:
58
+ if group not in self.group_times:
59
+ self.group_times[group] = 0.0
60
+ self.group_times[group] += elapsed
61
+ group_info = f" [{group}: {elapsed:.6f}s (total: {self.group_times[group]:.6f}s)]"
62
+
63
+ # Build the left side of the output
64
+ left_output = f"{green_color}[line={line_number}] {elapsed:.6f}s {line_content}{reset_color} {s}{group_info}"
65
+
66
+ # Get terminal width and right-align the file link
67
+ terminal_width = shutil.get_terminal_size().columns
68
+ # Strip ANSI codes for accurate length calculation
69
+ left_output_plain = re.sub(r'\033\[[0-9;]+m', '', left_output)
70
+ left_length = len(left_output_plain)
71
+
72
+ # Calculate padding needed
73
+ file_link_display = f"→ {file_link}"
74
+ padding_needed = terminal_width - left_length - len(file_link_display) - 1
75
+ padding = " " * max(1, padding_needed) # At least one space
76
+
77
+ output = f"{left_output}{padding}{file_link_display}"
78
+ print(output)
79
+
80
+ def reset(self, group=None):
81
+ """Reset group timer(s). If group is None, reset all groups."""
82
+ if group is None:
83
+ self.group_times = {}
84
+ elif group in self.group_times:
85
+ del self.group_times[group]
86
+
87
+ # Create a default instance for convenience
88
+ _default_gadget = Gadget()
89
+
90
+ # Convenience functions that use the default instance
91
+ def gadget(s='', group=None):
92
+ """Convenience function using default Gadget instance."""
93
+ caller_frame = inspect.currentframe().f_back
94
+ _default_gadget(s, group, _caller_frame=caller_frame)
95
+
96
+ def gadget_reset(group=None):
97
+ """Convenience function using default Gadget instance."""
98
+ _default_gadget.reset(group)
99
+
100
+ def gadget_config(verbose=True):
101
+ """Configure the default Gadget instance."""
102
+ global _default_gadget
103
+ _default_gadget = Gadget(verbose=verbose)
@@ -0,0 +1,100 @@
1
+ Metadata-Version: 2.4
2
+ Name: gadget-timer
3
+ Version: 0.1.0
4
+ Summary: Tiny timing helper for printing elapsed times with call-site context
5
+ Author: Sam Maddrell-Mander
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/s-maddrellmander/gadget
8
+ Project-URL: Repository, https://github.com/s-maddrellmander/gadget
9
+ Project-URL: Issues, https://github.com/s-maddrellmander/gadget/issues
10
+ Keywords: timing,debugging,profiling,logging
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8.0; extra == "dev"
26
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # gadget
30
+
31
+ [![Tests](https://github.com/s-maddrellmander/gadget/actions/workflows/tests.yml/badge.svg)](https://github.com/s-maddrellmander/gadget/actions/workflows/tests.yml)
32
+
33
+ A tiny helper for quick timing/debug prints with file and line context.
34
+
35
+ ## Install
36
+
37
+ ### From a local checkout
38
+
39
+ ```bash
40
+ pip install .
41
+ ```
42
+
43
+ ### Directly from GitHub
44
+
45
+ ```bash
46
+ pip install "git+https://github.com/s-maddrellmander/gadget.git"
47
+ ```
48
+
49
+ ### Editable install (for development)
50
+
51
+ ```bash
52
+ pip install -e .
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ ### Functional API
58
+
59
+ ```python
60
+ from gadget import gadget, gadget_reset, gadget_config
61
+
62
+ gadget("start")
63
+ # ... some code ...
64
+ gadget("after step")
65
+
66
+ gadget("phase 1", group="build")
67
+ gadget("phase 2", group="build")
68
+ gadget_reset("build")
69
+
70
+ gadget_config(verbose=False) # disable output globally
71
+ ```
72
+
73
+ ### Class-based API
74
+
75
+ ```python
76
+ from gadget import Gadget
77
+
78
+ timer = Gadget(verbose=True)
79
+ timer("start")
80
+ # ... some code ...
81
+ timer("step", group="build")
82
+ timer.reset("build")
83
+ timer.reset() # reset all groups
84
+ ```
85
+
86
+ ## Install from PyPI
87
+
88
+ ```bash
89
+ pip install gadget-timer
90
+ ```
91
+
92
+ ## Notes
93
+
94
+ - Package name on PyPI is `gadget-timer`.
95
+ - Import path is still `gadget`.
96
+ - For Git installs, the repository URL is:
97
+
98
+ ```bash
99
+ pip install "git+https://github.com/s-maddrellmander/gadget.git"
100
+ ```
@@ -0,0 +1,6 @@
1
+ gadget.py,sha256=C-QVeFFWP8xiEJ5kwjC_v2E4GhNLJ6A7G_mjUGgC2M0,3496
2
+ gadget_timer-0.1.0.dist-info/licenses/LICENSE,sha256=thdH96H8A1NZAoCIs6zHdo04Zwgeh8KR3NSH0EIMCc0,1076
3
+ gadget_timer-0.1.0.dist-info/METADATA,sha256=Y386HXXZtklCpQTbCj8zkL1mDd7HtBs7l-EVNEEEO_4,2415
4
+ gadget_timer-0.1.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
5
+ gadget_timer-0.1.0.dist-info/top_level.txt,sha256=RSJrRIfV7Bzp-NvVAfoFNnVV0rxuIpl2Xkxq0F7p-pw,7
6
+ gadget_timer-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sam Maddrell-Mander
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ gadget