hunter


Namehunter JSON
Version 3.7.0 PyPI version JSON
download
home_pagehttps://github.com/ionelmc/python-hunter
SummaryHunter is a flexible code tracing toolkit.
upload_time2024-05-02 14:07:03
maintainerNone
docs_urlNone
authorIonel Cristian Mărieș
requires_python>=3.8
licenseBSD-2-Clause
keywords trace tracer settrace debugger debugging code source
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            ========
Overview
========



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 <https://python-hunter.readthedocs.io/en/latest/introduction.html>`_,
a `convenient terminal API <environment-variable-activation_>`_ and
a `CLI tool to attach to processes <tracing-processes_>`_.

* Free software: BSD 2-Clause License

Installation
============

::

    pip install hunter

Documentation
=============


https://python-hunter.readthedocs.io/

Getting started
===============

Basic use involves passing various filters to the ``trace`` option. An example:

.. sourcecode:: python

    import hunter
    hunter.trace(module='posixpath', action=hunter.CallPrinter)

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

That would result in:

.. sourcecode:: pycon

    >>> os.path.join('a', 'b')
             /usr/lib/python3.6/posixpath.py:75    call      => join(a='a')
             /usr/lib/python3.6/posixpath.py:80    line         a = os.fspath(a)
             /usr/lib/python3.6/posixpath.py:81    line         sep = _get_sep(a)
             /usr/lib/python3.6/posixpath.py:41    call         => _get_sep(path='a')
             /usr/lib/python3.6/posixpath.py:42    line            if isinstance(path, bytes):
             /usr/lib/python3.6/posixpath.py:45    line            return '/'
             /usr/lib/python3.6/posixpath.py:45    return       <= _get_sep: '/'
             /usr/lib/python3.6/posixpath.py:82    line         path = a
             /usr/lib/python3.6/posixpath.py:83    line         try:
             /usr/lib/python3.6/posixpath.py:84    line         if not p:
             /usr/lib/python3.6/posixpath.py:86    line         for b in map(os.fspath, p):
             /usr/lib/python3.6/posixpath.py:87    line         if b.startswith(sep):
             /usr/lib/python3.6/posixpath.py:89    line         elif not path or path.endswith(sep):
             /usr/lib/python3.6/posixpath.py:92    line         path += sep + b
             /usr/lib/python3.6/posixpath.py:86    line         for b in map(os.fspath, p):
             /usr/lib/python3.6/posixpath.py:96    line         return path
             /usr/lib/python3.6/posixpath.py:96    return    <= join: 'a/b'
    'a/b'

In a terminal it would look like:

.. image:: https://raw.githubusercontent.com/ionelmc/python-hunter/master/docs/code-trace.png


Another useful scenario is to ignore all standard modules and force colors to make them stay even if the output is
redirected to a file.

.. sourcecode:: python

    import hunter
    hunter.trace(stdlib=False, action=hunter.CallPrinter(force_colors=True))

Actions
-------

Output format can be controlled with "actions". There's an alternative ``CodePrinter`` action that doesn't handle
nesting (it was the default action until Hunter 2.0).

If filters match then action will be run. Example:

.. sourcecode:: python

    import hunter
    hunter.trace(module='posixpath', action=hunter.CodePrinter)

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

That would result in:

.. sourcecode:: pycon

    >>> os.path.join('a', 'b')
             /usr/lib/python3.6/posixpath.py:75    call      def join(a, *p):
             /usr/lib/python3.6/posixpath.py:80    line          a = os.fspath(a)
             /usr/lib/python3.6/posixpath.py:81    line          sep = _get_sep(a)
             /usr/lib/python3.6/posixpath.py:41    call      def _get_sep(path):
             /usr/lib/python3.6/posixpath.py:42    line          if isinstance(path, bytes):
             /usr/lib/python3.6/posixpath.py:45    line              return '/'
             /usr/lib/python3.6/posixpath.py:45    return            return '/'
                                                   ...       return value: '/'
             /usr/lib/python3.6/posixpath.py:82    line          path = a
             /usr/lib/python3.6/posixpath.py:83    line          try:
             /usr/lib/python3.6/posixpath.py:84    line              if not p:
             /usr/lib/python3.6/posixpath.py:86    line              for b in map(os.fspath, p):
             /usr/lib/python3.6/posixpath.py:87    line                  if b.startswith(sep):
             /usr/lib/python3.6/posixpath.py:89    line                  elif not path or path.endswith(sep):
             /usr/lib/python3.6/posixpath.py:92    line                      path += sep + b
             /usr/lib/python3.6/posixpath.py:86    line              for b in map(os.fspath, p):
             /usr/lib/python3.6/posixpath.py:96    line          return path
             /usr/lib/python3.6/posixpath.py:96    return        return path
                                                   ...       return value: 'a/b'
    'a/b'

- or in a terminal:

.. image:: https://raw.githubusercontent.com/ionelmc/python-hunter/master/docs/simple-trace.png

------

Another useful action is the ``VarsPrinter``:

.. sourcecode:: python

    import hunter
    # note that this kind of invocation will also use the default `CallPrinter` action
    hunter.trace(hunter.Q(module='posixpath', action=hunter.VarsPrinter('path')))

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

That would result in:

.. sourcecode:: pycon

    >>> os.path.join('a', 'b')
         /usr/lib/python3.6/posixpath.py:75    call      => join(a='a')
         /usr/lib/python3.6/posixpath.py:80    line         a = os.fspath(a)
         /usr/lib/python3.6/posixpath.py:81    line         sep = _get_sep(a)
         /usr/lib/python3.6/posixpath.py:41    call      [path => 'a']
         /usr/lib/python3.6/posixpath.py:41    call         => _get_sep(path='a')
         /usr/lib/python3.6/posixpath.py:42    line      [path => 'a']
         /usr/lib/python3.6/posixpath.py:42    line            if isinstance(path, bytes):
         /usr/lib/python3.6/posixpath.py:45    line      [path => 'a']
         /usr/lib/python3.6/posixpath.py:45    line            return '/'
         /usr/lib/python3.6/posixpath.py:45    return    [path => 'a']
         /usr/lib/python3.6/posixpath.py:45    return       <= _get_sep: '/'
         /usr/lib/python3.6/posixpath.py:82    line         path = a
         /usr/lib/python3.6/posixpath.py:83    line      [path => 'a']
         /usr/lib/python3.6/posixpath.py:83    line         try:
         /usr/lib/python3.6/posixpath.py:84    line      [path => 'a']
         /usr/lib/python3.6/posixpath.py:84    line         if not p:
         /usr/lib/python3.6/posixpath.py:86    line      [path => 'a']
         /usr/lib/python3.6/posixpath.py:86    line         for b in map(os.fspath, p):
         /usr/lib/python3.6/posixpath.py:87    line      [path => 'a']
         /usr/lib/python3.6/posixpath.py:87    line         if b.startswith(sep):
         /usr/lib/python3.6/posixpath.py:89    line      [path => 'a']
         /usr/lib/python3.6/posixpath.py:89    line         elif not path or path.endswith(sep):
         /usr/lib/python3.6/posixpath.py:92    line      [path => 'a']
         /usr/lib/python3.6/posixpath.py:92    line         path += sep + b
         /usr/lib/python3.6/posixpath.py:86    line      [path => 'a/b']
         /usr/lib/python3.6/posixpath.py:86    line         for b in map(os.fspath, p):
         /usr/lib/python3.6/posixpath.py:96    line      [path => 'a/b']
         /usr/lib/python3.6/posixpath.py:96    line         return path
         /usr/lib/python3.6/posixpath.py:96    return    [path => 'a/b']
         /usr/lib/python3.6/posixpath.py:96    return    <= join: 'a/b'
    'a/b'

In a terminal it would look like:

.. image:: 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):

.. sourcecode:: python

    from hunter import trace, Q, Debugger
    from pdb import Pdb

    trace(
        # drop into a Pdb session if ``foo.bar()`` is called
        Q(module="foo", function="bar", kind="call", action=Debugger(klass=Pdb))
        |  # or
        Q(
            # show code that contains "mumbo.jumbo" on the current line
            lambda event: event.locals.get("mumbo") == "jumbo",
            # and it's not in Python's stdlib
            stdlib=False,
            # and it contains "mumbo" on the current line
            source__contains="mumbo"
        )
    )

    import foo
    foo.func()

With a ``foo.py`` like this:

.. sourcecode:: python

    def bar():
        execution_will_get_stopped  # cause we get a Pdb session here

    def func():
        mumbo = 1
        mumbo = "jumbo"
        print("not shown in trace")
        print(mumbo)
        mumbo = 2
        print(mumbo) # not shown in trace
        bar()


We get:

.. sourcecode:: pycon

    >>> foo.func()
    not shown in trace
        /home/ionel/osp/python-hunter/foo.py:8     line          print(mumbo)
    jumbo
        /home/ionel/osp/python-hunter/foo.py:9     line          mumbo = 2
    2
        /home/ionel/osp/python-hunter/foo.py:1     call      def bar():
    > /home/ionel/osp/python-hunter/foo.py(2)bar()
    -> execution_will_get_stopped  # cause we get a Pdb session here
    (Pdb)

In a terminal it would look like:

.. image:: https://raw.githubusercontent.com/ionelmc/python-hunter/master/docs/tree-trace.png

.. _tracing-processes:

Tracing processes
-----------------

In similar fashion to ``strace`` Hunter can trace other processes, eg::

    hunter-trace --gdb -p 123

If you wanna play it safe (no messy GDB) then add this in your code::

    from hunter import remote
    remote.install()

Then you can do::

    hunter-trace -p 123

See `docs on the remote feature <https://python-hunter.readthedocs.org/en/latest/remote.html>`_.

**Note:** Windows ain't supported.

.. _environment-variable-activation:

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>)

Note that Hunter is activated even if the env var is empty, eg: ``PYTHONHUNTER=""``.

Environment variable configuration
``````````````````````````````````

Sometimes you always use the same options (like ``stdlib=False`` or ``force_colors=True``). To save typing you can
set something like this in your environment::

    PYTHONHUNTERCONFIG="stdlib=False,force_colors=True"

This is the same as ``PYTHONHUNTER="stdlib=False,action=CallPrinter(force_colors=True)"``.

Notes:

* Setting ``PYTHONHUNTERCONFIG`` alone doesn't activate hunter.
* All the options for the builtin actions are supported.
* Although using predicates is supported it can be problematic. Example of setup that won't trace anything::

    PYTHONHUNTERCONFIG="Q(module_startswith='django')"
    PYTHONHUNTER="Q(module_startswith='celery')"

  which is the equivalent of::

    PYTHONHUNTER="Q(module_startswith='django'),Q(module_startswith='celery')"

  which is the equivalent of::

    PYTHONHUNTER="Q(module_startswith='django')&Q(module_startswith='celery')"



Filtering DSL
-------------

Hunter supports a flexible query DSL, see the `introduction
<https://python-hunter.readthedocs.org/en/latest/introduction.html>`_.

Development
===========

To run the all tests run::

    tox

Design notes
============

Hunter doesn't do everything. As a design goal of this library some things are made intentionally austere and verbose (to avoid complexity,
confusion and inconsistency). This has few consequences:

* There are `Operators <https://python-hunter.readthedocs.io/en/stable/introduction.html#operators>`_ but there's no negation operator.
  Instead you're expected to negate a Query object, eg: ``~Q(module='re')``.
* There are no specialized operators or filters - all filters behave exactly the same. For example:

  * No filter for packages. You're expected to filter by module with an operator.
  * No filter for arguments, return values or variables. You're expected to write your own filter function and deal with the problems
    of poking into objects.
* Layering is minimal. There's are some `helpers <https://python-hunter.readthedocs.io/en/stable/reference.html#helpers>`_ that do
  some argument processing and conversions to save you some typing but that's about it.
* The library doesn't try to hide the mechanics of tracing in Python - it's 1:1 regarding what Python sends to a trace function if you'd be
  using `sys.settrace <https://docs.python.org/3/library/sys.html#sys.settrace>`_.
* Doesn't have any storage. You are expected to redirect output to a file.

You should look at it like it's a tool to help you understand and debug big applications, or a framework ridding you of the boring parts of
settrace, not something that helps you learn Python.

FAQ
===

Why not Smiley?
---------------

There's some obvious overlap with `smiley <https://pypi.org/project/smiley/>`_ but there are few fundamental differences:

* Complexity. Smiley is simply over-engineered:

  * It uses IPC and a SQL database.
  * It has a webserver. Lots of dependencies.
  * It uses threads. Side-effects and subtle bugs are introduced in your code.
  * It records everything. Tries to dump any variable. Often fails and stops working.

  Why do you need all that just to debug some stuff in a terminal? Simply put, it's a nice idea but the design choices work
  against you when you're already neck-deep into debugging your own code. In my experience Smiley has been very buggy and
  unreliable. Your mileage may vary of course.

* Tracing long running code. This will make Smiley record lots of data, making it unusable.

  Now because Smiley records everything, you'd think it's better suited for short programs. But alas, if your program runs
  quickly then it's pointless to record the execution. You can just run it again.

  It seems there's only one situation where it's reasonable to use Smiley: tracing io-bound apps remotely. Those apps don't
  execute lots of code, they just wait on network so Smiley's storage won't blow out of proportion and tracing overhead might
  be acceptable.
* Use-cases. It seems to me Smiley's purpose is not really debugging code, but more of a "non interactive monitoring" tool.

In contrast, Hunter is very simple:

