Skip to content

Commit

Permalink
Improve the default code filter's correctness and performance. (#40)
Browse files Browse the repository at this point in the history
* Improve the default code filter to resolve symlinks

The wrong paths might be used when filtering depending on if the paths
Python is configured to use are actually symlinks on the underlying
system (for example, when /lib64 is a symlink to /lib).

* Significantly improve the default filter's performance with a cache

Resolving symlinks per function call has a large performance penalty.
But there's also no need to constantly recompute the default filter.
  • Loading branch information
vodik authored and carljm committed Dec 22, 2017
1 parent ade1a86 commit c93d6a7
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 5 deletions.
21 changes: 17 additions & 4 deletions monkeytype/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
from contextlib import contextmanager
import functools
import os
import pathlib
import sys
import sysconfig

Expand Down Expand Up @@ -95,14 +97,25 @@ def include_unparsable_defaults(self) -> bool:
lib_paths.add(
sysconfig.get_path('stdlib', vars={'installed_base': venv_real_prefix})
)
# exclude code objects from frozen importlib, which have a bogus co_filename
lib_paths.add('<frozen importlib.')
LIB_PATHS = tuple(p for p in lib_paths if p is not None)
LIB_PATHS = tuple(pathlib.Path(p).resolve() for p in lib_paths if p is not None)


def _startswith(a: pathlib.Path, b: pathlib.Path) -> bool:
try:
return bool(a.relative_to(b))
except ValueError:
return False


@functools.lru_cache()
def default_code_filter(code: CodeType) -> bool:
"""A CodeFilter to exclude stdlib and site-packages."""
return bool(code.co_filename and not code.co_filename.startswith(LIB_PATHS))
# Filter code without a source file
if not code.co_filename or code.co_filename[0] == '<':
return False

filename = pathlib.Path(code.co_filename).resolve()
return not any(_startswith(filename, lib_path) for lib_path in LIB_PATHS)


class DefaultConfig(Config):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def test_excludes_site_packages(self):
assert not config.default_code_filter(pytest.skip.__code__)

def test_includes_otherwise(self):
assert config.default_code_filter(config.default_code_filter.__code__)
assert config.default_code_filter(config.default_code_filter.__wrapped__.__code__)

def test_excludes_frozen_importlib(self):
assert not config.default_code_filter(_frozen_importlib.spec_from_loader.__code__)

0 comments on commit c93d6a7

Please sign in to comment.