Welcome to Hunter’s documentation!

Contents:

Overview

Hunter

docs Documentation Status
tests
Travis-CI Build Status AppVeyor Build Status Requirements Status
Coverage Status Coverage Status
Code Quality Status Scrutinizer Status Codacy Code Quality Status CodeClimate Quality Status
package PyPI Package latest release PyPI Package monthly downloads PyPI Wheel Supported versions Supported implementations

Hunter is a flexible code tracing toolkit, not for measuring coverage, but for debugging, logging, inspection and other nefarious purposes. It has a simple Python API and a convenient terminal API (see Environment variable activation).

API is considered unstable until 1.0 is released.

  • Free software: BSD license

Installation

pip install hunter

Overview

The default action is to just print the code being executed. Example:

import hunter
hunter.trace(module='posixpath')

import os
os.path.join('a', 'b')

Would result in:

python2.7/posixpath.py:60    call      def join(a, *p):
python2.7/posixpath.py:64    line          path = a
python2.7/posixpath.py:65    line          for b in p:
python2.7/posixpath.py:66    line              if b.startswith('/'):
python2.7/posixpath.py:68    line              elif path == '' or path.endswith('/'):
python2.7/posixpath.py:71    line                  path += '/' + b
python2.7/posixpath.py:65    line          for b in p:
python2.7/posixpath.py:72    line          return path
python2.7/posixpath.py:72    return        return path
                             ...       return value: 'a/b'
  • or in a terminal:
https://raw.githubusercontent.com/ionelmc/python-hunter/master/docs/simple-trace.png

You can have custom actions, like a variable printer - example:

import hunter
hunter.trace(hunter.Q(module='posixpath', action=hunter.VarsPrinter('path')))

import os
os.path.join('a', 'b')

Would result in:

python2.7/posixpath.py:60    call      def join(a, *p):
python2.7/posixpath.py:64    line          path = a
                             vars      path => 'a'
python2.7/posixpath.py:65    line          for b in p:
                             vars      path => 'a'
python2.7/posixpath.py:66    line              if b.startswith('/'):
                             vars      path => 'a'
python2.7/posixpath.py:68    line              elif path == '' or path.endswith('/'):
                             vars      path => 'a'
python2.7/posixpath.py:71    line                  path += '/' + b
                             vars      path => 'a/b'
python2.7/posixpath.py:65    line          for b in p:
                             vars      path => 'a/b'
python2.7/posixpath.py:72    line          return path
                             vars      path => 'a/b'
python2.7/posixpath.py:72    return        return path
                             ...       return value: 'a/b'
  • or in a terminal:
https://raw.githubusercontent.com/ionelmc/python-hunter/master/docs/vars-trace.png

You can give it a tree-like configuration where you can optionally configure specific actions for parts of the tree (like dumping variables or a pdb set_trace):

TODO: More examples.
Environment variable activation

For your convenience environment variable activation is available. Just run your app like this:

PYTHONHUNTER="module='os.path'" python yourapp.py

On Windows you’d do something like:

set PYTHONHUNTER=module='os.path'
python yourapp.py

The activation works with a clever .pth file that checks for that env var presence and before your app runs does something like this:

from hunter import *
trace(<whatever-you-had-in-the-PYTHONHUNTER-env-var>)

That also means that it will do activation even if the env var is empty, eg: PYTHONHUNTER="".

Development

To run the all tests run:

tox

Installation

At the command line:

pip install hunter

Introduction

Installation

To install hunter run:

pip install hunter

The trace function

The hunter.trace function can take 2 types of arguments:

Note that hunter.trace will use hunter.Q when you pass multiple positional arguments or keyword arguments.

The Q function

The hunter.Q function provides a convenience API for you:

  • Q(module='foobar') is converted to Query(module='foobar').
  • Q(module='foobar', action=Debugger) is converted to When(Query(module='foobar'), Debugger).
  • Q(module='foobar', actions=[CodePrinter, VarsPrinter('name')]) is converted to When(Query(module='foobar'), CodePrinter, VarsPrinter('name')).
  • Q(Q(module='foo'), Q(module='bar')) is converted to Or(Q(module='foo'), Q(module='bar')).
  • Q(your_own_callback, module='foo') is converted to Or(your_own_callback, Q(module='foo')).

Note that the default junction hunter.Q uses is hunter.Or.

The builtin predicates and actions

All the builtin predicates (hunter.Query, hunter.When, hunter.And and hunter.Or) support the | and & operators:

  • Query(module='foo') | Query(module='bar') is converted to Or(Query(module='foo'), Query(module='bar'))
  • Query(module='foo') & Query(module='bar') is converted to And(Query(module='foo'), Query(module='bar'))

Activation

You can activate Hunter in two ways.

via code

import hunter
hunter.trace(
    ...
)

via environment variable

Set the PYTHONHUNTER environment variable. Eg:

PYTHONHUNTER="module='os.path'" python yourapp.py