* Few dependencies.
* Low overhead (tracing/filtering code has an optional Cython extension).
* No storage. This simplifies lots of things.

  The only cost is that you might need to run the code multiple times to get the filtering/actions right. This means Hunter is
  not really suited for "post-mortem" debugging. If you can't reproduce the problem anymore then Hunter won't be of much help.

Why not pytrace?
----------------

`Pytrace <https://pypi.org/project/pytrace/>`_ is another tracer tool. It seems quite similar to Smiley - it uses a sqlite
database for the events, threads and IPC, thus it's reasonable to expect the same kind of problems.

Why not PySnooper or snoop?
---------------------------

`snoop <https://pypi.org/project/snoop/>`_ is a refined version of `PySnooper <https://pypi.org/project/PySnooper/>`_. Both are
more suited to tracing small programs or functions as the output is more verbose and less suited to the needs of tracing a big application
where Hunter provides more flexible setup, filtering capabilities, speed and brevity.

Why not coverage?
-----------------

For purposes of debugging `coverage <https://pypi.org/project/coverage/>`_ is a great tool but only as far as "debugging
by looking at what code is (not) run". Checking branch coverage is good but it will only get you as far.

From the other perspective, you'd be wondering if you could use Hunter to measure coverage-like things. You could do it but
for that purpose Hunter is very "rough": it has no builtin storage. You'd have to implement your own storage. You can do it
but it wouldn't give you any advantage over making your own tracer if you don't need to "pre-filter" whatever you're
recording.

In other words, filtering events is the main selling point of Hunter - it's fast (cython implementation) and the query API is
flexible enough.

Projects using Hunter
=====================

Noteworthy usages or Hunter (submit a PR with your project if you built a tool that relies on hunter):

* `Crunch-io/diagnose <https://github.com/Crunch-io/diagnose>`_ - a runtime instrumentation library.
* `talwrii/huntrace <https://github.com/talwrii/huntrace>`_ - an alternative cli (similar to ltrace).
* `anki-code/xunter <https://github.com/anki-code/xunter>`_ - a profiling tool made specifically for `the xonsh shell <https://xon.sh>`_.

More projects using it at https://github.com/ionelmc/python-hunter/network/dependents


Changelog
=========

3.7.0 (2024-05-02)
------------------

* Drop support for Python 3.7.
* Upgrade linters and refactor various string formatting and other cleanups.
* Upgrade Cython to latest release (3.0.10).
* Linux wheels should be back now.
* Switched docs theme to furo.

3.6.1 (2023-04-26)
------------------

* Added safe repr support for Decimal objects.

3.6.0 (2023-04-25)
------------------

* Added C extension support for Python 3.11. This may come with up to 10% performance hit (depending on use-case) for all Pythons.
  Unfortunately some `compat shims <https://raw.githubusercontent.com/python/pythoncapi_compat/master/pythoncapi_compat.h>`_ are
  now used for getting frame details. This is necessary to be able to work with Python 3.11 and be more future-proof.
* Added safe repr support for ZoneInfo objects.
* C extension files are now prebuilt with Cython 3.0b2.
* Replaced the flake8/isort pre-commit hooks with ruff.
* Disabled editable wheels (`PEP-0662 <https://peps.python.org/pep-0662/>`_) as they don't include the `hunter.pth` file.
  There may be a way to do it but I haven't figured out a way to customize the `editable_wheel` command without a
  very brittle solution glued to setuptools' internals.

3.5.1 (2022-09-27)
------------------

* Fixed breakage in ``hunter-trace`` when Ctrl-C.

3.5.0 (2022-09-11)
------------------

* Add support for generators and coroutines in the ``hunter.wrap`` decorator.
* Dropped support for Python 3.6.

3.4.3 (2021-12-15)
------------------

* Removed most of the Python 2 support code.
* Fix some refactoring regression in ``setup.py`` and make the 3.4.x series installable only on Python 3.6 and later.
* Yank 3.4.0, 3.4.1, 3.4.2 releases to avoid install problems on Python 2.7.

3.4.2 (2021-12-15)
------------------

* Fixed CI to properly make win32 wheels.

3.4.1 (2021-12-14)
------------------

* Add support for building a ``pp37.pp38`` tagged wheel
  (basically an universal wheel installable just for those two PyPy versions).

3.4.0 (2021-12-14)
------------------

* Switched CI to GitHub Actions, this has a couple consequences:

  * Support for Python 2.7 is dropped. You can still install it there but it's not tested anymore and
    Python 2 specific handling will be removed at some point.
  * Linux wheels are now provided in `musllinux` and `manylinux2014` variants.
* Extension building is now completely skipped on PyPy.
* A pure but tagged as platform specific wheel is now provided for PyPy (to have fast installs there as well).

3.3.8 (2021-06-23)
------------------

* Fixed CI problem that publishes same type of wheels two times.

3.3.7 (2021-06-23)
------------------

* Fixed a bug with how ``stdlib`` is detected on Windows (at least).

3.3.6 (2021-06-23)
------------------

* Fixed regression from *3.3.4*: ``stdlib`` filter was broken.
* Improved the pth file (``PYTHONHUNTER`` environment variable activation) to use a clean eval environment.
  No bogus variables like ``line`` (from the ``site.py`` machinery) will be available anymore.
* Fixed a bug in ``VarsSnooper`` that would make it fail in rare situation where a double `return` event is emitted.

3.3.5 (2021-06-11)
------------------

* Added support for Python 3.10.
* Added support for ``time`` objects and the ``fold`` option in ``safe_repr``.
* *3.3.4 was skipped cause I messed up the CI.*

3.3.3 (2021-05-04)
------------------

* Fixed tracer still being active for other threads after it was stopped.

  Python unfortunately only allows removing the trace function for the current thread -
  now ``hunter.tracer.Tracer`` will uninstall itself if it's marked as stopped.

  This fixes bogus errors that appear when using ``ipdb`` with
  the ``hunter.actions.Debugger`` action while thread support is enabled (the default).

3.3.2 (2021-03-25)
------------------

* Changed CI to build Python 3.9 wheels. Python 3.5 no longer tested and wheels no longer built to keep things simple.
* Documentation improvements.

3.3.1 (2020-10-24)
------------------

* Fixed CI/test issues that prevented all of 21 wheels being published.

3.3.0 (2020-10-23)
------------------

* Fixed handling so that ``hunter.event.Event.module`` is always the ``"?"`` string instead of ``None``.
  Previously it was ``None`` when tracing particularly broken code and broke various predicates.
* Similarly ``hunter.event.Event.filename`` is now ``"?"`` if there's no filename available.
* Building on the previous changes the actions have simpler code for displaying missing module/filenames.
* Changed ``hunter.actions.CallPrinter`` so that trace events for builtin functions are displayed differently.
  These events appear when using profile mode (eg: ``trace(profile=True)``).
* Fixed failure that could occur if ``hunter.event.Event.module`` is an unicode string. Now it's always a regular string.
  *Only applies to Python 2.*
* Fixed argument display when tracing functions with tuple arguments.
  Closes `#88 <https://github.com/ionelmc/python-hunter/issues/88>`_. *Only applies to Python 2.*
* Improved error reporting when internal failures occur. Now some details about the triggering event are logged.

3.2.2 (2020-09-04)
------------------

* Fixed oversight over what value is in ``hunter.event.Event.builtin``. Now it's always a boolean, and can be used consistently
  in filters (eg: ``builtin=True,function='getattr'``).

3.2.1 (2020-08-18)
------------------

* Added support for regex, date and datetime in ``safe_repr``.
* Fixed call argument display when positional and keyword arguments are used in ``hunter.actions.CallPrinter``.

3.2.0 (2020-08-16)
------------------

* Implemented the ``hunter.actions.StackPrinter`` action.
* Implemented the ``hunter.predicates.Backlog`` predicate.
  Contributed by Dan Ailenei in `#81 <https://github.com/ionelmc/python-hunter/pull/81>`_.
* Improved contributing section in docs a bit.
  Contributed by Tom Schraitle in `#85 <https://github.com/ionelmc/python-hunter/pull/85>`_.
* Improved filtering performance by avoiding a lot of unnecessary
  ``PyObject_GetAttr`` calls in the Cython implementation of ``hunter.predicates.Backlog``.
* Implemented the ``hunter.actions.ErrorSnooper`` action.
* Added support for profiling mode (eg: ``trace(profile=True)``).
  This mode will use ``setprofile`` instead of ``settrace``.
* Added ARM64 wheels and CI.
* Added ``hunter.event.Event.instruction`` and ``hunter.event.Event.builtin`` (usable in profile mode).
* Added more cookbook entries.

3.1.3 (2020-02-02)
------------------

* Improved again the stdlib check to handle certain paths better.

3.1.2 (2019-01-19)
------------------

* Really fixed the ``<frozen importlib.something`` stdlib check.

3.1.1 (2019-01-19)
------------------

* Marked all the ``<frozen importlib.something`` files as part of stdlib.

3.1.0 (2019-01-19)
------------------

* Added ``hunter.actions.ErrorSnooper`` - an action that detects silenced exceptions.
* Added ``hunter.load_config`` and fixed issues with configuration being loaded too late from the ``PYTHONHUNTERCONFIG`` environment
  variable.
* Changed ``hunter.From`` helper to automatically move ``depth`` and ``calls`` filters to the predicate (so they filter after
  ``hunter.predicates.From`` activates).
* Changed ``hunter.predicates.From`` to pass a copy of event to the predicate.
  The copy will have the ``depth`` and ``calls`` attributes adjusted to the point where ``hunter.predicates.From`` activated.
* Fixed a bunch of inconsistencies and bugs when using ``&`` and ``|`` operators with predicates.
* Fixed a bunch of broken fields on ``detached events <hunter.event.Event.detach>``
  (``hunter.event.Event.function_object`` and ``hunter.event.Event.arg``).
* Improved docstrings in various and added a configuration doc section.
* Improved testing (more coverage).

3.0.5 (2019-12-06)
------------------

* Really fixed ``safe_repr`` so it doesn't cause side-effects (now isinstance/issubclass are avoided - they
  can cause side-effects in code that abuses descriptors in special attributes/methods).

3.0.4 (2019-10-26)
------------------

* Really fixed ``stream`` setup in actions (using ``force_colors`` without any ``stream`` was broken).
  See: ``hunter.actions.ColorStreamAction``.
* Fixed ``__repr__`` for the ``hunter.predicates.From`` predicate to include ``watermark``.
* Added binary wheels for Python 3.8.

3.0.3 (2019-10-13)
------------------

* Fixed ``safe_repr`` on pypy so it's safer on method objects.
  See: ``hunter.actions.ColorStreamAction``.

3.0.2 (2019-10-10)
------------------

* Fixed setting ``stream`` from ``PYTHONHUNTERCONFIG`` environment variable.
  See: ``hunter.actions.ColorStreamAction``.
* Fixed a couple minor documentation issues.

3.0.1 (2019-06-17)
------------------

* Fixed issue with coloring missing source message (coloring leaked into next line).

3.0.0 (2019-06-17)
------------------

* The package now uses setuptools-scm for development builds (available at https://test.pypi.org/project/hunter/). As a
  consequence installing the sdist will download setuptools-scm.
* Recompiled cython modules with latest Cython. Hunter can be installed without any Cython, as before.
* Refactored some of the cython modules to have more typing information and not use deprecated property syntax.
* Replaced ``unsafe_repr`` option with ``repr_func``. Now you can use your custom repr function in the builtin actions.
  **BACKWARDS INCOMPATIBLE**
* Fixed buggy filename handling when using Hunter in ipython/jupyter. Source code should be properly displayed now.
* Removed ``globals`` option from ``VarsPrinter`` action. Globals are now always looked up. **BACKWARDS INCOMPATIBLE**
* Added support for locals in ``VarsPrinter`` action. Now you can do ``VarsPrinter('len(foobar)')``.
* Always pass module_globals dict to linecache methods. Source code from PEP-302 loaders is now printed properly.
  Contributed by Mikhail Borisov in `#65 <https://github.com/ionelmc/python-hunter/pull/65>`_.
* Various code cleanup, style and docstring fixing.
* Added ``hunter.From`` helper to allow passing in filters directly as keyword arguments.
* Added ``hunter.event.Event.detach`` for storing events without leaks or side-effects (due to prolonged references
  to Frame objects, local or global variables).
* Refactored the internals of actions for easier subclassing.

  Added the
  ``hunter.actions.ColorStreamAction.filename_prefix``,
  ``hunter.actions.ColorStreamAction.output``,
  ``hunter.actions.ColorStreamAction.pid_prefix``,
  ``hunter.actions.ColorStreamAction.thread_prefix``,
  ``hunter.actions.ColorStreamAction.try_repr`` and
  ``hunter.actions.ColorStreamAction.try_source`` methods
  to the ``hunter.actions.ColorStreamAction`` baseclass.
* Added ``hunter.actions.VarsSnooper`` - a PySnooper-inspired variant of ``hunter.actions.VarsPrinter``. It
  will record and show variable changes, with the risk of leaking or using too much memory of course :)
* Fixed tracers to log error and automatically stop if there's an internal failure. Previously error may have been
  silently dropped in some situations.

2.2.1 (2019-01-19)
------------------

* Fixed a link in changelog.
* Fixed some issues in the Travis configuration.

2.2.0 (2019-01-19)
------------------

* Added ``hunter.predicates.From`` predicate for tracing from a specific point. It stop after returning back to the
  same call depth with a configurable offset.
