Source code for hunter
from __future__ import absolute_import
import atexit
import inspect
import os
from functools import partial
from .actions import Action
from .actions import CodePrinter
from .actions import Debugger
from .actions import VarsPrinter
try:
if os.environ.get("PUREPYTHONHUNTER"):
raise ImportError("Skipped")
from ._predicates import And as _And
from ._predicates import Not
from ._predicates import Or as _Or
from ._predicates import When
from ._predicates import Query
from ._tracer import Tracer
except ImportError:
from .predicates import And as _And
from .predicates import Not
from .predicates import Or as _Or
from .predicates import When
from .predicates import Query
from .tracer import Tracer
__version__ = "1.0.2"
__all__ = (
'And',
'CodePrinter',
'Debugger',
'Not',
'Or',
'Q',
'Query',
'stop',
'trace',
'VarsPrinter',
'When',
)
[docs]def Q(*predicates, **query):
"""
Handles situations where :class:`hunter.Query` objects (or other callables) are passed in as positional arguments.
Conveniently converts that to an :class:`hunter.And` predicate.
"""
optional_actions = query.pop("actions", [])
if "action" in query:
optional_actions.append(query.pop("action"))
if predicates:
predicates = tuple(
p() if inspect.isclass(p) and issubclass(p, Action) else p
for p in predicates
)
if any(isinstance(p, CodePrinter) for p in predicates):
if CodePrinter in optional_actions:
optional_actions.remove(CodePrinter)
if query:
predicates += Query(**query),
result = And(*predicates)
else:
result = Query(**query)
if optional_actions:
result = When(result, *optional_actions)
return result
def _flatten(predicate, *predicates, **kwargs):
cls = kwargs.pop('cls')
if kwargs:
raise TypeError("Did not expecte keyword arguments")
if not predicates:
return predicate
else:
all_predicates = []
if isinstance(predicate, cls):
all_predicates.extend(predicate.predicates)
else:
all_predicates.append(predicate)
for p in predicates:
if isinstance(p, cls):
all_predicates.extend(p.predicates)
else:
all_predicates.append(p)
return cls(*all_predicates)
And = partial(_flatten, cls=_And)
Or = partial(_flatten, cls=_Or)
_current_tracer = None
[docs]def stop():
"""
Stop tracing.
Notes:
Restores previous tracer (if there was any).
"""
global _current_tracer
if _current_tracer is not None:
_current_tracer.stop()
_current_tracer = None
def _prepare_predicate(*predicates, **options):
if "action" not in options and "actions" not in options:
options["action"] = CodePrinter
return Q(*predicates, **options)
[docs]def trace(*predicates, **options):
"""
Starts tracing. Can be used as a context manager (with slightly incorrect semantics - it starts tracing
before ``__enter__`` is called).
Parameters:
*predicates (callables): Runs actions if any of the given predicates match.
Keyword Args:
clear_env_var: Disables tracing in subprocess. Default: ``False``.
action: Action to run if all the predicates return ``True``. Default: ``CodePrinter``.
actions: Actions to run (in case you want more than 1).
"""
global _current_tracer
predicate = _prepare_predicate(*predicates, **options)
clear_env_var = options.pop("clear_env_var", False)
if clear_env_var:
os.environ.pop("PYTHONHUNTER", None)
try:
_current_tracer = Tracer()
return _current_tracer.trace(predicate)
finally:
atexit.register(_current_tracer.stop)