On Windows you’d do something like:

set PYTHONHUNTER=module='os.path'
python yourapp.py

The activation works with a clever .pth file that checks for that env var presence and before your app runs does something like this:

from hunter import *
trace(
    <whatever-you-had-in-the-PYTHONHUNTER-env-var>
)

That also means that it will do activation even if the env var is empty, eg: PYTHONHUNTER="".

Cookbook

TODO

Reference

hunter

Functions

hunter.trace Starts tracing.
hunter.stop Stop tracing.
hunter.Q Handles situations where hunter.Query objects (or other callables) are passed in as positional arguments.

Predicates

hunter.Query A query class.
hunter.When Runs actions when condition(event) is True.
hunter.And And predicate. Exits at the first sub-predicate that returns False.
hunter.Or Or predicate. Exits at first sub-predicate that returns True.

Actions

hunter.CodePrinter An action that just prints the code being executed.
hunter.Debugger An action that starts pdb.
hunter.VarsPrinter An action that prints local variables and optionally global variables visible from the current executing frame.

Objects

hunter.Event Event wrapper for frame, kind, arg (the arguments the settrace function gets).
hunter.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 (hunter.Q instances) – Runs actions if any of the given predicates match.
  • options – Keyword arguments that are passed to hunter.Q, for convenience.
hunter.stop()

Stop tracing. Restores previous tracer (if any).

class hunter.Q[source]

Handles situations where hunter.Query objects (or other callables) are passed in as positional arguments. Conveniently converts that to an hunter.Or predicate.

class hunter.Query(**query)[source]

A query class.

See hunter.Event for fields that can be filtered on.

__and__(other)[source]

Convenience API so you can do Q() & Q(). It converts that to And(Q(), Q()).

__call__(event)[source]

Handles event. Returns True if all criteria matched.

__init__(**query)[source]
Parameters:query

criteria to match on.

Accepted arguments: arg, code, filename, frame, fullsource, function, globals, kind, lineno, locals, module, source, stdlib, tracer.

__or__(other)[source]

Convenience API so you can do Q() | Q(). It converts that to Or(Q(), Q()).

class hunter.When(condition, *actions)[source]

Runs actions when condition(event) is True.

Actions take a single event argument.

__call__(event)[source]

Handles the event.

class hunter.And(*predicates)[source]

And predicate. Exits at the first sub-predicate that returns False.

__call__(event)[source]

Handles the event.

class hunter.Or(*predicates)[source]

Or predicate. Exits at first sub-predicate that returns True.

__call__(event)[source]

Handles the event.

class hunter.CodePrinter(stream=<open file '<stderr>', mode 'w'>, force_colors=False, filename_alignment=40)[source]

An action that just prints the code being executed.

Parameters:
  • stream (file-like) – Stream to write to. Default: sys.stderr.
  • filename_alignment (int) – Default size for the filename column (files are right-aligned). Default: 40.
__call__(event, sep='/', join=<function join>)[source]

Handle event and print filename, line number and source code. If event.kind is a return or exception also prints values.

class hunter.Debugger(klass=<class pdb.Pdb>, **kwargs)[source]

An action that starts pdb.

__call__(event)[source]

Runs a pdb.set_trace at the matching frame.

class hunter.VarsPrinter(*names, **options)[source]

An action that prints local variables and optionally global variables visible from the current executing frame.

