Skip to content

Commit ec406ff

Browse files
authored
install debug into builtins via DebugProxy (#139)
1 parent f080d39 commit ec406ff

File tree

4 files changed

+48
-28
lines changed

4 files changed

+48
-28
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ update-lockfiles:
2525
.PHONY: format
2626
format:
2727
black $(sources)
28-
ruff $(sources) --fix --exit-zero
28+
ruff $(sources) --fix-only
2929

3030
.PHONY: lint
3131
lint:

devtools/__main__.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,32 @@
77
# language=python
88
install_code = """
99
# add devtools `debug` function to builtins
10-
import sys
11-
# we don't install here for pytest as it breaks pytest, it is
12-
# installed later by a pytest fixture
13-
if not sys.argv[0].endswith('pytest'):
14-
import builtins
15-
try:
16-
from devtools import debug
17-
except ImportError:
18-
pass
19-
else:
20-
setattr(builtins, 'debug', debug)
10+
# we don't want to import devtools until it's required since it breaks pytest, hence this proxy
11+
class DebugProxy:
12+
def __init__(self):
13+
self._debug = None
14+
15+
def _import_debug(self):
16+
if self._debug is None:
17+
from devtools import debug
18+
self._debug = debug
19+
20+
def __call__(self, *args, **kwargs):
21+
self._import_debug()
22+
kwargs['frame_depth_'] = 3
23+
return self._debug(*args, **kwargs)
24+
25+
def format(self, *args, **kwargs):
26+
self._import_debug()
27+
kwargs['frame_depth_'] = 3
28+
return self._debug.format(*args, **kwargs)
29+
30+
def __getattr__(self, item):
31+
self._import_debug()
32+
return getattr(self._debug, item)
33+
34+
import builtins
35+
setattr(builtins, 'debug', DebugProxy())
2136
"""
2237

2338

@@ -27,12 +42,6 @@ def print_code() -> int:
2742

2843

2944
def install() -> int:
30-
print('[WARNING: this command is experimental, report issues at github.com/samuelcolvin/python-devtools]\n')
31-
32-
if hasattr(builtins, 'debug'):
33-
print('Looks like devtools is already installed.')
34-
return 0
35-
3645
try:
3746
import sitecustomize # type: ignore
3847
except ImportError:
@@ -48,7 +57,11 @@ def install() -> int:
4857
else:
4958
install_path = Path(sitecustomize.__file__)
5059

51-
print(f'Found path "{install_path}" to install devtools into __builtins__')
60+
if hasattr(builtins, 'debug'):
61+
print(f'Looks like devtools is already installed, probably in `{install_path}`.')
62+
return 0
63+
64+
print(f'Found path `{install_path}` to install devtools into `builtins`')
5265
print('To install devtools, run the following command:\n')
5366
print(f' python -m devtools print-code >> {install_path}\n')
5467
if not install_path.is_relative_to(Path.home()):
@@ -65,5 +78,5 @@ def install() -> int:
6578
elif 'print-code' in sys.argv:
6679
sys.exit(print_code())
6780
else:
68-
print(f'python-devtools v{VERSION}, CLI usage: python -m devtools [install|print-code]')
81+
print(f'python-devtools v{VERSION}, CLI usage: `python -m devtools install|print-code`')
6982
sys.exit(1)

devtools/debug.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,15 @@ def __init__(self, *, warnings: 'Optional[bool]' = None, highlight: 'Optional[bo
112112
self._show_warnings = env_bool(warnings, 'PY_DEVTOOLS_WARNINGS', True)
113113
self._highlight = highlight
114114

115-
def __call__(self, *args: 'Any', file_: 'Any' = None, flush_: bool = True, **kwargs: 'Any') -> 'Any':
116-
d_out = self._process(args, kwargs)
115+
def __call__(
116+
self,
117+
*args: 'Any',
118+
file_: 'Any' = None,
119+
flush_: bool = True,
120+
frame_depth_: int = 2,
121+
**kwargs: 'Any',
122+
) -> 'Any':
123+
d_out = self._process(args, kwargs, frame_depth_)
117124
s = d_out.str(use_highlight(self._highlight, file_))
118125
print(s, file=file_, flush=flush_)
119126
if kwargs:
@@ -123,8 +130,8 @@ def __call__(self, *args: 'Any', file_: 'Any' = None, flush_: bool = True, **kwa
123130
else:
124131
return args
125132

126-
def format(self, *args: 'Any', **kwargs: 'Any') -> DebugOutput:
127-
return self._process(args, kwargs)
133+
def format(self, *args: 'Any', frame_depth_: int = 2, **kwargs: 'Any') -> DebugOutput:
134+
return self._process(args, kwargs, frame_depth_)
128135

129136
def breakpoint(self) -> None:
130137
import pdb
@@ -134,13 +141,13 @@ def breakpoint(self) -> None:
134141
def timer(self, name: 'Optional[str]' = None, *, verbose: bool = True, file: 'Any' = None, dp: int = 3) -> Timer:
135142
return Timer(name=name, verbose=verbose, file=file, dp=dp)
136143

137-
def _process(self, args: 'Any', kwargs: 'Any') -> DebugOutput:
144+
def _process(self, args: 'Any', kwargs: 'Any', frame_depth: int) -> DebugOutput:
138145
"""
139-
BEWARE: this must be called from a function exactly 2 levels below the top of the stack.
146+
BEWARE: this must be called from a function exactly `frame_depth` levels below the top of the stack.
140147
"""
141148
# HELP: any errors other than ValueError from _getframe? If so please submit an issue
142149
try:
143-
call_frame: 'FrameType' = sys._getframe(2)
150+
call_frame: 'FrameType' = sys._getframe(frame_depth)
144151
except ValueError:
145152
# "If [ValueError] is deeper than the call stack, ValueError is raised"
146153
return self.output_class(

devtools/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = '0.12.1'
1+
VERSION = '0.12.2'

0 commit comments

Comments
 (0)