Skip to content

Commit

Permalink
install debug into builtins via DebugProxy (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin authored Sep 3, 2023
1 parent f080d39 commit ec406ff
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ update-lockfiles:
.PHONY: format
format:
black $(sources)
ruff $(sources) --fix --exit-zero
ruff $(sources) --fix-only

.PHONY: lint
lint:
Expand Down
51 changes: 32 additions & 19 deletions devtools/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,32 @@
# language=python
install_code = """
# add devtools `debug` function to builtins
import sys
# we don't install here for pytest as it breaks pytest, it is
# installed later by a pytest fixture
if not sys.argv[0].endswith('pytest'):
import builtins
try:
from devtools import debug
except ImportError:
pass
else:
setattr(builtins, 'debug', debug)
# we don't want to import devtools until it's required since it breaks pytest, hence this proxy
class DebugProxy:
def __init__(self):
self._debug = None
def _import_debug(self):
if self._debug is None:
from devtools import debug
self._debug = debug
def __call__(self, *args, **kwargs):
self._import_debug()
kwargs['frame_depth_'] = 3
return self._debug(*args, **kwargs)
def format(self, *args, **kwargs):
self._import_debug()
kwargs['frame_depth_'] = 3
return self._debug.format(*args, **kwargs)
def __getattr__(self, item):
self._import_debug()
return getattr(self._debug, item)
import builtins
setattr(builtins, 'debug', DebugProxy())
"""


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


def install() -> int:
print('[WARNING: this command is experimental, report issues at github.com/samuelcolvin/python-devtools]\n')

if hasattr(builtins, 'debug'):
print('Looks like devtools is already installed.')
return 0

try:
import sitecustomize # type: ignore
except ImportError:
Expand All @@ -48,7 +57,11 @@ def install() -> int:
else:
install_path = Path(sitecustomize.__file__)

print(f'Found path "{install_path}" to install devtools into __builtins__')
if hasattr(builtins, 'debug'):
print(f'Looks like devtools is already installed, probably in `{install_path}`.')
return 0

print(f'Found path `{install_path}` to install devtools into `builtins`')
print('To install devtools, run the following command:\n')
print(f' python -m devtools print-code >> {install_path}\n')
if not install_path.is_relative_to(Path.home()):
Expand All @@ -65,5 +78,5 @@ def install() -> int:
elif 'print-code' in sys.argv:
sys.exit(print_code())
else:
print(f'python-devtools v{VERSION}, CLI usage: python -m devtools [install|print-code]')
print(f'python-devtools v{VERSION}, CLI usage: `python -m devtools install|print-code`')
sys.exit(1)
21 changes: 14 additions & 7 deletions devtools/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,15 @@ def __init__(self, *, warnings: 'Optional[bool]' = None, highlight: 'Optional[bo
self._show_warnings = env_bool(warnings, 'PY_DEVTOOLS_WARNINGS', True)
self._highlight = highlight

def __call__(self, *args: 'Any', file_: 'Any' = None, flush_: bool = True, **kwargs: 'Any') -> 'Any':
d_out = self._process(args, kwargs)
def __call__(
self,
*args: 'Any',
file_: 'Any' = None,
flush_: bool = True,
frame_depth_: int = 2,
**kwargs: 'Any',
) -> 'Any':
d_out = self._process(args, kwargs, frame_depth_)
s = d_out.str(use_highlight(self._highlight, file_))
print(s, file=file_, flush=flush_)
if kwargs:
Expand All @@ -123,8 +130,8 @@ def __call__(self, *args: 'Any', file_: 'Any' = None, flush_: bool = True, **kwa
else:
return args

def format(self, *args: 'Any', **kwargs: 'Any') -> DebugOutput:
return self._process(args, kwargs)
def format(self, *args: 'Any', frame_depth_: int = 2, **kwargs: 'Any') -> DebugOutput:
return self._process(args, kwargs, frame_depth_)

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

def _process(self, args: 'Any', kwargs: 'Any') -> DebugOutput:
def _process(self, args: 'Any', kwargs: 'Any', frame_depth: int) -> DebugOutput:
"""
BEWARE: this must be called from a function exactly 2 levels below the top of the stack.
BEWARE: this must be called from a function exactly `frame_depth` levels below the top of the stack.
"""
# HELP: any errors other than ValueError from _getframe? If so please submit an issue
try:
call_frame: 'FrameType' = sys._getframe(2)
call_frame: 'FrameType' = sys._getframe(frame_depth)
except ValueError:
# "If [ValueError] is deeper than the call stack, ValueError is raised"
return self.output_class(
Expand Down
2 changes: 1 addition & 1 deletion devtools/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = '0.12.1'
VERSION = '0.12.2'

0 comments on commit ec406ff

Please sign in to comment.