Parameters:
  • *names (strings) – Names to evaluate. Expressions can be used (will only try to evaluate if all the variables are present on the frame.
  • stream (file-like) – Stream to write to. Default: sys.stderr.
  • filename_alignment (int) – Default size for the filaneme column (files are right-aligned). Default: 40.
  • globals (bool) – Allow access to globals. Default: False (only looks at locals).
__call__(event)[source]

Handle event and print the specified variables.

static _iter_symbols(code)[source]

Iterate all the variable names in the given expression.

Example:

  • self.foobar yields self
  • self[foobar] yields self` and foobar
_safe_eval(code, event)[source]

Try to evaluate the given code on the given frame. If failure occurs, returns some ugly string with exception.

class hunter.Event(frame, kind, arg, tracer)[source]

Event wrapper for frame, kind, arg (the arguments the settrace function gets).

Provides few convenience properties.

code[source]

A code object (not a string).

filename[source]

A string with absolute path to file.

fullsource[source]

A string with the sourcecode for the current statement (from linecache - failures are ignored).

May include multiple lines if it’s a class/function definition (will include decorators).

function[source]

A string with function name.

globals[source]

A dict with global variables.

lineno[source]

An integer with line number in file.

locals[source]

A dict with local variables.

module[source]

A string with module name (eg"foo.bar").

source[source]

A string with the sourcecode for the current line (from linecache - failures are ignored).

Fast but sometimes incomplete.

stdlib[source]

A boolean flag. True if frame is in stdlib.

Contributing

Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

Bug reports

When reporting a bug please include:

  • Your operating system name and version.
  • Any details about your local setup that might be helpful in troubleshooting.
  • Detailed steps to reproduce the bug.

Documentation improvements

Hunter could always use more documentation, whether as part of the official Hunter docs, in docstrings, or even on the web in blog posts, articles, and such.

Feature requests and feedback

The best way to send feedback is to file an issue at https://github.com/ionelmc/python-hunter/issues.

If you are proposing a feature:

  • Explain in detail how it would work.
  • Keep the scope as narrow as possible, to make it easier to implement.
  • Remember that this is a volunteer-driven project, and that contributions are welcome :)

Development

To set up python-hunter for local development:

  1. Fork python-hunter on GitHub.

  2. Clone your fork locally:

    git clone git@github.com:your_name_here/python-hunter.git
    
  3. Create a branch for local development:

    git checkout -b name-of-your-bugfix-or-feature
    

    Now you can make your changes locally.

  4. When you’re done making changes, run all the checks, doc builder and spell checker with tox one command:

    tox
    
  5. Commit your changes and push your branch to GitHub:

    git add .
    git commit -m "Your detailed description of your changes."
    git push origin name-of-your-bugfix-or-feature
    
  6. Submit a pull request through the GitHub website.

Pull Request Guidelines

If you need some code review or feedback while you’re developing the code just make the pull request.

For merging, you should:

  1. Include passing tests (run tox) [1].
  2. Update documentation when there’s new API, functionality etc.
  3. Add a note to CHANGELOG.rst about the changes.
  4. Add yourself to AUTHORS.rst.
[1]

If you don’t have all the necessary python versions available locally you can rely on Travis - it will run the tests for each change you add in the pull request.

It will be slower though ...

Tips

To run a subset of tests:

tox -e envname -- py.test -k test_myfeature

To run all the test environments in parallel (you need to pip install detox):

detox

Authors

Changelog

0.6.0 (2015-10-10)

  • Added a clear_env_var option on the tracer (disables tracing in subprocess).
  • Added force_colors option on VarsPrinter and CodePrinter.
  • Allowed setting the stream to a file name (option on VarsPrinter and CodePrinter).
  • Bumped up the filename alignment to 40 cols.
  • If not merging then self is not kept as a previous tracer anymore. Closes #16.
  • Fixed handling in VarsPrinter: properly print eval errors and don’t try to show anything if there’s an AttributeError. Closes #18.
  • Added a stdlib boolean flag (for filtering purposes). Closes #15.
  • Fixed broken frames that have “None” for filename or module (so they can still be treated as strings).
  • Corrected output files in the install_lib command so that pip can uninstall the pth file. This only works when it’s installed with pip (sadly, setup.py install/develop and pip install -e will still leave pth garbage on pip uninstall hunter).

0.5.1 (2015-04-15)

  • Fixed Event.globals to actually be the dict of global vars (it was just the locals).

0.5.0 (2015-04-06)

  • Fixed And and Or “single argument unwrapping”.
  • Implemented predicate compression. Example: Or(Or(a, b), c) is converted to Or(a, b, c).
  • Renamed the Event.source to Event.fullsource.
  • Added Event.source that doesn’t do any fancy sourcecode tokenization.
  • Fixed Event.fullsource return value for situations where the tokenizer would fail.
  • Made the print function available in the PYTHONHUNTER env var payload.
  • Added a __repr__ for Event.

0.4.0 (2015-03-29)

  • Disabled colors for Jython (contributed by Claudiu Popa in #12).
  • Test suite fixes for Windows (contributed by Claudiu Popa in #11).
  • Added an introduction section in the docs.
  • Implemented a prettier fallback for when no sources are available for that frame.
  • Implemented fixups in cases where you use action classes as a predicates.

0.3.1 (2015-03-29)

  • Forgot to merge some commits ...

0.3.0 (2015-03-29)

  • Added handling for internal repr failures.
  • Fixed issues with displaying code that has non-ascii characters.
  • Implemented better display for call frames so that when a function has decorators the function definition is shown (instead of just the first decorator). See: #8.

0.2.1 (2015-03-28)

  • Added missing color entry for exception events.
  • Added Event.line property. It returns the source code for the line being run.

0.2.0 (2015-03-27)

  • Added color support (and colorama as dependency).
  • Added support for expressions in VarsPrinter.
  • Breaking changes:
    • Renamed F to Q. And Q is now just a convenience wrapper for Query.
    • Renamed the PYTHON_HUNTER env variable to PYTHONHUNTER.
    • Changed When to take positional arguments.
    • Changed output to show 2 path components (still not configurable).
    • Changed VarsPrinter to take positional arguments for the names.
  • Improved error reporting for env variable activation (PYTHONHUNTER).
  • Fixed env var activator (the .pth file) installation with setup.py install (the “egg installs”) and setup.py develop/pip install -e (the “egg links”).

0.1.0 (2015-03-22)

  • First release on PyPI.

Indices and tables