ionelmc / python-hunter

Hunter is a flexible code tracing toolkit.
https://python-hunter.readthedocs.io/
BSD 2-Clause "Simplified" License
794 stars 46 forks source link

event.stdlib returns false-positive #103

Open guettli opened 3 years ago

guettli commented 3 years ago

Unfortunately event.stdlib is True in my case, although the code is not from stdlib.

Here is a simple way to reproduce it:

python3 -m venv hunter-env

cd hunter-env

. bin/activate

pip install python-hunter

vi t.py

file t.py:

import hunter

def foo():
    i=0
    i+=1
    print(f'i: {i}')

def print_stdlib(event):
    print(f'{event.source.strip()} {event.stdlib}')

with hunter.trace(print_stdlib):
    foo()

main.py

import t

Call main.py

python main.py

output:

def trace(*predicates, **options): False
foo() True
def foo(): True
i=0 True
i+=1 True
print(f'i: {i}') True
i: 1
print(f'i: {i}') True

===> hunter thinks "foo()" is in stdlib, but it is not.


I guess it is because of:

            elif self.filename.startswith(SYS_PREFIX_PATHS):
                self._stdlib = True

in _event.pyx

Maybe it would help to exclude the virtualenv from SYS_PREFIX_PATHS.

ionelmc commented 3 years ago

Well technically t.py is not in the virtualenv. There were some bugs regarding stdlib detection that I fixed in the latest version - I wonder if there's a regression.

Either way I'd like to know:

guettli commented 3 years ago

I don't have a strange path for cwd. It is /home/guettli/tmp/hunter-env

I use Ubuntu 20.04

I use the default Python 3.8.10

BTW, I installed python-hunter, not hunter. Now this looks strange:

user@host> pip freeze | grep hunt

hunter==3.3.8
python-hunter==0.0

I modified t.py to output the prefixes which get used for SYS_PREFIX_PATHS

import sys
import hunter

def foo():
    i=0
    i+=1
    print(f'i: {i}')

def print_stdlib(event):
    print(f'{event.source.strip()} {event.stdlib}')

def show_sys_prefixes():
    for name in 'prefix exec_prefix real_prefix real_exec_prefix base_prefix base_exec_prefix'.split():
        print(name, getattr(sys, name, None))
    print()

show_sys_prefixes()

with hunter.trace(print_stdlib):
    foo()

The prefixes:

prefix /home/guettli/tmp/hunter-env
exec_prefix /home/guettli/tmp/hunter-env
real_prefix None
real_exec_prefix None
base_prefix /usr
base_exec_prefix /usr

And t.py is in /home/guettli/tmp/hunter-env

Outside the virtualenv the result looks like this:

prefix /usr
exec_prefix /usr
real_prefix None
real_exec_prefix None
base_prefix /usr
base_exec_prefix /usr

Maybe it makes sense to exclude prefix and exec_prefix.

guettli commented 3 years ago

I found this ugly hack, which seems to work for my use case:

def is_stdlib(event: Event):
    # Issue https://github.com/ionelmc/python-hunter/issues/103
    is_stdlib = event.stdlib
    if not is_stdlib:
        return False
    if sys.prefix != sys.base_prefix:
        # In virtualenv
        if event.filename.startswith(sys.prefix):
            return False
    return True