* Fixed ``PYTHONHUNTERCONFIG`` not working in some situations (config values were resolved at the wrong time).
* Made tests in CI test the wheel that will eventually be published to PyPI
  (`tox-wheel <https://pypi.org/project/tox-wheel/>`_).
* Made ``event.stdlib`` more reliable: ``pkg_resources`` is considered part of stdlib and few more paths will be
  considered as stdlib.
* Dumbed down the ``get_peercred`` check that is done when attaching with ``hunter-trace`` CLI (via
  ``hunter.remote.install()``). It will be slightly insecure but will work on OSX.
* Added OSX in the Travis test grid.

2.1.0 (2018-11-17)
------------------

* Made ``threading_support`` on by default but output automatic (also, now ``1`` or ``0`` allowed).
* Added ``pid_alignment`` and ``force_pid`` action options to show a pid prefix.
* Fixed some bugs around ``__eq__`` in various classes.
* Dropped Python 3.3 support.
* Dropped dependency on `fields <https://python-fields.readthedocs.io/en/stable/>`_.
* Actions now repr using a simplified implementation that tries to avoid calling ``__repr__`` on user classes in order
  to avoid creating side-effects while tracing.
* Added support for the ``PYTHONHUNTERCONFIG`` environment variable (stores defaults and doesn't activate hunter).

2.0.2 (2017-11-24)
------------------

* Fixed indentation in ``hunter.actions.CallPrinter`` action (shouldn't deindent on exception).
* Fixed option filtering in Cython Query implementation (filtering on ``tracer`` was allowed by mistake).
* Various fixes to docstrings and docs.

2.0.1 (2017-09-09)
------------------

* Now ``Py_AddPendingCall`` is used instead of acquiring the GIL (when using GDB).

2.0.0 (2017-09-02)
------------------

* Added the ``hunter.event.Event.count`` and ``hunter.event.Event.calls`` attributes.
* Added the ``lt``/``lte``/``gt``/``gte`` lookups.
* Added convenience aliases for ``startswith`` (``sw``), ``endswith`` (``ew``), ``contains`` (``has``)
  and ``regex`` (``rx``).
* Added a convenience ``hunter.wrap`` decorator to start tracing around a function.
* Added support for remote tracing (with two backends: `manhole <https://pypi.org/project/manhole/>`__ and GDB) via
  the ``hunter-trace`` bin. Note: **Windows is NOT SUPPORTED**.
* Changed the default action to ``hunter.actions.CallPrinter``.
  You'll need to use ``action=CodePrinter`` if you want the old output.

1.4.1 (2016-09-24)
------------------

* Fix support for getting sources for Cython module (it was broken on Windows and Python3.5+).

1.4.0 (2016-09-24)
------------------

* Added support for tracing Cython modules (`#30 <https://github.com/ionelmc/python-hunter/issues/30>`_). A
  `# cython: linetrace=True` stanza or equivalent is required in Cython modules for this to work.

1.3.0 (2016-04-14)
------------------

* Added ``hunter.event.Event.thread``.
* Added ``hunter.event.Event.threadid`` and ``hunter.event.Event.threadname``
  (available for filtering with ``hunter.Q``).
* Added ``hunter.event.Event.threading_support`` argument to ``hunter.trace``.
  It makes new threads be traced and changes action output to include thread name.
* Added support for using `pdb++ <https://pypi.org/project/pdbpp/>`_ in the ``hunter.actions.Debugger`` action.
* Added support for using `manhole <https://pypi.org/project/manhole/>`_ via a new ``hunter.actions.Manhole``
  action.
* Made the ``hunter.event.Event.handler`` a public but readonly property.


1.2.2 (2016-01-28)
------------------

* Fix broken import. Require ``fields>=4.0``.
* Simplify a string check in Cython code.

1.2.1 (2016-01-27)
------------------

* Fix "KeyError: 'normal'" bug in ``hunter.actions.CallPrinter``. Create the NO_COLORS dict from the COLOR dicts.
  Some keys were missing.

1.2.0 (2016-01-24)
------------------

* Fixed printouts of objects that return very large string in ``__repr__()``. Trimmed to 512. Configurable in actions
  with the ``repr_limit`` option.
* Improved validation of ``hunter.actions.VarsPrinter``'s initializer.
* Added a ``hunter.actions.CallPrinter`` action.

1.1.0 (2016-01-21)
------------------

* Implemented a destructor (``__dealloc__``) for the Cython tracer.
* Improved the restoring of the previous tracer in the Cython tracer (use ``PyEval_SetTrace``) directly.
* Removed ``tracer`` as an allowed filtering argument in ``hunter.Query``.
* Add basic validation (must be callable) for positional arguments and actions passed into ``hunter.Q``. Closes
  `#23 <https://github.com/ionelmc/python-hunter/issues/23>`_.
* Fixed ``stdlib`` checks (wasn't very reliable). Closes `#24 <https://github.com/ionelmc/python-hunter/issues/24>`_.

1.0.2 (2016-01-05)
------------------

* Fixed missing import in ``setup.py``.

1.0.1 (2015-12-24)
------------------

* Fix a compile issue with the MSVC compiler (seems it don't like the inline option on the ``fast_When_call``).

1.0.0 (2015-12-24)
------------------

* Implemented fast tracer and query objects in Cython. **MAY BE BACKWARDS INCOMPATIBLE**

  To force using the old pure-python implementation set the ``PUREPYTHONHUNTER`` environment variable to non-empty value.
* Added filtering operators: ``contains``, ``startswith``, ``endswith`` and ``in``. Examples:

  * ``Q(module_startswith='foo'`` will match events from ``foo``, ``foo.bar`` and ``foobar``.
  * ``Q(module_startswith=['foo', 'bar']`` will match events from ``foo``, ``foo.bar``, ``foobar``, ``bar``, ``bar.foo`` and ``baroo`` .
  * ``Q(module_endswith='bar'`` will match events from ``foo.bar`` and ``foobar``.
  * ``Q(module_contains='ip'`` will match events from ``lipsum``.
  * ``Q(module_in=['foo', 'bar']`` will match events from ``foo`` and ``bar``.
  * ``Q(module_regex=r"(re|sre.*)\b") will match events from ``re``, ``re.foobar``, ``srefoobar`` but not from ``repr``.

* Removed the ``merge`` option. Now when you call ``hunter.trace(...)`` multiple times only the last one is active.
  **BACKWARDS INCOMPATIBLE**
* Remove the ``previous_tracer`` handling. Now when you call ``hunter.trace(...)`` the previous tracer (whatever was in
  ``sys.gettrace()``) is disabled and restored when ``hunter.stop()`` is called. **BACKWARDS INCOMPATIBLE**
* Fixed ``CodePrinter`` to show module name if it fails to get any sources.

0.6.0 (2015-10-10)
------------------

* Added a ``clear_env_var`` option on the tracer (disables tracing in subprocess).
* Added ``force_colors`` option on ``hunter.actions.VarsPrinter`` and ``hunter.actions.CodePrinter``.
* Allowed setting the `stream` to a file name (option on ``hunter.actions.VarsPrinter`` and
  ``hunter.actions.CodePrinter``).
* Bumped up the filename alignment to 40 cols.
* If not merging then ``self`` is not kept as a previous tracer anymore.
  Closes `#16 <https://github.com/ionelmc/python-hunter/issues/16>`_.
* Fixed handling in VarsPrinter: properly print eval errors and don't try to show anything if there's an AttributeError.
  Closes `#18 <https://github.com/ionelmc/python-hunter/issues/18>`_.
* Added a ``stdlib`` boolean flag (for filtering purposes).
  Closes `#15 <https://github.com/ionelmc/python-hunter/issues/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 ``hunter.event.Event.globals`` to actually be the dict of global vars (it was just the locals).

0.5.0 (2015-04-06)
------------------

* Fixed ``hunter.And`` and ``hunter.Or`` "single argument unwrapping".
* Implemented predicate compression. Example: ``Or(Or(a, b), c)`` is converted to ``Or(a, b, c)``.
* Renamed ``hunter.event.Event.source`` to ``hunter.event.Event.fullsource``.
* Added ``hunter.event.Event.source`` that doesn't do any fancy sourcecode tokenization.
* Fixed ``hunter.event.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 ``hunter.event.Event``.

0.4.0 (2015-03-29)
------------------

* Disabled colors for Jython.
  Contributed by Claudiu Popa in `#12 <https://github.com/ionelmc/python-hunter/pull/12>`_.
* Test suite fixes for Windows.
  Contributed by Claudiu Popa in `#11 <https://github.com/ionelmc/python-hunter/pull/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 <https://github.com/ionelmc/python-hunter/issues/8>`_.

0.2.1 (2015-03-28)
------------------

* Added missing color entry for exception events.
* Added ``hunter.event.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 ``hunter.actions.VarsPrinter``.
* Breaking changes:

  * Renamed ``F`` to ``hunter.Q``. And ``hunter.Q`` is now just a convenience wrapper for
    ``hunter.predicates.Query``.
  * Renamed the ``PYTHON_HUNTER`` env variable to ``PYTHONHUNTER``.
  * Changed ``hunter.predicates.When`` to take positional arguments.
  * Changed output to show 2 path components (still not configurable).
  * Changed ``hunter.actions.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.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/ionelmc/python-hunter",
    "name": "hunter",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "trace, tracer, settrace, debugger, debugging, code, source",
    "author": "Ionel Cristian M\u0103rie\u0219",
    "author_email": "contact@ionelmc.ro",
    "download_url": "https://files.pythonhosted.org/packages/8f/92/f21e6066609232e5fa564b98ece34eaab70caee2e854ea1b45776a33edcb/hunter-3.7.0.tar.gz",
    "platform": null,
    "description": "========\nOverview\n========\n\n\n\nHunter is a flexible code tracing toolkit, not for measuring coverage, but for debugging, logging, inspection and other\nnefarious purposes. It has a `simple Python API <https://python-hunter.readthedocs.io/en/latest/introduction.html>`_,\na `convenient terminal API <environment-variable-activation_>`_ and\na `CLI tool to attach to processes <tracing-processes_>`_.\n\n* Free software: BSD 2-Clause License\n\nInstallation\n============\n\n::\n\n    pip install hunter\n\nDocumentation\n=============\n\n\nhttps://python-hunter.readthedocs.io/\n\nGetting started\n===============\n\nBasic use involves passing various filters to the ``trace`` option. An example:\n\n.. sourcecode:: python\n\n    import hunter\n    hunter.trace(module='posixpath', action=hunter.CallPrinter)\n\n    import os\n    os.path.join('a', 'b')\n\nThat would result in:\n\n.. sourcecode:: pycon\n\n    >>> os.path.join('a', 'b')\n             /usr/lib/python3.6/posixpath.py:75    call      => join(a='a')\n             /usr/lib/python3.6/posixpath.py:80    line         a = os.fspath(a)\n             /usr/lib/python3.6/posixpath.py:81    line         sep = _get_sep(a)\n             /usr/lib/python3.6/posixpath.py:41    call         => _get_sep(path='a')\n             /usr/lib/python3.6/posixpath.py:42    line            if isinstance(path, bytes):\n             /usr/lib/python3.6/posixpath.py:45    line            return '/'\n             /usr/lib/python3.6/posixpath.py:45    return       <= _get_sep: '/'\n             /usr/lib/python3.6/posixpath.py:82    line         path = a\n             /usr/lib/python3.6/posixpath.py:83    line         try:\n             /usr/lib/python3.6/posixpath.py:84    line         if not p:\n             /usr/lib/python3.6/posixpath.py:86    line         for b in map(os.fspath, p):\n             /usr/lib/python3.6/posixpath.py:87    line         if b.startswith(sep):\n             /usr/lib/python3.6/posixpath.py:89    line         elif not path or path.endswith(sep):\n             /usr/lib/python3.6/posixpath.py:92    line         path += sep + b\n             /usr/lib/python3.6/posixpath.py:86    line         for b in map(os.fspath, p):\n             /usr/lib/python3.6/posixpath.py:96    line         return path\n             /usr/lib/python3.6/posixpath.py:96    return    <= join: 'a/b'\n    'a/b'\n\nIn a terminal it would look like:\n\n.. image:: https://raw.githubusercontent.com/ionelmc/python-hunter/master/docs/code-trace.png\n\n\nAnother useful scenario is to ignore all standard modules and force colors to make them stay even if the output is\nredirected to a file.\n\n.. sourcecode:: python\n\n    import hunter\n    hunter.trace(stdlib=False, action=hunter.CallPrinter(force_colors=True))\n\nActions\n-------\n\nOutput format can be controlled with \"actions\". There's an alternative ``CodePrinter`` action that doesn't handle\nnesting (it was the default action until Hunter 2.0).\n\nIf filters match then action will be run. Example:\n\n.. sourcecode:: python\n\n    import hunter\n    hunter.trace(module='posixpath', action=hunter.CodePrinter)\n\n    import os\n    os.path.join('a', 'b')\n\nThat would result in:\n\n.. sourcecode:: pycon\n\n    >>> os.path.join('a', 'b')\n             /usr/lib/python3.6/posixpath.py:75    call      def join(a, *p):\n             /usr/lib/python3.6/posixpath.py:80    line          a = os.fspath(a)\n             /usr/lib/python3.6/posixpath.py:81    line          sep = _get_sep(a)\n             /usr/lib/python3.6/posixpath.py:41    call      def _get_sep(path):\n             /usr/lib/python3.6/posixpath.py:42    line          if isinstance(path, bytes):\n             /usr/lib/python3.6/posixpath.py:45    line              return '/'\n             /usr/lib/python3.6/posixpath.py:45    return            return '/'\n                                                   ...       return value: '/'\n             /usr/lib/python3.6/posixpath.py:82    line          path = a\n             /usr/lib/python3.6/posixpath.py:83    line          try:\n             /usr/lib/python3.6/posixpath.py:84    line              if not p:\n             /usr/lib/python3.6/posixpath.py:86    line              for b in map(os.fspath, p):\n             /usr/lib/python3.6/posixpath.py:87    line                  if b.startswith(sep):\n             /usr/lib/python3.6/posixpath.py:89    line                  elif not path or path.endswith(sep):\n             /usr/lib/python3.6/posixpath.py:92    line                      path += sep + b\n             /usr/lib/python3.6/posixpath.py:86    line              for b in map(os.fspath, p):\n             /usr/lib/python3.6/posixpath.py:96    line          return path\n             /usr/lib/python3.6/posixpath.py:96    return        return path\n                                                   ...       return value: 'a/b'\n    'a/b'\n\n- or in a terminal:\n\n.. image:: https://raw.githubusercontent.com/ionelmc/python-hunter/master/docs/simple-trace.png\n\n------\n\nAnother useful action is the ``VarsPrinter``:\n\n.. sourcecode:: python\n\n    import hunter\n    # note that this kind of invocation will also use the default `CallPrinter` action\n    hunter.trace(hunter.Q(module='posixpath', action=hunter.VarsPrinter('path')))\n\n    import os\n    os.path.join('a', 'b')\n\nThat would result in:\n\n.. sourcecode:: pycon\n\n    >>> os.path.join('a', 'b')\n         /usr/lib/python3.6/posixpath.py:75    call      => join(a='a')\n         /usr/lib/python3.6/posixpath.py:80    line         a = os.fspath(a)\n         /usr/lib/python3.6/posixpath.py:81    line         sep = _get_sep(a)\n         /usr/lib/python3.6/posixpath.py:41    call      [path => 'a']\n         /usr/lib/python3.6/posixpath.py:41    call         => _get_sep(path='a')\n         /usr/lib/python3.6/posixpath.py:42    line      [path => 'a']\n         /usr/lib/python3.6/posixpath.py:42    line            if isinstance(path, bytes):\n         /usr/lib/python3.6/posixpath.py:45    line      [path => 'a']\n         /usr/lib/python3.6/posixpath.py:45    line            return '/'\n         /usr/lib/python3.6/posixpath.py:45    return    [path => 'a']\n         /usr/lib/python3.6/posixpath.py:45    return       <= _get_sep: '/'\n         /usr/lib/python3.6/posixpath.py:82    line         path = a\n         /usr/lib/python3.6/posixpath.py:83    line      [path => 'a']\n         /usr/lib/python3.6/posixpath.py:83    line         try:\n         /usr/lib/python3.6/posixpath.py:84    line      [path => 'a']\n         /usr/lib/python3.6/posixpath.py:84    line         if not p:\n         /usr/lib/python3.6/posixpath.py:86    line      [path => 'a']\n         /usr/lib/python3.6/posixpath.py:86    line         for b in map(os.fspath, p):\n         /usr/lib/python3.6/posixpath.py:87    line      [path => 'a']\n         /usr/lib/python3.6/posixpath.py:87    line         if b.startswith(sep):\n         /usr/lib/python3.6/posixpath.py:89    line      [path => 'a']\n         /usr/lib/python3.6/posixpath.py:89    line         elif not path or path.endswith(sep):\n         /usr/lib/python3.6/posixpath.py:92    line      [path => 'a']\n         /usr/lib/python3.6/posixpath.py:92    line         path += sep + b\n         /usr/lib/python3.6/posixpath.py:86    line      [path => 'a/b']\n         /usr/lib/python3.6/posixpath.py:86    line         for b in map(os.fspath, p):\n         /usr/lib/python3.6/posixpath.py:96    line      [path => 'a/b']\n         /usr/lib/python3.6/posixpath.py:96    line         return path\n         /usr/lib/python3.6/posixpath.py:96    return    [path => 'a/b']\n         /usr/lib/python3.6/posixpath.py:96    return    <= join: 'a/b'\n    'a/b'\n\nIn a terminal it would look like:\n\n.. image:: https://raw.githubusercontent.com/ionelmc/python-hunter/master/docs/vars-trace.png\n\n-----\n\nYou can give it a tree-like configuration where you can optionally configure specific actions for parts of the\ntree (like dumping variables or a pdb set_trace):\n\n.. sourcecode:: python\n\n    from hunter import trace, Q, Debugger\n    from pdb import Pdb\n\n    trace(\n        # drop into a Pdb session if ``foo.bar()`` is called\n        Q(module=\"foo\", function=\"bar\", kind=\"call\", action=Debugger(klass=Pdb))\n        |  # or\n        Q(\n            # show code that contains \"mumbo.jumbo\" on the current line\n            lambda event: event.locals.get(\"mumbo\") == \"jumbo\",\n            # and it's not in Python's stdlib\n            stdlib=False,\n            # and it contains \"mumbo\" on the current line\n            source__contains=\"mumbo\"\n        )\n    )\n\n    import foo\n    foo.func()\n\nWith a ``foo.py`` like this:\n\n.. sourcecode:: python\n\n    def bar():\n        execution_will_get_stopped  # cause we get a Pdb session here\n\n    def func():\n        mumbo = 1\n        mumbo = \"jumbo\"\n        print(\"not shown in trace\")\n        print(mumbo)\n        mumbo = 2\n        print(mumbo) # not shown in trace\n        bar()\n\n\nWe get:\n\n.. sourcecode:: pycon\n\n    >>> foo.func()\n    not shown in trace\n        /home/ionel/osp/python-hunter/foo.py:8     line          print(mumbo)\n    jumbo\n        /home/ionel/osp/python-hunter/foo.py:9     line          mumbo = 2\n    2\n        /home/ionel/osp/python-hunter/foo.py:1     call      def bar():\n    > /home/ionel/osp/python-hunter/foo.py(2)bar()\n    -> execution_will_get_stopped  # cause we get a Pdb session here\n    (Pdb)\n\nIn a terminal it would look like:\n\n.. image:: https://raw.githubusercontent.com/ionelmc/python-hunter/master/docs/tree-trace.png\n\n.. _tracing-processes:\n\nTracing processes\n-----------------\n\nIn similar fashion to ``strace`` Hunter can trace other processes, eg::\n\n    hunter-trace --gdb -p 123\n\nIf you wanna play it safe (no messy GDB) then add this in your code::\n\n    from hunter import remote\n    remote.install()\n\nThen you can do::\n\n    hunter-trace -p 123\n\nSee `docs on the remote feature <https://python-hunter.readthedocs.org/en/latest/remote.html>`_.\n\n**Note:** Windows ain't supported.\n\n.. _environment-variable-activation:\n\nEnvironment variable activation\n-------------------------------\n\nFor your convenience environment variable activation is available. Just run your app like this::\n\n\n    PYTHONHUNTER=\"module='os.path'\" python yourapp.py\n\nOn Windows you'd do something like::\n\n    set PYTHONHUNTER=module='os.path'\n    python yourapp.py\n\nThe activation works with a clever ``.pth`` file that checks for that env var presence and before your app runs does something\nlike this::\n\n    from hunter import *\n    trace(<whatever-you-had-in-the-PYTHONHUNTER-env-var>)\n\nNote that Hunter is activated even if the env var is empty, eg: ``PYTHONHUNTER=\"\"``.\n\nEnvironment variable configuration\n``````````````````````````````````\n\nSometimes you always use the same options (like ``stdlib=False`` or ``force_colors=True``). To save typing you can\nset something like this in your environment::\n\n    PYTHONHUNTERCONFIG=\"stdlib=False,force_colors=True\"\n\nThis is the same as ``PYTHONHUNTER=\"stdlib=False,action=CallPrinter(force_colors=True)\"``.\n\nNotes:\n\n* Setting ``PYTHONHUNTERCONFIG`` alone doesn't activate hunter.\n* All the options for the builtin actions are supported.\n* Although using predicates is supported it can be problematic. Example of setup that won't trace anything::\n\n    PYTHONHUNTERCONFIG=\"Q(module_startswith='django')\"\n    PYTHONHUNTER=\"Q(module_startswith='celery')\"\n\n  which is the equivalent of::\n\n    PYTHONHUNTER=\"Q(module_startswith='django'),Q(module_startswith='celery')\"\n\n  which is the equivalent of::\n\n    PYTHONHUNTER=\"Q(module_startswith='django')&Q(module_startswith='celery')\"\n\n\n\nFiltering DSL\n-------------\n\nHunter supports a flexible query DSL, see the `introduction\n<https://python-hunter.readthedocs.org/en/latest/introduction.html>`_.\n\nDevelopment\n===========\n\nTo run the all tests run::\n\n    tox\n\nDesign notes\n============\n\nHunter doesn't do everything. As a design goal of this library some things are made intentionally austere and verbose (to avoid complexity,\nconfusion and inconsistency). This has few consequences:\n\n* There are `Operators <https://python-hunter.readthedocs.io/en/stable/introduction.html#operators>`_ but there's no negation operator.\n  Instead you're expected to negate a Query object, eg: ``~Q(module='re')``.\n* There are no specialized operators or filters - all filters behave exactly the same. For example:\n\n  * No filter for packages. You're expected to filter by module with an operator.\n  * No filter for arguments, return values or variables. You're expected to write your own filter function and deal with the problems\n    of poking into objects.\n* Layering is minimal. There's are some `helpers <https://python-hunter.readthedocs.io/en/stable/reference.html#helpers>`_ that do\n  some argument processing and conversions to save you some typing but that's about it.\n* The library doesn't try to hide the mechanics of tracing in Python - it's 1:1 regarding what Python sends to a trace function if you'd be\n  using `sys.settrace <https://docs.python.org/3/library/sys.html#sys.settrace>`_.\n* Doesn't have any storage. You are expected to redirect output to a file.\n\nYou should look at it like it's a tool to help you understand and debug big applications, or a framework ridding you of the boring parts of\nsettrace, not something that helps you learn Python.\n\nFAQ\n===\n\nWhy not Smiley?\n---------------\n\nThere's some obvious overlap with `smiley <https://pypi.org/project/smiley/>`_ but there are few fundamental differences:\n\n* Complexity. Smiley is simply over-engineered:\n\n  * It uses IPC and a SQL database.\n  * It has a webserver. Lots of dependencies.\n  * It uses threads. Side-effects and subtle bugs are introduced in your code.\n  * It records everything. Tries to dump any variable. Often fails and stops working.\n\n  Why do you need all that just to debug some stuff in a terminal? Simply put, it's a nice idea but the design choices work\n  against you when you're already neck-deep into debugging your own code. In my experience Smiley has been very buggy and\n  unreliable. Your mileage may vary of course.\n\n* Tracing long running code. This will make Smiley record lots of data, making it unusable.\n\n  Now because Smiley records everything, you'd think it's better suited for short programs. But alas, if your program runs\n  quickly then it's pointless to record the execution. You can just run it again.\n\n  It seems there's only one situation where it's reasonable to use Smiley: tracing io-bound apps remotely. Those apps don't\n  execute lots of code, they just wait on network so Smiley's storage won't blow out of proportion and tracing overhead might\n  be acceptable.\n* Use-cases. It seems to me Smiley's purpose is not really debugging code, but more of a \"non interactive monitoring\" tool.\n\nIn contrast, Hunter is very simple:\n\n* Few dependencies.\n* Low overhead (tracing/filtering code has an optional Cython extension).\n* No storage. This simplifies lots of things.\n\n  The only cost is that you might need to run the code multiple times to get the filtering/actions right. This means Hunter is\n  not really suited for \"post-mortem\" debugging. If you can't reproduce the problem anymore then Hunter won't be of much help.\n\nWhy not pytrace?\n----------------\n\n`Pytrace <https://pypi.org/project/pytrace/>`_ is another tracer tool. It seems quite similar to Smiley - it uses a sqlite\ndatabase for the events, threads and IPC, thus it's reasonable to expect the same kind of problems.\n\nWhy not PySnooper or snoop?\n---------------------------\n\n`snoop <https://pypi.org/project/snoop/>`_ is a refined version of `PySnooper <https://pypi.org/project/PySnooper/>`_. Both are\nmore suited to tracing small programs or functions as the output is more verbose and less suited to the needs of tracing a big application\nwhere Hunter provides more flexible setup, filtering capabilities, speed and brevity.\n\nWhy not coverage?\n-----------------\n\nFor purposes of debugging `coverage <https://pypi.org/project/coverage/>`_ is a great tool but only as far as \"debugging\nby looking at what code is (not) run\". Checking branch coverage is good but it will only get you as far.\n\nFrom the other perspective, you'd be wondering if you could use Hunter to measure coverage-like things. You could do it but\nfor that purpose Hunter is very \"rough\": it has no builtin storage. You'd have to implement your own storage. You can do it\nbut it wouldn't give you any advantage over making your own tracer if you don't need to \"pre-filter\" whatever you're\nrecording.\n\nIn other words, filtering events is the main selling point of Hunter - it's fast (cython implementation) and the query API is\nflexible enough.\n\nProjects using Hunter\n=====================\n\nNoteworthy usages or Hunter (submit a PR with your project if you built a tool that relies on hunter):\n\n* `Crunch-io/diagnose <https://github.com/Crunch-io/diagnose>`_ - a runtime instrumentation library.\n* `talwrii/huntrace <https://github.com/talwrii/huntrace>`_ - an alternative cli (similar to ltrace).\n* `anki-code/xunter <https://github.com/anki-code/xunter>`_ - a profiling tool made specifically for `the xonsh shell <https://xon.sh>`_.\n\nMore projects using it at https://github.com/ionelmc/python-hunter/network/dependents\n\n\nChangelog\n=========\n\n3.7.0 (2024-05-02)\n------------------\n\n* Drop support for Python 3.7.\n* Upgrade linters and refactor various string formatting and other cleanups.\n* Upgrade Cython to latest release (3.0.10).\n* Linux wheels should be back now.\n* Switched docs theme to furo.\n\n3.6.1 (2023-04-26)\n------------------\n\n* Added safe repr support for Decimal objects.\n\n3.6.0 (2023-04-25)\n------------------\n\n* Added C extension support for Python 3.11. This may come with up to 10% performance hit (depending on use-case) for all Pythons.\n  Unfortunately some `compat shims <https://raw.githubusercontent.com/python/pythoncapi_compat/master/pythoncapi_compat.h>`_ are\n  now used for getting frame details. This is necessary to be able to work with Python 3.11 and be more future-proof.\n* Added safe repr support for ZoneInfo objects.\n* C extension files are now prebuilt with Cython 3.0b2.\n* Replaced the flake8/isort pre-commit hooks with ruff.\n* Disabled editable wheels (`PEP-0662 <https://peps.python.org/pep-0662/>`_) as they don't include the `hunter.pth` file.\n  There may be a way to do it but I haven't figured out a way to customize the `editable_wheel` command without a\n  very brittle solution glued to setuptools' internals.\n\n3.5.1 (2022-09-27)\n------------------\n\n* Fixed breakage in ``hunter-trace`` when Ctrl-C.\n\n3.5.0 (2022-09-11)\n------------------\n\n* Add support for generators and coroutines in the ``hunter.wrap`` decorator.\n* Dropped support for Python 3.6.\n\n3.4.3 (2021-12-15)\n------------------\n\n* Removed most of the Python 2 support code.\n* Fix some refactoring regression in ``setup.py`` and make the 3.4.x series installable only on Python 3.6 and later.\n* Yank 3.4.0, 3.4.1, 3.4.2 releases to avoid install problems on Python 2.7.\n\n3.4.2 (2021-12-15)\n------------------\n\n* Fixed CI to properly make win32 wheels.\n\n3.4.1 (2021-12-14)\n------------------\n\n* Add support for building a ``pp37.pp38`` tagged wheel\n  (basically an universal wheel installable just for those two PyPy versions).\n\n3.4.0 (2021-12-14)\n------------------\n\n* Switched CI to GitHub Actions, this has a couple consequences:\n\n  * Support for Python 2.7 is dropped. You can still install it there but it's not tested anymore and\n    Python 2 specific handling will be removed at some point.\n  * Linux wheels are now provided in `musllinux` and `manylinux2014` variants.\n* Extension building is now completely skipped on PyPy.\n* A pure but tagged as platform specific wheel is now provided for PyPy (to have fast installs there as well).\n\n3.3.8 (2021-06-23)\n------------------\n\n* Fixed CI problem that publishes same type of wheels two times.\n\n3.3.7 (2021-06-23)\n------------------\n\n* Fixed a bug with how ``stdlib`` is detected on Windows (at least).\n\n3.3.6 (2021-06-23)\n------------------\n\n* Fixed regression from *3.3.4*: ``stdlib`` filter was broken.\n* Improved the pth file (``PYTHONHUNTER`` environment variable activation) to use a clean eval environment.\n  No bogus variables like ``line`` (from the ``site.py`` machinery) will be available anymore.\n* Fixed a bug in ``VarsSnooper`` that would make it fail in rare situation where a double `return` event is emitted.\n\n3.3.5 (2021-06-11)\n------------------\n\n* Added support for Python 3.10.\n* Added support for ``time`` objects and the ``fold`` option in ``safe_repr``.\n* *3.3.4 was skipped cause I messed up the CI.*\n\n3.3.3 (2021-05-04)\n------------------\n\n* Fixed tracer still being active for other threads after it was stopped.\n\n  Python unfortunately only allows removing the trace function for the current thread -\n  now ``hunter.tracer.Tracer`` will uninstall itself if it's marked as stopped.\n\n  This fixes bogus errors that appear when using ``ipdb`` with\n  the ``hunter.actions.Debugger`` action while thread support is enabled (the default).\n\n3.3.2 (2021-03-25)\n------------------\n\n* Changed CI to build Python 3.9 wheels. Python 3.5 no longer tested and wheels no longer built to keep things simple.\n* Documentation improvements.\n\n3.3.1 (2020-10-24)\n------------------\n\n* Fixed CI/test issues that prevented all of 21 wheels being published.\n\n3.3.0 (2020-10-23)\n------------------\n\n* Fixed handling so that ``hunter.event.Event.module`` is always the ``\"?\"`` string instead of ``None``.\n  Previously it was ``None`` when tracing particularly broken code and broke various predicates.\n* Similarly ``hunter.event.Event.filename`` is now ``\"?\"`` if there's no filename available.\n* Building on the previous changes the actions have simpler code for displaying missing module/filenames.\n* Changed ``hunter.actions.CallPrinter`` so that trace events for builtin functions are displayed differently.\n  These events appear when using profile mode (eg: ``trace(profile=True)``).\n* Fixed failure that could occur if ``hunter.event.Event.module`` is an unicode string. Now it's always a regular string.\n  *Only applies to Python 2.*\n* Fixed argument display when tracing functions with tuple arguments.\n  Closes `#88 <https://github.com/ionelmc/python-hunter/issues/88>`_. *Only applies to Python 2.*\n* Improved error reporting when internal failures occur. Now some details about the triggering event are logged.\n\n3.2.2 (2020-09-04)\n------------------\n\n* Fixed oversight over what value is in ``hunter.event.Event.builtin``. Now it's always a boolean, and can be used consistently\n  in filters (eg: ``builtin=True,function='getattr'``).\n\n3.2.1 (2020-08-18)\n------------------\n\n* Added support for regex, date and datetime in ``safe_repr``.\n* Fixed call argument display when positional and keyword arguments are used in ``hunter.actions.CallPrinter``.\n\n3.2.0 (2020-08-16)\n------------------\n\n* Implemented the ``hunter.actions.StackPrinter`` action.\n* Implemented the ``hunter.predicates.Backlog`` predicate.\n  Contributed by Dan Ailenei in `#81 <https://github.com/ionelmc/python-hunter/pull/81>`_.\n* Improved contributing section in docs a bit.\n  Contributed by Tom Schraitle in `#85 <https://github.com/ionelmc/python-hunter/pull/85>`_.\n* Improved filtering performance by avoiding a lot of unnecessary\n  ``PyObject_GetAttr`` calls in the Cython implementation of ``hunter.predicates.Backlog``.\n* Implemented the ``hunter.actions.ErrorSnooper`` action.\n* Added support for profiling mode (eg: ``trace(profile=True)``).\n  This mode will use ``setprofile`` instead of ``settrace``.\n* Added ARM64 wheels and CI.\n* Added ``hunter.event.Event.instruction`` and ``hunter.event.Event.builtin`` (usable in profile mode).\n* Added more cookbook entries.\n\n3.1.3 (2020-02-02)\n------------------\n\n* Improved again the stdlib check to handle certain paths better.\n\n3.1.2 (2019-01-19)\n------------------\n\n* Really fixed the ``<frozen importlib.something`` stdlib check.\n\n3.1.1 (2019-01-19)\n------------------\n\n* Marked all the ``<frozen importlib.something`` files as part of stdlib.\n\n3.1.0 (2019-01-19)\n------------------\n\n* Added ``hunter.actions.ErrorSnooper`` - an action that detects silenced exceptions.\n* Added ``hunter.load_config`` and fixed issues with configuration being loaded too late from the ``PYTHONHUNTERCONFIG`` environment\n  variable.\n* Changed ``hunter.From`` helper to automatically move ``depth`` and ``calls`` filters to the predicate (so they filter after\n  ``hunter.predicates.From`` activates).\n* Changed ``hunter.predicates.From`` to pass a copy of event to the predicate.\n  The copy will have the ``depth`` and ``calls`` attributes adjusted to the point where ``hunter.predicates.From`` activated.\n* Fixed a bunch of inconsistencies and bugs when using ``&`` and ``|`` operators with predicates.\n* Fixed a bunch of broken fields on ``detached events <hunter.event.Event.detach>``\n  (``hunter.event.Event.function_object`` and ``hunter.event.Event.arg``).\n* Improved docstrings in various and added a configuration doc section.\n* Improved testing (more coverage).\n\n3.0.5 (2019-12-06)\n------------------\n\n* Really fixed ``safe_repr`` so it doesn't cause side-effects (now isinstance/issubclass are avoided - they\n  can cause side-effects in code that abuses descriptors in special attributes/methods).\n\n3.0.4 (2019-10-26)\n------------------\n\n* Really fixed ``stream`` setup in actions (using ``force_colors`` without any ``stream`` was broken).\n  See: ``hunter.actions.ColorStreamAction``.\n* Fixed ``__repr__`` for the ``hunter.predicates.From`` predicate to include ``watermark``.\n* Added binary wheels for Python 3.8.\n\n3.0.3 (2019-10-13)\n------------------\n\n* Fixed ``safe_repr`` on pypy so it's safer on method objects.\n  See: ``hunter.actions.ColorStreamAction``.\n\n3.0.2 (2019-10-10)\n------------------\n\n* Fixed setting ``stream`` from ``PYTHONHUNTERCONFIG`` environment variable.\n  See: ``hunter.actions.ColorStreamAction``.\n* Fixed a couple minor documentation issues.\n\n3.0.1 (2019-06-17)\n------------------\n\n* Fixed issue with coloring missing source message (coloring leaked into next line).\n\n3.0.0 (2019-06-17)\n------------------\n\n* The package now uses setuptools-scm for development builds (available at https://test.pypi.org/project/hunter/). As a\n  consequence installing the sdist will download setuptools-scm.\n* Recompiled cython modules with latest Cython. Hunter can be installed without any Cython, as before.\n* Refactored some of the cython modules to have more typing information and not use deprecated property syntax.\n* Replaced ``unsafe_repr`` option with ``repr_func``. Now you can use your custom repr function in the builtin actions.\n  **BACKWARDS INCOMPATIBLE**\n* Fixed buggy filename handling when using Hunter in ipython/jupyter. Source code should be properly displayed now.\n* Removed ``globals`` option from ``VarsPrinter`` action. Globals are now always looked up. **BACKWARDS INCOMPATIBLE**\n* Added support for locals in ``VarsPrinter`` action. Now you can do ``VarsPrinter('len(foobar)')``.\n* Always pass module_globals dict to linecache methods. Source code from PEP-302 loaders is now printed properly.\n  Contributed by Mikhail Borisov in `#65 <https://github.com/ionelmc/python-hunter/pull/65>`_.\n* Various code cleanup, style and docstring fixing.\n* Added ``hunter.From`` helper to allow passing in filters directly as keyword arguments.\n* Added ``hunter.event.Event.detach`` for storing events without leaks or side-effects (due to prolonged references\n  to Frame objects, local or global variables).\n* Refactored the internals of actions for easier subclassing.\n\n  Added the\n  ``hunter.actions.ColorStreamAction.filename_prefix``,\n  ``hunter.actions.ColorStreamAction.output``,\n  ``hunter.actions.ColorStreamAction.pid_prefix``,\n  ``hunter.actions.ColorStreamAction.thread_prefix``,\n  ``hunter.actions.ColorStreamAction.try_repr`` and\n  ``hunter.actions.ColorStreamAction.try_source`` methods\n  to the ``hunter.actions.ColorStreamAction`` baseclass.\n* Added ``hunter.actions.VarsSnooper`` - a PySnooper-inspired variant of ``hunter.actions.VarsPrinter``. It\n  will record and show variable changes, with the risk of leaking or using too much memory of course :)\n* Fixed tracers to log error and automatically stop if there's an internal failure. Previously error may have been\n  silently dropped in some situations.\n\n2.2.1 (2019-01-19)\n------------------\n\n* Fixed a link in changelog.\n* Fixed some issues in the Travis configuration.\n\n2.2.0 (2019-01-19)\n------------------\n\n* Added ``hunter.predicates.From`` predicate for tracing from a specific point. It stop after returning back to the\n  same call depth with a configurable offset.\n* Fixed ``PYTHONHUNTERCONFIG`` not working in some situations (config values were resolved at the wrong time).\n* Made tests in CI test the wheel that will eventually be published to PyPI\n  (`tox-wheel <https://pypi.org/project/tox-wheel/>`_).\n* Made ``event.stdlib`` more reliable: ``pkg_resources`` is considered part of stdlib and few more paths will be\n  considered as stdlib.\n* Dumbed down the ``get_peercred`` check that is done when attaching with ``hunter-trace`` CLI (via\n  ``hunter.remote.install()``). It will be slightly insecure but will work on OSX.\n* Added OSX in the Travis test grid.\n\n2.1.0 (2018-11-17)\n------------------\n\n* Made ``threading_support`` on by default but output automatic (also, now ``1`` or ``0`` allowed).\n* Added ``pid_alignment`` and ``force_pid`` action options to show a pid prefix.\n* Fixed some bugs around ``__eq__`` in various classes.\n* Dropped Python 3.3 support.\n* Dropped dependency on `fields <https://python-fields.readthedocs.io/en/stable/>`_.\n* Actions now repr using a simplified implementation that tries to avoid calling ``__repr__`` on user classes in order\n  to avoid creating side-effects while tracing.\n* Added support for the ``PYTHONHUNTERCONFIG`` environment variable (stores defaults and doesn't activate hunter).\n\n2.0.2 (2017-11-24)\n------------------\n\n* Fixed indentation in ``hunter.actions.CallPrinter`` action (shouldn't deindent on exception).\n* Fixed option filtering in Cython Query implementation (filtering on ``tracer`` was allowed by mistake).\n* Various fixes to docstrings and docs.\n\n2.0.1 (2017-09-09)\n------------------\n\n* Now ``Py_AddPendingCall`` is used instead of acquiring the GIL (when using GDB).\n\n2.0.0 (2017-09-02)\n------------------\n\n* Added the ``hunter.event.Event.count`` and ``hunter.event.Event.calls`` attributes.\n* Added the ``lt``/``lte``/``gt``/``gte`` lookups.\n* Added convenience aliases for ``startswith`` (``sw``), ``endswith`` (``ew``), ``contains`` (``has``)\n  and ``regex`` (``rx``).\n* Added a convenience ``hunter.wrap`` decorator to start tracing around a function.\n* Added support for remote tracing (with two backends: `manhole <https://pypi.org/project/manhole/>`__ and GDB) via\n  the ``hunter-trace`` bin. Note: **Windows is NOT SUPPORTED**.\n* Changed the default action to ``hunter.actions.CallPrinter``.\n  You'll need to use ``action=CodePrinter`` if you want the old output.\n\n1.4.1 (2016-09-24)\n------------------\n\n* Fix support for getting sources for Cython module (it was broken on Windows and Python3.5+).\n\n1.4.0 (2016-09-24)\n------------------\n\n* Added support for tracing Cython modules (`#30 <https://github.com/ionelmc/python-hunter/issues/30>`_). A\n  `# cython: linetrace=True` stanza or equivalent is required in Cython modules for this to work.\n\n1.3.0 (2016-04-14)\n------------------\n\n* Added ``hunter.event.Event.thread``.\n* Added ``hunter.event.Event.threadid`` and ``hunter.event.Event.threadname``\n  (available for filtering with ``hunter.Q``).\n* Added ``hunter.event.Event.threading_support`` argument to ``hunter.trace``.\n  It makes new threads be traced and changes action output to include thread name.\n* Added support for using `pdb++ <https://pypi.org/project/pdbpp/>`_ in the ``hunter.actions.Debugger`` action.\n* Added support for using `manhole <https://pypi.org/project/manhole/>`_ via a new ``hunter.actions.Manhole``\n  action.\n* Made the ``hunter.event.Event.handler`` a public but readonly property.\n\n\n1.2.2 (2016-01-28)\n------------------\n\n* Fix broken import. Require ``fields>=4.0``.\n* Simplify a string check in Cython code.\n\n1.2.1 (2016-01-27)\n------------------\n\n* Fix \"KeyError: 'normal'\" bug in ``hunter.actions.CallPrinter``. Create the NO_COLORS dict from the COLOR dicts.\n  Some keys were missing.\n\n1.2.0 (2016-01-24)\n------------------\n\n* Fixed printouts of objects that return very large string in ``__repr__()``. Trimmed to 512. Configurable in actions\n  with the ``repr_limit`` option.\n* Improved validation of ``hunter.actions.VarsPrinter``'s initializer.\n* Added a ``hunter.actions.CallPrinter`` action.\n\n1.1.0 (2016-01-21)\n------------------\n\n* Implemented a destructor (``__dealloc__``) for the Cython tracer.\n* Improved the restoring of the previous tracer in the Cython tracer (use ``PyEval_SetTrace``) directly.\n* Removed ``tracer`` as an allowed filtering argument in ``hunter.Query``.\n* Add basic validation (must be callable) for positional arguments and actions passed into ``hunter.Q``. Closes\n  `#23 <https://github.com/ionelmc/python-hunter/issues/23>`_.\n* Fixed ``stdlib`` checks (wasn't very reliable). Closes `#24 <https://github.com/ionelmc/python-hunter/issues/24>`_.\n\n1.0.2 (2016-01-05)\n------------------\n\n* Fixed missing import in ``setup.py``.\n\n1.0.1 (2015-12-24)\n------------------\n\n* Fix a compile issue with the MSVC compiler (seems it don't like the inline option on the ``fast_When_call``).\n\n1.0.0 (2015-12-24)\n------------------\n\n* Implemented fast tracer and query objects in Cython. **MAY BE BACKWARDS INCOMPATIBLE**\n\n  To force using the old pure-python implementation set the ``PUREPYTHONHUNTER`` environment variable to non-empty value.\n* Added filtering operators: ``contains``, ``startswith``, ``endswith`` and ``in``. Examples:\n\n  * ``Q(module_startswith='foo'`` will match events from ``foo``, ``foo.bar`` and ``foobar``.\n  * ``Q(module_startswith=['foo', 'bar']`` will match events from ``foo``, ``foo.bar``, ``foobar``, ``bar``, ``bar.foo`` and ``baroo`` .\n  * ``Q(module_endswith='bar'`` will match events from ``foo.bar`` and ``foobar``.\n  * ``Q(module_contains='ip'`` will match events from ``lipsum``.\n  * ``Q(module_in=['foo', 'bar']`` will match events from ``foo`` and ``bar``.\n  * ``Q(module_regex=r\"(re|sre.*)\\b\") will match events from ``re``, ``re.foobar``, ``srefoobar`` but not from ``repr``.\n\n* Removed the ``merge`` option. Now when you call ``hunter.trace(...)`` multiple times only the last one is active.\n  **BACKWARDS INCOMPATIBLE**\n* Remove the ``previous_tracer`` handling. Now when you call ``hunter.trace(...)`` the previous tracer (whatever was in\n  ``sys.gettrace()``) is disabled and restored when ``hunter.stop()`` is called. **BACKWARDS INCOMPATIBLE**\n* Fixed ``CodePrinter`` to show module name if it fails to get any sources.\n\n0.6.0 (2015-10-10)\n------------------\n\n* Added a ``clear_env_var`` option on the tracer (disables tracing in subprocess).\n* Added ``force_colors`` option on ``hunter.actions.VarsPrinter`` and ``hunter.actions.CodePrinter``.\n* Allowed setting the `stream` to a file name (option on ``hunter.actions.VarsPrinter`` and\n  ``hunter.actions.CodePrinter``).\n* Bumped up the filename alignment to 40 cols.\n* If not merging then ``self`` is not kept as a previous tracer anymore.\n  Closes `#16 <https://github.com/ionelmc/python-hunter/issues/16>`_.\n* Fixed handling in VarsPrinter: properly print eval errors and don't try to show anything if there's an AttributeError.\n  Closes `#18 <https://github.com/ionelmc/python-hunter/issues/18>`_.\n* Added a ``stdlib`` boolean flag (for filtering purposes).\n  Closes `#15 <https://github.com/ionelmc/python-hunter/issues/15>`_.\n* Fixed broken frames that have \"None\" for filename or module (so they can still be treated as strings).\n* Corrected output files in the ``install_lib`` command so that pip can uninstall the pth file.\n  This only works when it's installed with pip (sadly, ``setup.py install/develop`` and ``pip install -e`` will still\n  leave pth garbage on ``pip uninstall hunter``).\n\n0.5.1 (2015-04-15)\n------------------\n\n* Fixed ``hunter.event.Event.globals`` to actually be the dict of global vars (it was just the locals).\n\n0.5.0 (2015-04-06)\n------------------\n\n* Fixed ``hunter.And`` and ``hunter.Or`` \"single argument unwrapping\".\n* Implemented predicate compression. Example: ``Or(Or(a, b), c)`` is converted to ``Or(a, b, c)``.\n* Renamed ``hunter.event.Event.source`` to ``hunter.event.Event.fullsource``.\n* Added ``hunter.event.Event.source`` that doesn't do any fancy sourcecode tokenization.\n* Fixed ``hunter.event.Event.fullsource`` return value for situations where the tokenizer would fail.\n* Made the print function available in the ``PYTHONHUNTER`` env var payload.\n* Added a __repr__ for ``hunter.event.Event``.\n\n0.4.0 (2015-03-29)\n------------------\n\n* Disabled colors for Jython.\n  Contributed by Claudiu Popa in `#12 <https://github.com/ionelmc/python-hunter/pull/12>`_.\n* Test suite fixes for Windows.\n  Contributed by Claudiu Popa in `#11 <https://github.com/ionelmc/python-hunter/pull/11>`_.\n* Added an introduction section in the docs.\n* Implemented a prettier fallback for when no sources are available for that frame.\n* Implemented fixups in cases where you use action classes as a predicates.\n\n0.3.1 (2015-03-29)\n------------------\n\n* Forgot to merge some commits ...\n\n0.3.0 (2015-03-29)\n------------------\n\n* Added handling for internal repr failures.\n* Fixed issues with displaying code that has non-ascii characters.\n* Implemented better display for ``call`` frames so that when a function has decorators the\n  function definition is shown (instead of just the first decorator).\n  See: `#8 <https://github.com/ionelmc/python-hunter/issues/8>`_.\n\n0.2.1 (2015-03-28)\n------------------\n\n* Added missing color entry for exception events.\n* Added ``hunter.event.Event.line`` property. It returns the source code for the line being run.\n\n0.2.0 (2015-03-27)\n------------------\n\n* Added color support (and ``colorama`` as dependency).\n* Added support for expressions in ``hunter.actions.VarsPrinter``.\n* Breaking changes:\n\n  * Renamed ``F`` to ``hunter.Q``. And ``hunter.Q`` is now just a convenience wrapper for\n    ``hunter.predicates.Query``.\n  * Renamed the ``PYTHON_HUNTER`` env variable to ``PYTHONHUNTER``.\n  * Changed ``hunter.predicates.When`` to take positional arguments.\n  * Changed output to show 2 path components (still not configurable).\n  * Changed ``hunter.actions.VarsPrinter`` to take positional arguments for the names.\n* Improved error reporting for env variable activation (``PYTHONHUNTER``).\n* Fixed env var activator (the ``.pth`` file) installation with ``setup.py install`` (the \"egg installs\") and\n  ``setup.py develop``/``pip install -e`` (the \"egg links\").\n\n0.1.0 (2015-03-22)\n------------------\n\n* First release on PyPI.\n",
    "bugtrack_url": null,
    "license": "BSD-2-Clause",
    "summary": "Hunter is a flexible code tracing toolkit.",
    "version": "3.7.0",
    "project_urls": {
        "Changelog": "https://python-hunter.readthedocs.io/en/latest/changelog.html",
        "Documentation": "https://python-hunter.readthedocs.io/",
        "Homepage": "https://github.com/ionelmc/python-hunter",
        "Issue Tracker": "https://github.com/ionelmc/python-hunter/issues"
    },
    "split_keywords": [
        "trace",
        " tracer",
        " settrace",
        " debugger",
        " debugging",
        " code",
        " source"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a1a1232a39e9ca8afd597509046f27f6460f17078bf54aea86197ff2d7631e73",
                "md5": "a1472faf999fdb9876d5fd3568c192fa",
                "sha256": "0107fef8c95aa9574f42f2c6e4de97dc205f18dbc81c1789f1eedf2cb28da21a"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp310-cp310-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "a1472faf999fdb9876d5fd3568c192fa",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 295268,
            "upload_time": "2024-05-02T14:03:50",
            "upload_time_iso_8601": "2024-05-02T14:03:50.587410Z",
            "url": "https://files.pythonhosted.org/packages/a1/a1/232a39e9ca8afd597509046f27f6460f17078bf54aea86197ff2d7631e73/hunter-3.7.0-cp310-cp310-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "84ec3bc2477a7d8b22bf40b99bea303cf5b585f9da21ab43bc34b1f2b4d5b969",
                "md5": "6b74ed8c427d724342df9cb5832adbba",
                "sha256": "7bf1a6eeed9c3d1d87cc436d8be099626416e2ca0ca487100b7ada5e48e7ae72"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "has_sig": false,
            "md5_digest": "6b74ed8c427d724342df9cb5832adbba",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 1610909,
            "upload_time": "2024-05-02T14:04:03",
            "upload_time_iso_8601": "2024-05-02T14:04:03.120205Z",
            "url": "https://files.pythonhosted.org/packages/84/ec/3bc2477a7d8b22bf40b99bea303cf5b585f9da21ab43bc34b1f2b4d5b969/hunter-3.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "366c9d9a742bb4d78a74e9e2f1c7c579e839a5d71e9369c7fe342055fcade455",
                "md5": "4cf70904ba4a98d3604241b0f51b1a98",
                "sha256": "76bb34aafc609e711d436f70bc29a7ebcd9179c0ef827c7ba96da2d4c91bc4a3"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "4cf70904ba4a98d3604241b0f51b1a98",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 1627342,
            "upload_time": "2024-05-02T14:04:09",
            "upload_time_iso_8601": "2024-05-02T14:04:09.239458Z",
            "url": "https://files.pythonhosted.org/packages/36/6c/9d9a742bb4d78a74e9e2f1c7c579e839a5d71e9369c7fe342055fcade455/hunter-3.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3706b68e84e4f6fa05947f7a93facf5c68955cb6fa4dafa35a25ad02a3af5899",
                "md5": "ecd15be6cc07392888bf45b5aa708726",
                "sha256": "2d44d0d47e7c37b0e9790fa8141f3cbdc4208a5562b26ba05efb956f2681e0ed"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp310-cp310-musllinux_1_1_aarch64.whl",
            "has_sig": false,
            "md5_digest": "ecd15be6cc07392888bf45b5aa708726",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 1624940,
            "upload_time": "2024-05-02T14:04:06",
            "upload_time_iso_8601": "2024-05-02T14:04:06.204704Z",
            "url": "https://files.pythonhosted.org/packages/37/06/b68e84e4f6fa05947f7a93facf5c68955cb6fa4dafa35a25ad02a3af5899/hunter-3.7.0-cp310-cp310-musllinux_1_1_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "38f9b31b69d369cbc678124a939f59842a3ad1ec097dcdbd6070fa067de16fa7",
                "md5": "78a8b439c591b0bd745b060fa2bb8a88",
                "sha256": "5c1c28456cf4f04a3aeee0a7b5807d1aec0ac25806252421948b5db6daffd522"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp310-cp310-musllinux_1_1_x86_64.whl",
            "has_sig": false,
            "md5_digest": "78a8b439c591b0bd745b060fa2bb8a88",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 1633241,
            "upload_time": "2024-05-02T14:04:12",
            "upload_time_iso_8601": "2024-05-02T14:04:12.116348Z",
            "url": "https://files.pythonhosted.org/packages/38/f9/b31b69d369cbc678124a939f59842a3ad1ec097dcdbd6070fa067de16fa7/hunter-3.7.0-cp310-cp310-musllinux_1_1_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3ef14e149c4d3424032394b96bd02c799987f32985d2ff516a20bc6c4b79bed4",
                "md5": "5ff0a643e40da8e4616dfa6e072af95c",
                "sha256": "f6c2ef27930b1973e5bda559d7ae449336328576362a15992504aa97ccb06ecb"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp310-cp310-win32.whl",
            "has_sig": false,
            "md5_digest": "5ff0a643e40da8e4616dfa6e072af95c",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 242657,
            "upload_time": "2024-05-02T14:05:08",
            "upload_time_iso_8601": "2024-05-02T14:05:08.498217Z",
            "url": "https://files.pythonhosted.org/packages/3e/f1/4e149c4d3424032394b96bd02c799987f32985d2ff516a20bc6c4b79bed4/hunter-3.7.0-cp310-cp310-win32.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "fe7c1c3e25ef449647d626725fd2d90f7cd8a80a12228d3c36b6b0c12748d3d6",
                "md5": "4e8d1953a20226443d2d7f1c85a93cfe",
                "sha256": "f13c81c03cf1a7bab4d63ffd952b6f6f0c4df9ea90461c1e0174ce68cf175a08"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp310-cp310-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "4e8d1953a20226443d2d7f1c85a93cfe",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 282123,
            "upload_time": "2024-05-02T14:05:03",
            "upload_time_iso_8601": "2024-05-02T14:05:03.805706Z",
            "url": "https://files.pythonhosted.org/packages/fe/7c/1c3e25ef449647d626725fd2d90f7cd8a80a12228d3c36b6b0c12748d3d6/hunter-3.7.0-cp310-cp310-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ba1cee08e1a9fe5715e435c468a841f632a63b67553bc7a4c51c25fc100d186c",
                "md5": "60aa488c7d47cf6e3a48ec2b168c6850",
                "sha256": "94efe2df941a6186e0aa7fc0b87df6686d9455ac77fc48f67a0d10237397591f"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp311-cp311-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "60aa488c7d47cf6e3a48ec2b168c6850",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 295348,
            "upload_time": "2024-05-02T14:03:53",
            "upload_time_iso_8601": "2024-05-02T14:03:53.203294Z",
            "url": "https://files.pythonhosted.org/packages/ba/1c/ee08e1a9fe5715e435c468a841f632a63b67553bc7a4c51c25fc100d186c/hunter-3.7.0-cp311-cp311-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "747f9499b1e3b533f323ec61b4af566efe8ea27d665b6dfabb24e7c7ef346615",
                "md5": "3891ad1123de5ebf4cf3806c1ad3c3ea",
                "sha256": "1c3516266c3dceae92636c7f21808f0ca2cd571d368bff79da57c7fb505398f5"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "has_sig": false,
            "md5_digest": "3891ad1123de5ebf4cf3806c1ad3c3ea",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 1739579,
            "upload_time": "2024-05-02T14:04:14",
            "upload_time_iso_8601": "2024-05-02T14:04:14.680307Z",
            "url": "https://files.pythonhosted.org/packages/74/7f/9499b1e3b533f323ec61b4af566efe8ea27d665b6dfabb24e7c7ef346615/hunter-3.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9b142644682b1584559b51615d539763c565c029fd46cdcceb93ced14e07775a",
                "md5": "e63b8bb9f4316ea25a858be2969e6047",
                "sha256": "e3b94da39b1881492cfda6f4630c27815c17823078ddefb9dfaf3d79d0c7ee89"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "e63b8bb9f4316ea25a858be2969e6047",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 1747282,
            "upload_time": "2024-05-02T14:04:20",
            "upload_time_iso_8601": "2024-05-02T14:04:20.199126Z",
            "url": "https://files.pythonhosted.org/packages/9b/14/2644682b1584559b51615d539763c565c029fd46cdcceb93ced14e07775a/hunter-3.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3ec46378617564856d629a88bd5a9c984497f53fe01af0e9ff38bd838fd1aa3d",
                "md5": "b12c967eabbdc1c44e246c688283b16b",
                "sha256": "403a54ba425dda94cb00ebc47c71a2e7c8e8c2e4188dc9b1b6848ce2e13059d0"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp311-cp311-musllinux_1_1_aarch64.whl",
            "has_sig": false,
            "md5_digest": "b12c967eabbdc1c44e246c688283b16b",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 1745075,
            "upload_time": "2024-05-02T14:04:17",
            "upload_time_iso_8601": "2024-05-02T14:04:17.237500Z",
            "url": "https://files.pythonhosted.org/packages/3e/c4/6378617564856d629a88bd5a9c984497f53fe01af0e9ff38bd838fd1aa3d/hunter-3.7.0-cp311-cp311-musllinux_1_1_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "73d56015311386c1776d0866abd206e2d718b1718009c0fd90b8f848e00815f3",
                "md5": "71fe753358604d26839b21a70141bed8",
                "sha256": "1fe660d266547f85644bce8230e714e6984bfaa94e8513d77c8c8b5562aeefae"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp311-cp311-musllinux_1_1_x86_64.whl",
            "has_sig": false,
            "md5_digest": "71fe753358604d26839b21a70141bed8",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 1750568,
            "upload_time": "2024-05-02T14:04:23",
            "upload_time_iso_8601": "2024-05-02T14:04:23.556551Z",
            "url": "https://files.pythonhosted.org/packages/73/d5/6015311386c1776d0866abd206e2d718b1718009c0fd90b8f848e00815f3/hunter-3.7.0-cp311-cp311-musllinux_1_1_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9222c6be40b4f72d56aa2a2340b44cf3a3e69dd16f182704bffdb4c181006f45",
                "md5": "6f1d501b134699c97de386d0bbc1d8d3",
                "sha256": "c810b56999576a8313793d4da64b857165e97a8f6f6a3b161f13c8b285324dd4"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp311-cp311-win32.whl",
            "has_sig": false,
            "md5_digest": "6f1d501b134699c97de386d0bbc1d8d3",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 242283,
            "upload_time": "2024-05-02T14:05:14",
            "upload_time_iso_8601": "2024-05-02T14:05:14.160766Z",
            "url": "https://files.pythonhosted.org/packages/92/22/c6be40b4f72d56aa2a2340b44cf3a3e69dd16f182704bffdb4c181006f45/hunter-3.7.0-cp311-cp311-win32.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6861c2fb157aae9453580b6bec835a482f4bd54585f4dc8cb891f73ccaf55a60",
                "md5": "f407da34ef69cf86b3f43308b87ffdcf",
                "sha256": "6d32a1c3ead5347325f92b90299f4e049b8cdea45a7682312187a34f0ecdbf43"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp311-cp311-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "f407da34ef69cf86b3f43308b87ffdcf",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 282415,
            "upload_time": "2024-05-02T14:05:11",
            "upload_time_iso_8601": "2024-05-02T14:05:11.609822Z",
            "url": "https://files.pythonhosted.org/packages/68/61/c2fb157aae9453580b6bec835a482f4bd54585f4dc8cb891f73ccaf55a60/hunter-3.7.0-cp311-cp311-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "43d720c368f004ae0701fe713c5560a6e917357f1fa4f54aa15dee858e273101",
                "md5": "3888858d6229337eb921cee391cfb267",
                "sha256": "c5aebe6d72749d509a2001ebf4bc6cba2a6bafb7dd645b8a6a027dcc2c966465"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp312-cp312-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "3888858d6229337eb921cee391cfb267",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 296673,
            "upload_time": "2024-05-02T14:03:55",
            "upload_time_iso_8601": "2024-05-02T14:03:55.376676Z",
            "url": "https://files.pythonhosted.org/packages/43/d7/20c368f004ae0701fe713c5560a6e917357f1fa4f54aa15dee858e273101/hunter-3.7.0-cp312-cp312-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6a7e2b45e6cfbd198a568342695515b9c31e780e27d3c73691e73ad5f87b709a",
                "md5": "8a861d616091075f76486d97a3ffe33a",
                "sha256": "e8948f11a0dfb3d19112cf67539d9ade7517ea22c5b3471b0566c7d62075f090"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "has_sig": false,
            "md5_digest": "8a861d616091075f76486d97a3ffe33a",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 1764609,
            "upload_time": "2024-05-02T14:04:26",
            "upload_time_iso_8601": "2024-05-02T14:04:26.601061Z",
            "url": "https://files.pythonhosted.org/packages/6a/7e/2b45e6cfbd198a568342695515b9c31e780e27d3c73691e73ad5f87b709a/hunter-3.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ccc544aac92dbc04c6cad054558676c87745a4117a71d510863943c82def6885",
                "md5": "fc449795cfcd9c4f4e1752d352a2a0a9",
                "sha256": "ab774df3dcf99c1b933afe91c1d5ed7e9bfa53826bd88c7b37cd0650db9e4ce9"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "fc449795cfcd9c4f4e1752d352a2a0a9",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 1795531,
            "upload_time": "2024-05-02T14:04:32",
            "upload_time_iso_8601": "2024-05-02T14:04:32.862864Z",
            "url": "https://files.pythonhosted.org/packages/cc/c5/44aac92dbc04c6cad054558676c87745a4117a71d510863943c82def6885/hunter-3.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9050674d31e1ee47eec0ef78ccb57b8327b12cc23dcb158e080c1f5490c0372c",
                "md5": "1402907971219e37b7154773f5672b7e",
                "sha256": "161bc2b522449696d06943c0fdc61219c15f8a407d4f06ddfc71689e43a77d80"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp312-cp312-musllinux_1_1_aarch64.whl",
            "has_sig": false,
            "md5_digest": "1402907971219e37b7154773f5672b7e",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 1766200,
            "upload_time": "2024-05-02T14:04:29",
            "upload_time_iso_8601": "2024-05-02T14:04:29.534703Z",
            "url": "https://files.pythonhosted.org/packages/90/50/674d31e1ee47eec0ef78ccb57b8327b12cc23dcb158e080c1f5490c0372c/hunter-3.7.0-cp312-cp312-musllinux_1_1_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "e2f6b2a0b707ce4c03a16ead5d200bf430d50e0eb4120622b2bda02f6e963e12",
                "md5": "c0a1cffbce7204c26c2eaf6b19d74d34",
                "sha256": "fa34b19b00be465054f1001158e6ac89511ce7b3bb40f8e7bc56a9ae18184c5b"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp312-cp312-musllinux_1_1_x86_64.whl",
            "has_sig": false,
            "md5_digest": "c0a1cffbce7204c26c2eaf6b19d74d34",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 1797151,
            "upload_time": "2024-05-02T14:04:35",
            "upload_time_iso_8601": "2024-05-02T14:04:35.014068Z",
            "url": "https://files.pythonhosted.org/packages/e2/f6/b2a0b707ce4c03a16ead5d200bf430d50e0eb4120622b2bda02f6e963e12/hunter-3.7.0-cp312-cp312-musllinux_1_1_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "b974576bb43d99abeec2a189424c5a88fc0a65fd50422608672ec64631ecb40a",
                "md5": "0998796e37c367e3f1d09c5d578d9e84",
                "sha256": "b73aa7eeb5990807698692367f874fb746d9ffb45430fb07ef497ac9fb7ad63c"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp312-cp312-win32.whl",
            "has_sig": false,
            "md5_digest": "0998796e37c367e3f1d09c5d578d9e84",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 241124,
            "upload_time": "2024-05-02T14:05:21",
            "upload_time_iso_8601": "2024-05-02T14:05:21.868942Z",
            "url": "https://files.pythonhosted.org/packages/b9/74/576bb43d99abeec2a189424c5a88fc0a65fd50422608672ec64631ecb40a/hunter-3.7.0-cp312-cp312-win32.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5ce35c46f1deded043c4390cb268595dd6e19f97517c16b7252d542073b66c7c",
                "md5": "134a7601af4a5b6ef6f0bb6f25347ca5",
                "sha256": "d1e72f28d81c67921548f504afc254f5bd96128ef92ecbf17946a145752a6602"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp312-cp312-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "134a7601af4a5b6ef6f0bb6f25347ca5",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 279359,
            "upload_time": "2024-05-02T14:05:18",
            "upload_time_iso_8601": "2024-05-02T14:05:18.521612Z",
            "url": "https://files.pythonhosted.org/packages/5c/e3/5c46f1deded043c4390cb268595dd6e19f97517c16b7252d542073b66c7c/hunter-3.7.0-cp312-cp312-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "f7197bf2ee32e2b85f9a1de607cd47cbff72542fdfae8f5f3b0821d676c9ef11",
                "md5": "00f271108f97afb498ace910f17f1cd5",
                "sha256": "4eda5403e1a142daea018736a31818d41f7ecbf39808d0a13a67d6d2641a317b"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp38-cp38-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "00f271108f97afb498ace910f17f1cd5",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 296688,
            "upload_time": "2024-05-02T14:03:57",
            "upload_time_iso_8601": "2024-05-02T14:03:57.952338Z",
            "url": "https://files.pythonhosted.org/packages/f7/19/7bf2ee32e2b85f9a1de607cd47cbff72542fdfae8f5f3b0821d676c9ef11/hunter-3.7.0-cp38-cp38-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a525874f01ac6f5333c12c96e03ba2c97e7626f39facbd0bb6cd237095af04ca",
                "md5": "c26ca188d95212b5c8ac7ebd6023fa73",
                "sha256": "5473821bd45973443ca6053b9fd64bf62bff78914b25f8c6d9460c0ffdbdc956"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "has_sig": false,
            "md5_digest": "c26ca188d95212b5c8ac7ebd6023fa73",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 1657042,
            "upload_time": "2024-05-02T14:04:37",
            "upload_time_iso_8601": "2024-05-02T14:04:37.479007Z",
            "url": "https://files.pythonhosted.org/packages/a5/25/874f01ac6f5333c12c96e03ba2c97e7626f39facbd0bb6cd237095af04ca/hunter-3.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "e4cb02f74d95f64be2c4e584f38e8b5fb8aeb915af1b98e439b19f95b4e06857",
                "md5": "9061cae47856ac090ed05001ec05b9e0",
                "sha256": "e795e8de62f3132d52b655033e444a17e4179d902f91fd4d5ba8936452b23a57"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "9061cae47856ac090ed05001ec05b9e0",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 1677728,
            "upload_time": "2024-05-02T14:04:46",
            "upload_time_iso_8601": "2024-05-02T14:04:46.639537Z",
            "url": "https://files.pythonhosted.org/packages/e4/cb/02f74d95f64be2c4e584f38e8b5fb8aeb915af1b98e439b19f95b4e06857/hunter-3.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9524340afcf8cbeb6325469394521761e7b4f6359be8cc44b937a81ff96a5831",
                "md5": "4b0f67bbcc2488eec9295ba3c2b34d01",
                "sha256": "7f3a5d81d665bf986ca557817006cbe85b5a89d08ec7fe795c1c1ad0d254868b"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp38-cp38-musllinux_1_1_aarch64.whl",
            "has_sig": false,
            "md5_digest": "4b0f67bbcc2488eec9295ba3c2b34d01",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 1742954,
            "upload_time": "2024-05-02T14:04:41",
            "upload_time_iso_8601": "2024-05-02T14:04:41.720097Z",
            "url": "https://files.pythonhosted.org/packages/95/24/340afcf8cbeb6325469394521761e7b4f6359be8cc44b937a81ff96a5831/hunter-3.7.0-cp38-cp38-musllinux_1_1_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "867772a79a98c5d9e7fad27787fc69cc18ca42d0aa2d0e251006173ba97402af",
                "md5": "4f026c7a48bbe90c27564503372863ca",
                "sha256": "6602b5d90a0cd2ff8feac470a78c412bed79226608afb0ff6d416b7008a47ae0"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp38-cp38-musllinux_1_1_x86_64.whl",
            "has_sig": false,
            "md5_digest": "4f026c7a48bbe90c27564503372863ca",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 1750713,
            "upload_time": "2024-05-02T14:04:49",
            "upload_time_iso_8601": "2024-05-02T14:04:49.531794Z",
            "url": "https://files.pythonhosted.org/packages/86/77/72a79a98c5d9e7fad27787fc69cc18ca42d0aa2d0e251006173ba97402af/hunter-3.7.0-cp38-cp38-musllinux_1_1_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "031975c397617825b485ed2d7f142829eef04bff12b7de29a3d004d5ea9a9ec2",
                "md5": "28ca2702affb0259b1191fbd51b6f006",
                "sha256": "5c997df47fcb03445a08007e15b9d78b35d92aa21334b2889527393a8ae716e2"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp38-cp38-win32.whl",
            "has_sig": false,
            "md5_digest": "28ca2702affb0259b1191fbd51b6f006",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 243645,
            "upload_time": "2024-05-02T14:05:30",
            "upload_time_iso_8601": "2024-05-02T14:05:30.457111Z",
            "url": "https://files.pythonhosted.org/packages/03/19/75c397617825b485ed2d7f142829eef04bff12b7de29a3d004d5ea9a9ec2/hunter-3.7.0-cp38-cp38-win32.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "fe0f688db51fa762b92bc6a6f3e0dbc3b15e2dabe99573a6a07acf305430da12",
                "md5": "0dc316f5558b1c06ad5fb1d1c124d01a",
                "sha256": "e5b97f8dbd1e909f8366ed7a23deacc5c488b3a0ddf00f1ca958753fa46181fe"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp38-cp38-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "0dc316f5558b1c06ad5fb1d1c124d01a",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 283632,
            "upload_time": "2024-05-02T14:05:24",
            "upload_time_iso_8601": "2024-05-02T14:05:24.902552Z",
            "url": "https://files.pythonhosted.org/packages/fe/0f/688db51fa762b92bc6a6f3e0dbc3b15e2dabe99573a6a07acf305430da12/hunter-3.7.0-cp38-cp38-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5a4c8e81104585aada1ff1972867f5d464b34df588dd69eec1ae24074372f51f",
                "md5": "7b52e7b22e7ec31bc0a3add0e3657dd8",
                "sha256": "d186c17739326dc3a3fa0df96bcfaf25b2fd057d6689554f7a64c422309bba57"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp39-cp39-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "7b52e7b22e7ec31bc0a3add0e3657dd8",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 296670,
            "upload_time": "2024-05-02T14:03:59",
            "upload_time_iso_8601": "2024-05-02T14:03:59.884939Z",
            "url": "https://files.pythonhosted.org/packages/5a/4c/8e81104585aada1ff1972867f5d464b34df588dd69eec1ae24074372f51f/hunter-3.7.0-cp39-cp39-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "575cfb632014859fb3d5bdd73e33592c8b4f701a5392f87ba6488479d41bbe92",
                "md5": "cd42feedd556a1ee84cb438c8a069b23",
                "sha256": "0de84cbf88b6f57ce573cd46f64493cb3e0739e689a8895d932cbcb631691d9b"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "has_sig": false,
            "md5_digest": "cd42feedd556a1ee84cb438c8a069b23",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 1615921,
            "upload_time": "2024-05-02T14:04:51",
            "upload_time_iso_8601": "2024-05-02T14:04:51.493472Z",
            "url": "https://files.pythonhosted.org/packages/57/5c/fb632014859fb3d5bdd73e33592c8b4f701a5392f87ba6488479d41bbe92/hunter-3.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "dcfcab11568de8ed772dbd395452b340a0c7c92ea1d8d7b741e74a956a023abf",
                "md5": "726b44a083244bd9540f564530f229f9",
                "sha256": "57e0895d1b1b7ad57204ec484c08cb720d5a2fcbe7c4f8f05add05c0dc1527eb"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "726b44a083244bd9540f564530f229f9",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 1631983,
            "upload_time": "2024-05-02T14:04:57",
            "upload_time_iso_8601": "2024-05-02T14:04:57.727842Z",
            "url": "https://files.pythonhosted.org/packages/dc/fc/ab11568de8ed772dbd395452b340a0c7c92ea1d8d7b741e74a956a023abf/hunter-3.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ee5e81899db03bbca74cb989685571f59ca85955e0d80616b53106ae178bb467",
                "md5": "6cfeaeac8ddb64b647d1e2df0abef934",
                "sha256": "b97bf277f4471f9061d7c6f9c23ce66f5132fb98374d5ee28f55310eecd5fe1f"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp39-cp39-musllinux_1_1_aarch64.whl",
            "has_sig": false,
            "md5_digest": "6cfeaeac8ddb64b647d1e2df0abef934",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 1635670,
            "upload_time": "2024-05-02T14:04:53",
            "upload_time_iso_8601": "2024-05-02T14:04:53.570002Z",
            "url": "https://files.pythonhosted.org/packages/ee/5e/81899db03bbca74cb989685571f59ca85955e0d80616b53106ae178bb467/hunter-3.7.0-cp39-cp39-musllinux_1_1_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "309543e74b75e8b0671ca6c76a62e5eb1ed917c64b085f4d217093bae771c4d3",
                "md5": "d1b4de8a3d129b06d4b1ac845c839d36",
                "sha256": "49b0da542802a5c873f9e6a7ec95c52428e3565e1922386f5df0e20b907f47d9"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp39-cp39-musllinux_1_1_x86_64.whl",
            "has_sig": false,
            "md5_digest": "d1b4de8a3d129b06d4b1ac845c839d36",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 1643751,
            "upload_time": "2024-05-02T14:05:00",
            "upload_time_iso_8601": "2024-05-02T14:05:00.618115Z",
            "url": "https://files.pythonhosted.org/packages/30/95/43e74b75e8b0671ca6c76a62e5eb1ed917c64b085f4d217093bae771c4d3/hunter-3.7.0-cp39-cp39-musllinux_1_1_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "901d56069c6f30e6a95b3af226d53524feabceee20865694abcdce39faa9925b",
                "md5": "6d7db5758a917fdea614f922f3071f37",
                "sha256": "2169ef7e92ca57f2829a2d9291ba5ead093c197b330ba6630871715964ea4ba1"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp39-cp39-win32.whl",
            "has_sig": false,
            "md5_digest": "6d7db5758a917fdea614f922f3071f37",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 243957,
            "upload_time": "2024-05-02T14:05:35",
            "upload_time_iso_8601": "2024-05-02T14:05:35.183463Z",
            "url": "https://files.pythonhosted.org/packages/90/1d/56069c6f30e6a95b3af226d53524feabceee20865694abcdce39faa9925b/hunter-3.7.0-cp39-cp39-win32.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "eacc37efecf3498944d30739e95d17481c2b8f39f357e469aeee10dd3ff3cc2f",
                "md5": "c149d9c5595671b30629d406eb9c4c90",
                "sha256": "36b149cd8b7eb2af007bd69ce79f64452c7cb03a0d136764ed8261da7bce3501"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-cp39-cp39-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "c149d9c5595671b30629d406eb9c4c90",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 282999,
            "upload_time": "2024-05-02T14:05:32",
            "upload_time_iso_8601": "2024-05-02T14:05:32.687792Z",
            "url": "https://files.pythonhosted.org/packages/ea/cc/37efecf3498944d30739e95d17481c2b8f39f357e469aeee10dd3ff3cc2f/hunter-3.7.0-cp39-cp39-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "13238549752fc57eff4b1ae113703b4d7943180b0aedb9d3513126b3605ca03f",
                "md5": "57b8ff39889a8536454ee1b06aa2855b",
                "sha256": "b9955013d2e8eb4d0472823153086cba7c36f894ea3acab43aface1d9d27444c"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0-pp310.pp38.pp39-none-any.whl",
            "has_sig": false,
            "md5_digest": "57b8ff39889a8536454ee1b06aa2855b",
            "packagetype": "bdist_wheel",
            "python_version": "pp310.pp38.pp39",
            "requires_python": ">=3.8",
            "size": 52719,
            "upload_time": "2024-05-02T14:03:46",
            "upload_time_iso_8601": "2024-05-02T14:03:46.587746Z",
            "url": "https://files.pythonhosted.org/packages/13/23/8549752fc57eff4b1ae113703b4d7943180b0aedb9d3513126b3605ca03f/hunter-3.7.0-pp310.pp38.pp39-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8f92f21e6066609232e5fa564b98ece34eaab70caee2e854ea1b45776a33edcb",
                "md5": "61c0c6f0ae46b6de1c385b3b7b36c345",
                "sha256": "16f4fb4dbde366bac61ebf235bc4d075bfd8748009c8a4e0327d2fbca6f611d6"
            },
            "downloads": -1,
            "filename": "hunter-3.7.0.tar.gz",
            "has_sig": false,
            "md5_digest": "61c0c6f0ae46b6de1c385b3b7b36c345",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 672577,
            "upload_time": "2024-05-02T14:07:03",
            "upload_time_iso_8601": "2024-05-02T14:07:03.833585Z",
            "url": "https://files.pythonhosted.org/packages/8f/92/f21e6066609232e5fa564b98ece34eaab70caee2e854ea1b45776a33edcb/hunter-3.7.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-05-02 14:07:03",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ionelmc",
    "github_project": "python-hunter",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "tox": true,
    "lcname": "hunter"
}
        
Elapsed time: 2.97792s