Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make sure functions can be excluded #263

Merged
merged 1 commit into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions _appmap/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,16 @@ def instrument_functions(filterable, selected_functions=None):
logger.debug(" functions %s", functions)

for fn_name, static_fn, fn in functions:
if selected_functions and fn_name not in selected_functions:
continue

new_fn = cls.filter_chain.wrap(FilterableFn(filterable, fn, static_fn))
if fn != new_fn:
wrapt.wrap_function_wrapper(filterable.obj, fn_name, new_fn)
# Only instrument the function if it was specifically called out for the package
# (e.g. because it should be labeled), or it's included by the filters
filterableFn = FilterableFn(filterable, fn, static_fn)
matched = cls.filter_chain.filter(filterableFn)
selected = selected_functions and fn_name in selected_functions
if selected or matched:
new_fn = cls.filter_chain.wrap(filterableFn)
if fn != new_fn:
wrapt.wrap_function_wrapper(filterable.obj, fn_name, new_fn)

# Import Config here, to avoid circular top-level imports.
from .configuration import Config # pylint: disable=import-outside-toplevel
Expand Down
10 changes: 9 additions & 1 deletion _appmap/test/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import importlib
import sys
from distutils.dir_util import copy_tree
from functools import partialmethod

Expand Down Expand Up @@ -65,6 +66,10 @@ def pytest_runtest_setup(item):
# Reload it to make sure it's instrumented, or not, as set in appmap.yml.
importlib.reload(yaml)

# Remove the example_class module, so it will get reinstrumented the next time it's needed. We
# should find a way to do this more generically, i.e. for any modules loaded by a test case.
sys.modules.pop("example_class", None)


@pytest.fixture(scope="session", name="git_directory")
def git_directory_fixture(tmp_path_factory):
Expand Down Expand Up @@ -100,6 +105,7 @@ def _dj_autoclear_mailbox() -> None:

@pytest.fixture(name="verify_example_appmap")
def fixture_verify_appmap(monkeypatch):

def _generate(check_fn, method_name):
monkeypatch.setattr(
generation.FuncEntry,
Expand All @@ -110,7 +116,9 @@ def _generate(check_fn, method_name):
rec = appmap.Recording()
with rec:
# pylint: disable=import-outside-toplevel, import-error
from example_class import ExampleClass
from example_class import ( # pyright: ignore[reportMissingImports]
ExampleClass,
)

# pylint: enable=import-outside-toplevel, import-error

Expand Down
7 changes: 7 additions & 0 deletions _appmap/test/data/appmap-exclude-fn.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

name: TestApp
packages:
- path: example_class
exclude:
- ExampleClass.another_method
- path: package1.package2.Mod1Class
13 changes: 13 additions & 0 deletions _appmap/test/data/appmap-no-pyyaml.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: TestApp
packages:
# note: this rule needs to go first, before the more general rule
- path: example_class.Super
shallow: true
- path: example_class
- path: appmap_testing
- path: package1
labels:
serialization: yaml.dump
example-label:
- example_class.ExampleClass.test_exception
- yaml.dump
18 changes: 18 additions & 0 deletions _appmap/test/test_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import sys

import pytest

from _appmap.importer import Importer, wrap_exec_module
from _appmap.wrapt.wrappers import BoundFunctionWrapper


def test_exec_module_protection(monkeypatch):
Expand All @@ -28,3 +31,18 @@ def do_import(*args, **kwargs): # pylint: disable=unused-argument

f()
assert True


@pytest.mark.appmap_enabled(config="appmap-exclude-fn.yml")
@pytest.mark.usefixtures("with_data_dir")
def test_excluded(verify_example_appmap):
def check_imports(*_):
# pylint: disable=import-outside-toplevel, import-error
from example_class import ExampleClass # pyright: ignore[reportMissingImports]

# pylint: enable=import-outside-toplevel, import-error

assert isinstance(ExampleClass.instance_method, BoundFunctionWrapper)
assert not isinstance(ExampleClass.another_method, BoundFunctionWrapper)

verify_example_appmap(check_imports, "instance_method")
1 change: 1 addition & 0 deletions _appmap/test/test_labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def check_labels(*_):

verify_example_appmap(check_labels, "instance_method")

@pytest.mark.appmap_enabled(config="appmap-no-pyyaml.yml")
def test_mod_instrumented_by_preset(self, verify_example_appmap):
def check_labels(*_):
import yaml # pylint: disable=import-outside-toplevel
Expand Down