appmap 2.1.2__tar.gz → 2.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {appmap-2.1.2 → appmap-2.1.4}/PKG-INFO +1 -1
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/configuration.py +40 -16
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/importer.py +21 -9
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/instrument.py +1 -1
- appmap-2.1.2/_appmap/test/data/unittest/appmap.yml → appmap-2.1.4/_appmap/test/data/pytest/appmap-no-test-cases.yml +1 -0
- appmap-2.1.4/_appmap/test/data/pytest/appmap.yml +5 -0
- appmap-2.1.2/_appmap/test/data/pytest/expected/pytest-numpy1.appmap.json → appmap-2.1.4/_appmap/test/data/pytest/expected/pytest-numpy1-no-test-cases.appmap.json +1 -1
- appmap-2.1.4/_appmap/test/data/pytest/expected/pytest-numpy1.appmap.json +281 -0
- appmap-2.1.2/_appmap/test/data/pytest/expected/pytest-numpy2.appmap.json → appmap-2.1.4/_appmap/test/data/pytest/expected/pytest-numpy2-no-test-cases.appmap.json +1 -1
- appmap-2.1.4/_appmap/test/data/pytest/expected/pytest-numpy2.appmap.json +281 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/pytest/expected/status_errored.metadata.json +1 -1
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/pytest/expected/status_failed.metadata.json +1 -1
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/pytest/expected/status_xfailed.metadata.json +1 -1
- appmap-2.1.4/_appmap/test/data/trial/appmap-no-test-cases.yml +4 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/trial/appmap.yml +1 -0
- appmap-2.1.4/_appmap/test/data/trial/expected/pytest-no-test-cases.appmap.json +28 -0
- appmap-2.1.4/_appmap/test/data/unittest/appmap-no-test-cases.yml +4 -0
- {appmap-2.1.2/_appmap/test/data/pytest → appmap-2.1.4/_appmap/test/data/unittest}/appmap.yml +1 -0
- appmap-2.1.4/_appmap/test/data/unittest/expected/unittest-no-test-cases.appmap.json +148 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_configuration.py +5 -2
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_events.py +0 -1
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_fastapi.py +2 -4
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_params.py +1 -1
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_properties.py +1 -4
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_test_frameworks.py +24 -1
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/testing_framework.py +15 -10
- appmap-2.1.4/_appmap/unittest.py +39 -0
- appmap-2.1.4/appmap/command/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/fastapi.py +1 -1
- {appmap-2.1.2 → appmap-2.1.4}/appmap/pytest.py +2 -0
- {appmap-2.1.2 → appmap-2.1.4}/pyproject.toml +3 -1
- {appmap-2.1.2 → appmap-2.1.4}/vendor/_appmap/wrapt/wrappers.py +18 -13
- appmap-2.1.2/_appmap/unittest.py +0 -84
- {appmap-2.1.2 → appmap-2.1.4}/LICENSE +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/README.md +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/django.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/env.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/event.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/fastapi.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/flask.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/generation.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/labels.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/metadata.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/noappmap.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/py_version_check.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/recorder.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/recording.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/remote_recording.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/singleton.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/appmap_test_base.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/bin/server_runner +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/conftest.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap-all-paths-malformed.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap-broken.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap-class.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap-empty-path.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap-exclude-fn.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap-func.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap-malformed-path.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap-no-pyyaml.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap_testing/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/appmap_testing/django_simplelazyobject.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config/src/package/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config/test/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config/test/foo.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config-exclude/.hide/hidden_mod/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config-exclude/node_modules/node_mod/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config-exclude/src/package/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config-exclude/test/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config-exclude/venv/venv_mod/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config-up/appmap.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config-up/project/p1/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/config-up/project/p2/sub1/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/appmap.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/djangoapp/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/djangoapp/djangoapp.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/djangoapp/hello_world.html +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/djangoapp/middleware.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/djangoapp/settings.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/djangoapp/settings_dev.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/djangoapp/urls.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/init/sitecustomize.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/manage.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/pytest.ini +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/test/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/test/test_app.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/django/test/test_unittest_setup.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/example_class.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/expected.appmap.json +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/fastapi/appmap.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/fastapi/fastapiapp/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/fastapi/fastapiapp/main.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/fastapi/init/sitecustomize.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/fastapi/test_app.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/flask/appmap.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/flask/flaskapp.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/flask/init/sitecustomize.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/flask/templates/test.html +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/flask/test_app.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/flask-instrumented/appmap.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/flask-instrumented/flaskapp.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/flask-instrumented/init/sitecustomize.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/flask-instrumented/test_app.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/package1/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/package1/package2/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/package1/package2/__main__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/package1/package2/mod1.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/params.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/pytest/simple.py +0 -0
- {appmap-2.1.2/_appmap/test/data/trial/test → appmap-2.1.4/_appmap/test/data/pytest/tests}/__init__.py +0 -0
- {appmap-2.1.2/_appmap/test/data/pytest → appmap-2.1.4/_appmap/test/data/pytest/tests}/test_noappmap.py +0 -0
- {appmap-2.1.2/_appmap/test/data/pytest → appmap-2.1.4/_appmap/test/data/pytest/tests}/test_simple.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/pytest-instrumented/appmap.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/pytest-instrumented/init/sitecustomize.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/pytest-instrumented/test_instrumented.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/remote.appmap.json +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/trial/.gitignore +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/trial/expected/pytest.appmap.json +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/trial/init/sitecustomize.py +0 -0
- {appmap-2.1.2/appmap/command → appmap-2.1.4/_appmap/test/data/trial/test}/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/trial/test/test_deferred.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/expected/pytest.appmap.json +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/expected/status_errored.metadata.json +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/expected/status_failed.metadata.json +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/expected/status_xfailed.metadata.json +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/expected/status_xsucceeded.metadata.json +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/expected/unittest.appmap.json +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/init/sitecustomize.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/simple/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/simple/test_noappmap.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/data/unittest/simple/test_simple.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/helpers.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/normalize.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_command.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_describe_value.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_django.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_django_simplelazyobject.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_env.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_flask.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_generation.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_http.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_importer.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_labels.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_metadata.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_recording.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_runner.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_sqlalchemy.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/test_util.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/test/web_framework.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/trace_logger.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/utils.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/_appmap/web_framework.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/command/appmap_agent_init.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/command/appmap_agent_status.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/command/appmap_agent_validate.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/command/runner.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/django.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/flask.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/http.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/crypto.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/django.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/flask.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/formats.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/http.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/jobs.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/jwt.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/logger.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/labeling/random.yml +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/sqlalchemy.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/unittest.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap/uvicorn.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/appmap.pth +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/vendor/_appmap/wrapt/LICENSE +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/vendor/_appmap/wrapt/__init__.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/vendor/_appmap/wrapt/arguments.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/vendor/_appmap/wrapt/decorators.py +0 -0
- {appmap-2.1.2 → appmap-2.1.4}/vendor/_appmap/wrapt/importer.py +0 -0
|
@@ -5,13 +5,16 @@ Manage Configuration AppMap recorder for Python.
|
|
|
5
5
|
import ast
|
|
6
6
|
import importlib.metadata
|
|
7
7
|
import inspect
|
|
8
|
+
import json
|
|
8
9
|
import os
|
|
10
|
+
import re
|
|
9
11
|
import sys
|
|
10
12
|
from os.path import realpath
|
|
11
13
|
from pathlib import Path
|
|
12
14
|
from textwrap import dedent
|
|
13
15
|
|
|
14
16
|
import yaml
|
|
17
|
+
from yaml import SafeLoader
|
|
15
18
|
from yaml.parser import ParserError
|
|
16
19
|
|
|
17
20
|
from _appmap.labels import LabelSet
|
|
@@ -49,20 +52,19 @@ def _get_sys_prefix():
|
|
|
49
52
|
return realpath(sys.prefix)
|
|
50
53
|
|
|
51
54
|
|
|
55
|
+
_EXCLUDE_PATTERN = re.compile(r"\..*|node_modules|.*test.*|site-packages")
|
|
56
|
+
|
|
57
|
+
|
|
52
58
|
def find_top_packages(rootdir):
|
|
53
59
|
"""
|
|
54
|
-
Scan a directory tree for packages that should appear in the
|
|
55
|
-
default config file.
|
|
60
|
+
Scan a directory tree for packages that should appear in the default config file.
|
|
56
61
|
|
|
57
|
-
Examine
|
|
58
|
-
|
|
59
|
-
scan any of its subdirectories. If it doesn't, scan its
|
|
62
|
+
Examine each directory in rootdir, to see if it contains an __init__.py. If it does, add it to
|
|
63
|
+
the list of packages and don't scan any of its subdirectories. If it doesn't, scan its
|
|
60
64
|
subdirectories to find __init__.py.
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
* Hidden directories (i.e. those that start with a '.')
|
|
65
|
-
* node_modules
|
|
66
|
+
Directory traversal will stop at directories that match _EXCLUDE_PATTERN. Such a directory (and
|
|
67
|
+
its subdirectories) will not be added to the returned packages.
|
|
66
68
|
|
|
67
69
|
For example, in a directory like this
|
|
68
70
|
|
|
@@ -70,7 +72,7 @@ def find_top_packages(rootdir):
|
|
|
70
72
|
LICENSE Makefile appveyor.yml docs/ src/ tests/
|
|
71
73
|
MANIFEST.in README.rst blog/ setup.py tddium.yml tox.ini
|
|
72
74
|
|
|
73
|
-
docs, src,
|
|
75
|
+
docs, src, blog will get scanned. tests will be ignored.
|
|
74
76
|
|
|
75
77
|
Only src has a subdirectory containing an __init__.py:
|
|
76
78
|
|
|
@@ -104,7 +106,7 @@ def find_top_packages(rootdir):
|
|
|
104
106
|
packages = set()
|
|
105
107
|
|
|
106
108
|
def excluded(d):
|
|
107
|
-
excluded = d
|
|
109
|
+
excluded = _EXCLUDE_PATTERN.search(d) is not None
|
|
108
110
|
if excluded:
|
|
109
111
|
logger.trace("excluding dir %s", d)
|
|
110
112
|
return excluded
|
|
@@ -129,6 +131,19 @@ def find_top_packages(rootdir):
|
|
|
129
131
|
class AppMapInvalidConfigException(Exception):
|
|
130
132
|
pass
|
|
131
133
|
|
|
134
|
+
# We don't have any control over the PyYAML class hierarchy, so we can't control how many ancestors
|
|
135
|
+
# SafeLoader has....
|
|
136
|
+
class _ConfigLoader(SafeLoader): # pylint: disable=too-many-ancestors
|
|
137
|
+
def construct_mapping(self, node, deep=False):
|
|
138
|
+
mapping = super().construct_mapping(node, deep=deep)
|
|
139
|
+
# Allow record_test_cases to be set using a string (in addition to allowing a boolean).
|
|
140
|
+
if "record_test_cases" in mapping:
|
|
141
|
+
val = mapping["record_test_cases"]
|
|
142
|
+
if isinstance(val, str):
|
|
143
|
+
mapping["record_test_cases"] = val.lower() == "true"
|
|
144
|
+
return mapping
|
|
145
|
+
|
|
146
|
+
|
|
132
147
|
class Config(metaclass=SingletonMeta):
|
|
133
148
|
"""Singleton Config class"""
|
|
134
149
|
|
|
@@ -144,6 +159,9 @@ class Config(metaclass=SingletonMeta):
|
|
|
144
159
|
if "labels" in self._config:
|
|
145
160
|
self.labels.append(self._config["labels"])
|
|
146
161
|
|
|
162
|
+
def __repr__(self):
|
|
163
|
+
return json.dumps(self._config["packages"])
|
|
164
|
+
|
|
147
165
|
@property
|
|
148
166
|
def name(self):
|
|
149
167
|
return self._config["name"]
|
|
@@ -152,11 +170,16 @@ class Config(metaclass=SingletonMeta):
|
|
|
152
170
|
def packages(self):
|
|
153
171
|
return self._config["packages"]
|
|
154
172
|
|
|
173
|
+
@property
|
|
174
|
+
def record_test_cases(self):
|
|
175
|
+
return self._config.get("record_test_cases", False)
|
|
176
|
+
|
|
155
177
|
@property
|
|
156
178
|
def default(self):
|
|
157
179
|
ret = {
|
|
158
180
|
"name": self.default_name,
|
|
159
181
|
"language": "python",
|
|
182
|
+
"record_test_cases": False,
|
|
160
183
|
"packages": self.default_packages,
|
|
161
184
|
}
|
|
162
185
|
env = Env.current
|
|
@@ -229,7 +252,7 @@ class Config(metaclass=SingletonMeta):
|
|
|
229
252
|
Env.current.enabled = False
|
|
230
253
|
self.file_valid = False
|
|
231
254
|
try:
|
|
232
|
-
self._config = yaml.
|
|
255
|
+
self._config = yaml.load(path.read_text(encoding="utf-8"), Loader=_ConfigLoader)
|
|
233
256
|
if not self._config:
|
|
234
257
|
# It parsed, but was (effectively) empty.
|
|
235
258
|
self._config = self.default
|
|
@@ -330,7 +353,6 @@ It will be created with this configuration:
|
|
|
330
353
|
except SyntaxError:
|
|
331
354
|
return False
|
|
332
355
|
|
|
333
|
-
|
|
334
356
|
def startswith(prefix, sequence):
|
|
335
357
|
"""
|
|
336
358
|
Check if a sequence starts with the prefix.
|
|
@@ -373,7 +395,8 @@ class DistMatcher(PathMatcher):
|
|
|
373
395
|
def __init__(self, dist, *args, **kwargs):
|
|
374
396
|
super().__init__(*args, **kwargs)
|
|
375
397
|
self.dist = dist
|
|
376
|
-
|
|
398
|
+
dist_files = importlib.metadata.files(dist)
|
|
399
|
+
self.files = [str(pp.locate()) for pp in dist_files] if dist_files is not None else []
|
|
377
400
|
|
|
378
401
|
def matches(self, filterable):
|
|
379
402
|
try:
|
|
@@ -419,8 +442,8 @@ class MatcherFilter(Filter):
|
|
|
419
442
|
# appropriate.
|
|
420
443
|
#
|
|
421
444
|
rule = self.match(filterable)
|
|
422
|
-
wrapped = getattr(filterable.obj, "
|
|
423
|
-
if wrapped
|
|
445
|
+
wrapped = getattr(filterable.obj, "_appmap_instrumented", None)
|
|
446
|
+
if not wrapped:
|
|
424
447
|
logger.trace(" wrapping %s", filterable.fqname)
|
|
425
448
|
Config.current.labels.apply(filterable)
|
|
426
449
|
ret = instrument(filterable)
|
|
@@ -483,6 +506,7 @@ if _startup_messages_shown is None:
|
|
|
483
506
|
# pylint: disable=protected-access
|
|
484
507
|
c._load_config(show_warnings=True)
|
|
485
508
|
logger.info("file: %s", c._file if c.file_present else "[no appmap.yml]")
|
|
509
|
+
logger.info("config: %r", c)
|
|
486
510
|
logger.debug("package_functions: %s", c.package_functions)
|
|
487
511
|
logger.info("env: %r", os.environ)
|
|
488
512
|
os.environ["_APPMAP_MESSAGES_SHOWN"] = "true"
|
|
@@ -42,8 +42,8 @@ class FilterableFn(
|
|
|
42
42
|
):
|
|
43
43
|
__slots__ = ()
|
|
44
44
|
|
|
45
|
-
def __new__(cls, scope, fn, static_fn, auxtype=None):
|
|
46
|
-
fqname = "%s.%s" % (scope.fqname,
|
|
45
|
+
def __new__(cls, scope, fn_name, fn, static_fn, auxtype=None): # pylint: disable=too-many-arguments
|
|
46
|
+
fqname = "%s.%s" % (scope.fqname, fn_name)
|
|
47
47
|
self = super(FilterableFn, cls).__new__(cls, scope.scope, fqname, fn, static_fn, auxtype)
|
|
48
48
|
return self
|
|
49
49
|
|
|
@@ -132,7 +132,9 @@ def get_members(cls):
|
|
|
132
132
|
if key.startswith("__"):
|
|
133
133
|
continue
|
|
134
134
|
static_value = inspect.getattr_static(cls, key)
|
|
135
|
-
|
|
135
|
+
# Don't use isinstance to check the type of static_value -- we don't want to invoke the
|
|
136
|
+
# descriptor protocol.
|
|
137
|
+
if Importer.instrument_properties and type(static_value) is property: # pylint: disable=unidiomatic-typecheck
|
|
136
138
|
properties[key] = (
|
|
137
139
|
static_value,
|
|
138
140
|
{
|
|
@@ -164,6 +166,9 @@ class Importer:
|
|
|
164
166
|
cls.filter_stack = []
|
|
165
167
|
cls.filter_chain = []
|
|
166
168
|
cls._skip_instrumenting = ("appmap", "_appmap")
|
|
169
|
+
cls.instrument_properties = (
|
|
170
|
+
Env.current.get("APPMAP_INSTRUMENT_PROPERTIES", "false").lower() == "true"
|
|
171
|
+
)
|
|
167
172
|
|
|
168
173
|
@classmethod
|
|
169
174
|
def use_filter(cls, filter_class):
|
|
@@ -191,27 +196,34 @@ class Importer:
|
|
|
191
196
|
cls.filter_chain = reduce(lambda acc, e: e(acc), cls.filter_stack, NullFilter(None))
|
|
192
197
|
|
|
193
198
|
def instrument_functions(filterable, selected_functions=None):
|
|
199
|
+
# pylint: disable=too-many-locals
|
|
194
200
|
logger.trace(" looking for members of %s", filterable.obj)
|
|
195
201
|
functions, properties = get_members(filterable.obj)
|
|
196
202
|
logger.trace(" functions %s", functions)
|
|
197
203
|
|
|
198
204
|
for fn_name, static_fn, fn in functions:
|
|
199
|
-
filterableFn = FilterableFn(filterable, fn, static_fn)
|
|
205
|
+
filterableFn = FilterableFn(filterable, fn.__name__, fn, static_fn)
|
|
200
206
|
new_fn = cls.instrument_function(fn_name, filterableFn, selected_functions)
|
|
201
207
|
if new_fn != fn:
|
|
202
|
-
wrapt.wrap_function_wrapper(filterable.obj, fn_name, new_fn)
|
|
208
|
+
fw = wrapt.wrap_function_wrapper(filterable.obj, fn_name, new_fn)
|
|
209
|
+
fw._appmap_instrumented = True # pylint: disable=protected-access
|
|
210
|
+
|
|
203
211
|
# Now that we've instrumented all the functions, go through the properties and update
|
|
204
212
|
# them
|
|
205
213
|
for prop_name, (prop, prop_fns) in properties.items():
|
|
206
214
|
instrumented_fns = {}
|
|
207
215
|
for k, (fn, auxtype) in prop_fns.items():
|
|
208
|
-
if fn is None
|
|
216
|
+
if fn is None:
|
|
209
217
|
continue
|
|
210
|
-
filterableFn = FilterableFn(filterable, fn, fn, auxtype)
|
|
211
|
-
|
|
218
|
+
filterableFn = FilterableFn(filterable, prop_name, fn, fn, auxtype)
|
|
219
|
+
if getattr(fn, "_appmap_instrumented", None):
|
|
220
|
+
continue
|
|
221
|
+
new_fn = cls.instrument_function(prop_name, filterableFn, selected_functions)
|
|
212
222
|
if new_fn != fn:
|
|
213
223
|
new_fn = wrapt.FunctionWrapper(fn, new_fn)
|
|
214
|
-
|
|
224
|
+
# Set _appmap_instrumented on the FunctionWrapper, not on the wrapped function
|
|
225
|
+
new_fn._appmap_instrumented = True # pylint: disable=protected-access
|
|
226
|
+
|
|
215
227
|
instrumented_fns[k] = new_fn
|
|
216
228
|
if len(instrumented_fns) > 0:
|
|
217
229
|
instrumented_fns["doc"] = prop.__doc__
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.9",
|
|
3
|
+
"metadata": {
|
|
4
|
+
"language": {
|
|
5
|
+
"name": "python"
|
|
6
|
+
},
|
|
7
|
+
"client": {
|
|
8
|
+
"name": "appmap",
|
|
9
|
+
"url": "https://github.com/applandinc/appmap-python"
|
|
10
|
+
},
|
|
11
|
+
"source_location": "tests/test_simple.py:5",
|
|
12
|
+
"name": "hello world",
|
|
13
|
+
"feature": "Hello world",
|
|
14
|
+
"app": "Simple",
|
|
15
|
+
"recorder": {
|
|
16
|
+
"name": "pytest",
|
|
17
|
+
"type": "tests"
|
|
18
|
+
},
|
|
19
|
+
"test_status": "succeeded"
|
|
20
|
+
},
|
|
21
|
+
"events": [
|
|
22
|
+
{
|
|
23
|
+
"static": true,
|
|
24
|
+
"parameters": [],
|
|
25
|
+
"id": 1,
|
|
26
|
+
"event": "call",
|
|
27
|
+
"thread_id": 1,
|
|
28
|
+
"defined_class": "tests.test_simple",
|
|
29
|
+
"method_id": "test_hello_world",
|
|
30
|
+
"path": "tests/test_simple.py",
|
|
31
|
+
"lineno": 6
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"static": false,
|
|
35
|
+
"receiver": {
|
|
36
|
+
"kind": "req",
|
|
37
|
+
"value": "<simple.Simple object at 0xabcdef>",
|
|
38
|
+
"name": "self",
|
|
39
|
+
"class": "simple.Simple"
|
|
40
|
+
},
|
|
41
|
+
"parameters": [],
|
|
42
|
+
"id": 2,
|
|
43
|
+
"event": "call",
|
|
44
|
+
"thread_id": 1,
|
|
45
|
+
"defined_class": "simple.Simple",
|
|
46
|
+
"method_id": "hello_world",
|
|
47
|
+
"path": "simple.py",
|
|
48
|
+
"lineno": 8
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"static": false,
|
|
52
|
+
"receiver": {
|
|
53
|
+
"kind": "req",
|
|
54
|
+
"value": "<simple.Simple object at 0xabcdef>",
|
|
55
|
+
"name": "self",
|
|
56
|
+
"class": "simple.Simple"
|
|
57
|
+
},
|
|
58
|
+
"parameters": [],
|
|
59
|
+
"id": 3,
|
|
60
|
+
"event": "call",
|
|
61
|
+
"thread_id": 1,
|
|
62
|
+
"defined_class": "simple.Simple",
|
|
63
|
+
"method_id": "hello",
|
|
64
|
+
"path": "simple.py",
|
|
65
|
+
"lineno": 2
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"return_value": {
|
|
69
|
+
"value": "'Hello'",
|
|
70
|
+
"class": "builtins.str"
|
|
71
|
+
},
|
|
72
|
+
"parent_id": 3,
|
|
73
|
+
"id": 4,
|
|
74
|
+
"event": "return",
|
|
75
|
+
"thread_id": 1
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"static": false,
|
|
79
|
+
"receiver": {
|
|
80
|
+
"kind": "req",
|
|
81
|
+
"value": "<simple.Simple object at 0xabcdef>",
|
|
82
|
+
"name": "self",
|
|
83
|
+
"class": "simple.Simple"
|
|
84
|
+
},
|
|
85
|
+
"parameters": [],
|
|
86
|
+
"id": 5,
|
|
87
|
+
"event": "call",
|
|
88
|
+
"thread_id": 1,
|
|
89
|
+
"defined_class": "simple.Simple",
|
|
90
|
+
"method_id": "world",
|
|
91
|
+
"path": "simple.py",
|
|
92
|
+
"lineno": 5
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"return_value": {
|
|
96
|
+
"value": "'world!'",
|
|
97
|
+
"class": "builtins.str"
|
|
98
|
+
},
|
|
99
|
+
"parent_id": 5,
|
|
100
|
+
"id": 6,
|
|
101
|
+
"event": "return",
|
|
102
|
+
"thread_id": 1
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"return_value": {
|
|
106
|
+
"value": "'Hello world!'",
|
|
107
|
+
"class": "builtins.str"
|
|
108
|
+
},
|
|
109
|
+
"parent_id": 2,
|
|
110
|
+
"id": 7,
|
|
111
|
+
"event": "return",
|
|
112
|
+
"thread_id": 1
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"static": false,
|
|
116
|
+
"receiver": {
|
|
117
|
+
"kind": "req",
|
|
118
|
+
"value": "<simple.Simple object at 0xabcdef>",
|
|
119
|
+
"name": "self",
|
|
120
|
+
"class": "simple.Simple"
|
|
121
|
+
},
|
|
122
|
+
"parameters": [],
|
|
123
|
+
"id": 8,
|
|
124
|
+
"event": "call",
|
|
125
|
+
"thread_id": 1,
|
|
126
|
+
"defined_class": "simple.Simple",
|
|
127
|
+
"method_id": "show_numpy_dict",
|
|
128
|
+
"path": "simple.py",
|
|
129
|
+
"lineno": 11
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"static": false,
|
|
133
|
+
"receiver": {
|
|
134
|
+
"kind": "req",
|
|
135
|
+
"value": "<simple.Simple object at 0xabcdef>",
|
|
136
|
+
"name": "self",
|
|
137
|
+
"class": "simple.Simple"
|
|
138
|
+
},
|
|
139
|
+
"parameters": [
|
|
140
|
+
{
|
|
141
|
+
"kind": "req",
|
|
142
|
+
"value": "{0: 'zero', 1: 'one'}",
|
|
143
|
+
"name": "d",
|
|
144
|
+
"class": "builtins.dict",
|
|
145
|
+
"properties": [
|
|
146
|
+
{
|
|
147
|
+
"name": "0",
|
|
148
|
+
"class": "builtins.str"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"name": "1",
|
|
152
|
+
"class": "builtins.str"
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
"size": 2
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
"id": 9,
|
|
159
|
+
"event": "call",
|
|
160
|
+
"thread_id": 1,
|
|
161
|
+
"defined_class": "simple.Simple",
|
|
162
|
+
"method_id": "get_numpy_dict",
|
|
163
|
+
"path": "simple.py",
|
|
164
|
+
"lineno": 18
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"return_value": {
|
|
168
|
+
"value": "{0: 'zero', 1: 'one'}",
|
|
169
|
+
"class": "builtins.dict",
|
|
170
|
+
"properties": [
|
|
171
|
+
{
|
|
172
|
+
"name": "0",
|
|
173
|
+
"class": "builtins.str"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"name": "1",
|
|
177
|
+
"class": "builtins.str"
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
"size": 2
|
|
181
|
+
},
|
|
182
|
+
"parent_id": 9,
|
|
183
|
+
"id": 10,
|
|
184
|
+
"event": "return",
|
|
185
|
+
"thread_id": 1
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"return_value": {
|
|
189
|
+
"value": "{0: 'zero', 1: 'one'}",
|
|
190
|
+
"class": "builtins.dict",
|
|
191
|
+
"properties": [
|
|
192
|
+
{
|
|
193
|
+
"name": "0",
|
|
194
|
+
"class": "builtins.str"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "1",
|
|
198
|
+
"class": "builtins.str"
|
|
199
|
+
}
|
|
200
|
+
],
|
|
201
|
+
"size": 2
|
|
202
|
+
},
|
|
203
|
+
"parent_id": 8,
|
|
204
|
+
"id": 11,
|
|
205
|
+
"event": "return",
|
|
206
|
+
"thread_id": 1
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"return_value": {
|
|
210
|
+
"value": "None",
|
|
211
|
+
"class": "builtins.NoneType"
|
|
212
|
+
},
|
|
213
|
+
"parent_id": 1,
|
|
214
|
+
"id": 12,
|
|
215
|
+
"event": "return",
|
|
216
|
+
"thread_id": 1
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
"classMap": [
|
|
220
|
+
{
|
|
221
|
+
"name": "simple",
|
|
222
|
+
"type": "package",
|
|
223
|
+
"children": [
|
|
224
|
+
{
|
|
225
|
+
"name": "Simple",
|
|
226
|
+
"type": "class",
|
|
227
|
+
"children": [
|
|
228
|
+
{
|
|
229
|
+
"name": "get_numpy_dict",
|
|
230
|
+
"type": "function",
|
|
231
|
+
"location": "simple.py:18",
|
|
232
|
+
"static": false
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "hello",
|
|
236
|
+
"type": "function",
|
|
237
|
+
"location": "simple.py:2",
|
|
238
|
+
"static": false
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"name": "hello_world",
|
|
242
|
+
"type": "function",
|
|
243
|
+
"location": "simple.py:8",
|
|
244
|
+
"static": false
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"name": "show_numpy_dict",
|
|
248
|
+
"type": "function",
|
|
249
|
+
"location": "simple.py:11",
|
|
250
|
+
"static": false
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"name": "world",
|
|
254
|
+
"type": "function",
|
|
255
|
+
"location": "simple.py:5",
|
|
256
|
+
"static": false
|
|
257
|
+
}
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"name": "tests",
|
|
264
|
+
"type": "package",
|
|
265
|
+
"children": [
|
|
266
|
+
{
|
|
267
|
+
"name": "test_simple",
|
|
268
|
+
"type": "class",
|
|
269
|
+
"children": [
|
|
270
|
+
{
|
|
271
|
+
"name": "test_hello_world",
|
|
272
|
+
"type": "function",
|
|
273
|
+
"location": "tests/test_simple.py:6",
|
|
274
|
+
"static": true
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
}
|