appmap 2.1.3__tar.gz → 2.1.5__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.3 → appmap-2.1.5}/PKG-INFO +1 -1
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/event.py +36 -4
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/importer.py +6 -5
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/instrument.py +1 -1
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap.yml +1 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/example_class.py +0 -49
- appmap-2.1.5/_appmap/test/data/properties_class.py +71 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_params.py +1 -2
- appmap-2.1.5/_appmap/test/test_properties.py +148 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/utils.py +2 -7
- {appmap-2.1.3 → appmap-2.1.5}/appmap/fastapi.py +1 -1
- {appmap-2.1.3 → appmap-2.1.5}/pyproject.toml +1 -1
- appmap-2.1.3/_appmap/test/test_properties.py +0 -105
- {appmap-2.1.3 → appmap-2.1.5}/LICENSE +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/README.md +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/configuration.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/django.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/env.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/fastapi.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/flask.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/generation.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/labels.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/metadata.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/noappmap.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/py_version_check.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/recorder.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/recording.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/remote_recording.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/singleton.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/appmap_test_base.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/bin/server_runner +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/conftest.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap-all-paths-malformed.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap-broken.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap-class.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap-empty-path.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap-exclude-fn.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap-func.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap-malformed-path.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap-no-pyyaml.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap_testing/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/appmap_testing/django_simplelazyobject.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config/src/package/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config/test/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config/test/foo.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config-exclude/.hide/hidden_mod/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config-exclude/node_modules/node_mod/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config-exclude/src/package/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config-exclude/test/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config-exclude/venv/venv_mod/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config-up/appmap.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config-up/project/p1/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/config-up/project/p2/sub1/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/appmap.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/djangoapp/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/djangoapp/djangoapp.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/djangoapp/hello_world.html +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/djangoapp/middleware.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/djangoapp/settings.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/djangoapp/settings_dev.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/djangoapp/urls.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/init/sitecustomize.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/manage.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/pytest.ini +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/test/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/test/test_app.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/django/test/test_unittest_setup.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/expected.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/fastapi/appmap.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/fastapi/fastapiapp/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/fastapi/fastapiapp/main.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/fastapi/init/sitecustomize.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/fastapi/test_app.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/flask/appmap.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/flask/flaskapp.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/flask/init/sitecustomize.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/flask/templates/test.html +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/flask/test_app.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/flask-instrumented/appmap.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/flask-instrumented/flaskapp.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/flask-instrumented/init/sitecustomize.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/flask-instrumented/test_app.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/package1/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/package1/package2/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/package1/package2/__main__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/package1/package2/mod1.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/params.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/appmap-no-test-cases.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/appmap.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/expected/pytest-numpy1-no-test-cases.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/expected/pytest-numpy1.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/expected/pytest-numpy2-no-test-cases.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/expected/pytest-numpy2.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/expected/status_errored.metadata.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/expected/status_failed.metadata.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/expected/status_xfailed.metadata.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/simple.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/tests/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/tests/test_noappmap.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest/tests/test_simple.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest-instrumented/appmap.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest-instrumented/init/sitecustomize.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/pytest-instrumented/test_instrumented.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/remote.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/trial/.gitignore +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/trial/appmap-no-test-cases.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/trial/appmap.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/trial/expected/pytest-no-test-cases.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/trial/expected/pytest.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/trial/init/sitecustomize.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/trial/test/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/trial/test/test_deferred.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/appmap-no-test-cases.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/appmap.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/expected/pytest.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/expected/status_errored.metadata.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/expected/status_failed.metadata.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/expected/status_xfailed.metadata.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/expected/status_xsucceeded.metadata.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/expected/unittest-no-test-cases.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/expected/unittest.appmap.json +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/init/sitecustomize.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/simple/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/simple/test_noappmap.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/data/unittest/simple/test_simple.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/helpers.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/normalize.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_command.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_configuration.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_describe_value.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_django.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_django_simplelazyobject.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_env.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_events.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_fastapi.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_flask.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_generation.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_http.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_importer.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_labels.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_metadata.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_recording.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_runner.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_sqlalchemy.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_test_frameworks.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/test_util.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/test/web_framework.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/testing_framework.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/trace_logger.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/unittest.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/_appmap/web_framework.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/command/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/command/appmap_agent_init.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/command/appmap_agent_status.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/command/appmap_agent_validate.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/command/runner.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/django.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/flask.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/http.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/crypto.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/django.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/flask.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/formats.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/http.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/jobs.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/jwt.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/logger.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/labeling/random.yml +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/pytest.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/sqlalchemy.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/unittest.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap/uvicorn.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/appmap.pth +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/vendor/_appmap/wrapt/LICENSE +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/vendor/_appmap/wrapt/__init__.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/vendor/_appmap/wrapt/arguments.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/vendor/_appmap/wrapt/decorators.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/vendor/_appmap/wrapt/importer.py +0 -0
- {appmap-2.1.3 → appmap-2.1.5}/vendor/_appmap/wrapt/wrappers.py +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
|
|
2
2
|
import inspect
|
|
3
3
|
import logging
|
|
4
|
+
import operator
|
|
4
5
|
import threading
|
|
5
6
|
from functools import lru_cache, partial
|
|
6
7
|
from inspect import Parameter, Signature
|
|
7
8
|
from itertools import chain
|
|
9
|
+
import types
|
|
8
10
|
|
|
9
11
|
from .env import Env
|
|
10
12
|
from .recorder import Recorder
|
|
@@ -174,24 +176,50 @@ class Param:
|
|
|
174
176
|
ret.update(describe_value(self.name, value))
|
|
175
177
|
return ret
|
|
176
178
|
|
|
179
|
+
def _get_name_parts(filterable):
|
|
180
|
+
"""
|
|
181
|
+
Return the module name and qualname for filterable.obj.
|
|
182
|
+
|
|
183
|
+
If filterable.obj is an operator.attrgetter that we've determined is associated with a property,
|
|
184
|
+
compute the names from the fqname of the filterable. If it's anything else, try to get its
|
|
185
|
+
__module__ and __qualname__, falling back to default values if they're not available.
|
|
186
|
+
"""
|
|
187
|
+
fn = filterable.obj
|
|
188
|
+
assert callable(fn), f"{filterable} doesn't have a callable obj"
|
|
189
|
+
|
|
190
|
+
if (
|
|
191
|
+
type(fn) is operator.attrgetter
|
|
192
|
+
and (parts := filterable.fqname.split("."))
|
|
193
|
+
and len(parts) > 2
|
|
194
|
+
):
|
|
195
|
+
# filterable.fqname was set when we identified this filterable as a property
|
|
196
|
+
modname = ".".join(parts[:-2])
|
|
197
|
+
qualname = ".".join(parts[-2:])
|
|
198
|
+
else:
|
|
199
|
+
modname = getattr(fn, "__module__", "unknown")
|
|
200
|
+
qualname = getattr(fn, "__qualname__", None)
|
|
201
|
+
if qualname is None:
|
|
202
|
+
qualname = getattr(fn.__class__, "__name__", "unknown")
|
|
203
|
+
return modname, qualname
|
|
177
204
|
|
|
178
205
|
class CallEvent(Event):
|
|
179
206
|
# pylint: disable=method-cache-max-size-none
|
|
180
207
|
__slots__ = ["_fn", "_fqfn", "static", "receiver", "parameters", "labels", "auxtype"]
|
|
181
208
|
|
|
182
209
|
@staticmethod
|
|
183
|
-
def make(
|
|
210
|
+
def make(filterable):
|
|
184
211
|
"""
|
|
185
212
|
Return a factory for creating new CallEvents based on
|
|
186
213
|
introspecting the given function.
|
|
187
214
|
"""
|
|
188
215
|
|
|
189
216
|
# Delete the labels so the app doesn't see them.
|
|
217
|
+
fn = filterable.obj
|
|
190
218
|
labels = getattr(fn, "_appmap_labels", None)
|
|
191
219
|
if labels:
|
|
192
220
|
del fn._appmap_labels
|
|
193
221
|
|
|
194
|
-
return partial(CallEvent,
|
|
222
|
+
return partial(CallEvent, filterable, labels=labels)
|
|
195
223
|
|
|
196
224
|
@staticmethod
|
|
197
225
|
def make_params(filterable):
|
|
@@ -312,10 +340,14 @@ class CallEvent(Event):
|
|
|
312
340
|
comment = inspect.getcomments(self._fn)
|
|
313
341
|
return comment
|
|
314
342
|
|
|
315
|
-
def __init__(self,
|
|
343
|
+
def __init__(self, filterable, parameters, labels):
|
|
316
344
|
super().__init__("call")
|
|
345
|
+
fn = filterable.obj
|
|
317
346
|
self._fn = fn
|
|
318
|
-
|
|
347
|
+
modname, qualname = _get_name_parts(filterable)
|
|
348
|
+
self._fqfn = FqFnName(modname, qualname)
|
|
349
|
+
|
|
350
|
+
fntype = filterable.fntype
|
|
319
351
|
self.static = fntype in FnType.STATIC | FnType.CLASS | FnType.MODULE
|
|
320
352
|
self.receiver = None
|
|
321
353
|
if fntype in FnType.CLASS | FnType.INSTANCE:
|
|
@@ -5,7 +5,7 @@ import types
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from collections import namedtuple
|
|
7
7
|
from collections.abc import MutableSequence
|
|
8
|
-
from functools import reduce
|
|
8
|
+
from functools import partial, reduce
|
|
9
9
|
|
|
10
10
|
from _appmap import wrapt
|
|
11
11
|
|
|
@@ -134,7 +134,7 @@ def get_members(cls):
|
|
|
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
136
|
# descriptor protocol.
|
|
137
|
-
if Importer.instrument_properties and type(static_value) is property:
|
|
137
|
+
if Importer.instrument_properties and type(static_value) is property:
|
|
138
138
|
properties[key] = (
|
|
139
139
|
static_value,
|
|
140
140
|
{
|
|
@@ -147,7 +147,7 @@ def get_members(cls):
|
|
|
147
147
|
if not is_member_func(static_value):
|
|
148
148
|
continue
|
|
149
149
|
value = getattr(cls, key)
|
|
150
|
-
if value
|
|
150
|
+
if (m := getattr(value, "__module__", None)) and (m is None or m != modname):
|
|
151
151
|
continue
|
|
152
152
|
functions.append((key, static_value, value))
|
|
153
153
|
|
|
@@ -202,7 +202,7 @@ class Importer:
|
|
|
202
202
|
logger.trace(" functions %s", functions)
|
|
203
203
|
|
|
204
204
|
for fn_name, static_fn, fn in functions:
|
|
205
|
-
filterableFn = FilterableFn(filterable,
|
|
205
|
+
filterableFn = FilterableFn(filterable, fn_name, fn, static_fn)
|
|
206
206
|
new_fn = cls.instrument_function(fn_name, filterableFn, selected_functions)
|
|
207
207
|
if new_fn != fn:
|
|
208
208
|
fw = wrapt.wrap_function_wrapper(filterable.obj, fn_name, new_fn)
|
|
@@ -221,7 +221,8 @@ class Importer:
|
|
|
221
221
|
new_fn = cls.instrument_function(prop_name, filterableFn, selected_functions)
|
|
222
222
|
if new_fn != fn:
|
|
223
223
|
new_fn = wrapt.FunctionWrapper(fn, new_fn)
|
|
224
|
-
# Set _appmap_instrumented on the FunctionWrapper, not on the wrapped
|
|
224
|
+
# Set _appmap_instrumented on the FunctionWrapper, not on the wrapped
|
|
225
|
+
# function.
|
|
225
226
|
new_fn._appmap_instrumented = True # pylint: disable=protected-access
|
|
226
227
|
|
|
227
228
|
instrumented_fns[k] = new_fn
|
|
@@ -121,7 +121,7 @@ def instrument(filterable):
|
|
|
121
121
|
logger.debug("hooking %s", filterable.fqname)
|
|
122
122
|
fn = filterable.obj
|
|
123
123
|
|
|
124
|
-
make_call_event = event.CallEvent.make(
|
|
124
|
+
make_call_event = event.CallEvent.make(filterable)
|
|
125
125
|
params = CallEvent.make_params(filterable)
|
|
126
126
|
|
|
127
127
|
# django depends on being able to find the cache_clear attribute
|
|
@@ -114,55 +114,6 @@ class ExampleClass(Super, ClassMethodMixin):
|
|
|
114
114
|
def return_self(self):
|
|
115
115
|
return self
|
|
116
116
|
|
|
117
|
-
def __init__(self):
|
|
118
|
-
self._read_only = "read only"
|
|
119
|
-
self._fully_accessible = "fully accessible"
|
|
120
|
-
self._undecorated = "undecorated"
|
|
121
|
-
|
|
122
|
-
@property
|
|
123
|
-
def read_only(self):
|
|
124
|
-
"""Read-only"""
|
|
125
|
-
return self._read_only
|
|
126
|
-
|
|
127
|
-
@property
|
|
128
|
-
def fully_accessible(self):
|
|
129
|
-
"""Fully-accessible"""
|
|
130
|
-
return self._fully_accessible
|
|
131
|
-
|
|
132
|
-
@fully_accessible.setter
|
|
133
|
-
def fully_accessible(self, v):
|
|
134
|
-
self._fully_accessible = v
|
|
135
|
-
|
|
136
|
-
@fully_accessible.deleter
|
|
137
|
-
def fully_accessible(self):
|
|
138
|
-
del self._fully_accessible
|
|
139
|
-
|
|
140
|
-
def get_undecorated(self):
|
|
141
|
-
return self._undecorated
|
|
142
|
-
|
|
143
|
-
def set_undecorated(self, value):
|
|
144
|
-
self._undecorated = value
|
|
145
|
-
|
|
146
|
-
def delete_undecorated(self):
|
|
147
|
-
del self._undecorated
|
|
148
|
-
|
|
149
|
-
undecorated_property = property(get_undecorated, set_undecorated, delete_undecorated)
|
|
150
|
-
|
|
151
|
-
def set_write_only(self, v):
|
|
152
|
-
self._write_only = v
|
|
153
|
-
|
|
154
|
-
def del_write_only(self):
|
|
155
|
-
del self._write_only
|
|
156
|
-
|
|
157
|
-
write_only = property(None, set_write_only, del_write_only, "Write-only")
|
|
158
|
-
|
|
159
|
-
def raise_base_exception(self) -> NoReturn:
|
|
160
|
-
raise BaseException("not derived from Exception") # pylint: disable=broad-exception-raised
|
|
161
117
|
|
|
162
118
|
def modfunc():
|
|
163
119
|
return "Hello world!"
|
|
164
|
-
|
|
165
|
-
if __name__ == "__main__":
|
|
166
|
-
ec = ExampleClass()
|
|
167
|
-
ec.fully_accessible = "updated"
|
|
168
|
-
print(ec.fully_accessible)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from functools import cached_property, partial
|
|
2
|
+
import operator
|
|
3
|
+
from typing import NoReturn
|
|
4
|
+
|
|
5
|
+
def free_read_only(self):
|
|
6
|
+
return self._read_only
|
|
7
|
+
|
|
8
|
+
def free_func():
|
|
9
|
+
return "hello world"
|
|
10
|
+
class PropertiesClass:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self._read_only = "read only"
|
|
13
|
+
self._fully_accessible = "fully accessible"
|
|
14
|
+
self._undecorated = "undecorated"
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def read_only(self):
|
|
18
|
+
"""Read-only"""
|
|
19
|
+
return self._read_only
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def fully_accessible(self):
|
|
23
|
+
"""Fully-accessible"""
|
|
24
|
+
return self._fully_accessible
|
|
25
|
+
|
|
26
|
+
@fully_accessible.setter
|
|
27
|
+
def fully_accessible(self, v):
|
|
28
|
+
self._fully_accessible = v
|
|
29
|
+
|
|
30
|
+
@fully_accessible.deleter
|
|
31
|
+
def fully_accessible(self):
|
|
32
|
+
del self._fully_accessible
|
|
33
|
+
|
|
34
|
+
def get_undecorated(self):
|
|
35
|
+
return self._undecorated
|
|
36
|
+
|
|
37
|
+
def set_undecorated(self, value):
|
|
38
|
+
self._undecorated = value
|
|
39
|
+
|
|
40
|
+
def delete_undecorated(self):
|
|
41
|
+
del self._undecorated
|
|
42
|
+
|
|
43
|
+
undecorated_property = property(get_undecorated, set_undecorated, delete_undecorated)
|
|
44
|
+
|
|
45
|
+
def set_write_only(self, v):
|
|
46
|
+
self._write_only = v
|
|
47
|
+
|
|
48
|
+
def del_write_only(self):
|
|
49
|
+
del self._write_only
|
|
50
|
+
|
|
51
|
+
write_only = property(None, set_write_only, del_write_only, "Write-only")
|
|
52
|
+
|
|
53
|
+
def raise_base_exception(self) -> NoReturn:
|
|
54
|
+
raise BaseException("not derived from Exception") # pylint: disable=broad-exception-raised
|
|
55
|
+
|
|
56
|
+
@cached_property
|
|
57
|
+
def cached_read_only(self):
|
|
58
|
+
return self._read_only
|
|
59
|
+
|
|
60
|
+
operator_read_only = property(operator.attrgetter("cached_read_only"))
|
|
61
|
+
|
|
62
|
+
tastes = {"bacon": "yum"}
|
|
63
|
+
|
|
64
|
+
def __getitem__(self, key):
|
|
65
|
+
return self.tastes[key]
|
|
66
|
+
|
|
67
|
+
taste = property(operator.itemgetter("bacon"))
|
|
68
|
+
|
|
69
|
+
free_read_only_prop = property(free_read_only)
|
|
70
|
+
|
|
71
|
+
static_partial_method = staticmethod(partial(free_func))
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Tests for methods decorated with @property"""
|
|
2
|
+
|
|
3
|
+
# pyright: reportMissingImports=false
|
|
4
|
+
# pylint: disable=import-error,import-outside-toplevel
|
|
5
|
+
import pytest
|
|
6
|
+
from _appmap.test.helpers import DictIncluding
|
|
7
|
+
|
|
8
|
+
pytestmark = pytest.mark.appmap_enabled
|
|
9
|
+
|
|
10
|
+
@pytest.fixture(autouse=True)
|
|
11
|
+
def setup(with_data_dir): # pylint: disable=unused-argument
|
|
12
|
+
# with_data_dir sets up sys.path so properties_class can be imported
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_getter_instrumented(events):
|
|
17
|
+
from properties_class import PropertiesClass
|
|
18
|
+
|
|
19
|
+
ec = PropertiesClass()
|
|
20
|
+
|
|
21
|
+
actual = PropertiesClass.read_only.__doc__
|
|
22
|
+
assert actual == "Read-only"
|
|
23
|
+
|
|
24
|
+
assert ec.read_only == "read only"
|
|
25
|
+
|
|
26
|
+
with pytest.raises(AttributeError, match=r".*(has no setter|can't set attribute).*"):
|
|
27
|
+
ec.read_only = "not allowed"
|
|
28
|
+
|
|
29
|
+
with pytest.raises(AttributeError, match=r".*(has no deleter|can't delete attribute).*"):
|
|
30
|
+
del ec.read_only
|
|
31
|
+
|
|
32
|
+
assert len(events) == 2
|
|
33
|
+
assert events[0].to_dict() == DictIncluding({
|
|
34
|
+
"event": "call",
|
|
35
|
+
"defined_class": "properties_class.PropertiesClass",
|
|
36
|
+
"method_id": "read_only (get)",
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_accessible_instrumented(events):
|
|
41
|
+
from properties_class import PropertiesClass
|
|
42
|
+
|
|
43
|
+
ec = PropertiesClass()
|
|
44
|
+
assert PropertiesClass.fully_accessible.__doc__ == "Fully-accessible"
|
|
45
|
+
|
|
46
|
+
assert ec.fully_accessible == "fully accessible"
|
|
47
|
+
|
|
48
|
+
ec.fully_accessible = "updated"
|
|
49
|
+
# Check the value of the attribute directly, to avoid extra events
|
|
50
|
+
assert ec._fully_accessible == "updated" # pylint: disable=protected-access
|
|
51
|
+
|
|
52
|
+
del ec.fully_accessible
|
|
53
|
+
|
|
54
|
+
assert len(events) == 6
|
|
55
|
+
assert events[0].to_dict() == DictIncluding({
|
|
56
|
+
"event": "call",
|
|
57
|
+
"defined_class": "properties_class.PropertiesClass",
|
|
58
|
+
"method_id": "fully_accessible (get)",
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
assert events[2].to_dict() == DictIncluding({
|
|
62
|
+
"event": "call",
|
|
63
|
+
"defined_class": "properties_class.PropertiesClass",
|
|
64
|
+
"method_id": "fully_accessible (set)",
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
assert events[4].to_dict() == DictIncluding({
|
|
68
|
+
"event": "call",
|
|
69
|
+
"defined_class": "properties_class.PropertiesClass",
|
|
70
|
+
"method_id": "fully_accessible (del)",
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_writable_instrumented(events):
|
|
75
|
+
from properties_class import PropertiesClass
|
|
76
|
+
|
|
77
|
+
ec = PropertiesClass()
|
|
78
|
+
assert PropertiesClass.write_only.__doc__ == "Write-only"
|
|
79
|
+
|
|
80
|
+
with pytest.raises(AttributeError, match=r".*(has no getter|unreadable attribute).*"):
|
|
81
|
+
_ = ec.write_only
|
|
82
|
+
|
|
83
|
+
ec.write_only = "updated example"
|
|
84
|
+
|
|
85
|
+
assert len(events) == 2
|
|
86
|
+
assert events[0].to_dict() == DictIncluding({
|
|
87
|
+
"event": "call",
|
|
88
|
+
"defined_class": "properties_class.PropertiesClass",
|
|
89
|
+
"method_id": "set_write_only (set)",
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_operator_attrgetter(events):
|
|
94
|
+
from properties_class import PropertiesClass
|
|
95
|
+
|
|
96
|
+
ec = PropertiesClass()
|
|
97
|
+
|
|
98
|
+
assert ec.operator_read_only == "read only"
|
|
99
|
+
|
|
100
|
+
with pytest.raises(AttributeError, match=r".*(has no setter|can't set attribute).*"):
|
|
101
|
+
ec.operator_read_only = "not allowed"
|
|
102
|
+
|
|
103
|
+
with pytest.raises(AttributeError, match=r".*(has no deleter|can't delete attribute).*"):
|
|
104
|
+
del ec.operator_read_only
|
|
105
|
+
|
|
106
|
+
assert len(events) == 2
|
|
107
|
+
assert events[0].to_dict() == DictIncluding({
|
|
108
|
+
"event": "call",
|
|
109
|
+
"defined_class": "properties_class.PropertiesClass",
|
|
110
|
+
"method_id": "operator_read_only (get)",
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
def test_operator_itemgetter(events):
|
|
114
|
+
from properties_class import PropertiesClass
|
|
115
|
+
|
|
116
|
+
ec = PropertiesClass()
|
|
117
|
+
assert ec.taste == "yum"
|
|
118
|
+
assert len(events) == 2
|
|
119
|
+
assert events[0].to_dict() == DictIncluding({
|
|
120
|
+
"event": "call",
|
|
121
|
+
# operator.itemgetter.__module__ isn't available before 3.10
|
|
122
|
+
# "defined_class": "operator",
|
|
123
|
+
"method_id": "itemgetter (get)",
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_free_function(events):
|
|
128
|
+
from properties_class import PropertiesClass
|
|
129
|
+
|
|
130
|
+
ec = PropertiesClass()
|
|
131
|
+
assert ec.free_read_only_prop == "read only"
|
|
132
|
+
assert len(events) == 2
|
|
133
|
+
assert events[0].to_dict() == DictIncluding({
|
|
134
|
+
"event": "call",
|
|
135
|
+
"defined_class": "properties_class",
|
|
136
|
+
"method_id": "free_read_only (get)",
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@pytest.mark.xfail(
|
|
141
|
+
raises=AssertionError,
|
|
142
|
+
reason="needs fix for https://github.com/getappmap/appmap-python/issues/365",
|
|
143
|
+
)
|
|
144
|
+
def test_functools_partial(events):
|
|
145
|
+
from properties_class import PropertiesClass
|
|
146
|
+
|
|
147
|
+
PropertiesClass.static_partial_method()
|
|
148
|
+
assert len(events) > 0
|
|
@@ -8,7 +8,6 @@ from contextlib import contextmanager
|
|
|
8
8
|
from contextvars import ContextVar
|
|
9
9
|
from enum import Enum, IntFlag, auto
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Any, Callable
|
|
12
11
|
|
|
13
12
|
from .env import Env
|
|
14
13
|
|
|
@@ -79,10 +78,8 @@ class FqFnName:
|
|
|
79
78
|
FqFnName makes it easy to reference the parts of the fully-qualified name of a callable.
|
|
80
79
|
"""
|
|
81
80
|
|
|
82
|
-
def __init__(self,
|
|
83
|
-
|
|
84
|
-
self._modname = fn.__module__
|
|
85
|
-
qualname = fn.__qualname__
|
|
81
|
+
def __init__(self, modname, qualname):
|
|
82
|
+
self._modname = modname
|
|
86
83
|
if "." in qualname:
|
|
87
84
|
self._scope = Scope.CLASS
|
|
88
85
|
self._class_name, self._fn_name = qualname.rsplit(".", 1)
|
|
@@ -112,8 +109,6 @@ class FqFnName:
|
|
|
112
109
|
def fn_name(self):
|
|
113
110
|
return self._fn_name
|
|
114
111
|
|
|
115
|
-
FqFnName(fqname)
|
|
116
|
-
|
|
117
112
|
def root_relative_path(path):
|
|
118
113
|
"""Returns the path relative to the current root_dir.
|
|
119
114
|
|
|
@@ -31,7 +31,7 @@ def _add_api_route(wrapped, _, args, kwargs):
|
|
|
31
31
|
|
|
32
32
|
fn = args[1]
|
|
33
33
|
|
|
34
|
-
fqn = utils.FqFnName(fn)
|
|
34
|
+
fqn = utils.FqFnName(fn.__module__, fn.__qualname__)
|
|
35
35
|
scope = Filterable(fqn.scope, fqn.fqclass, None)
|
|
36
36
|
|
|
37
37
|
filterable_fn = FilterableFn(scope, fn.__name__, fn, fn)
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"""Tests for methods decorated with @property"""
|
|
2
|
-
|
|
3
|
-
# pyright: reportMissingImports=false
|
|
4
|
-
# pylint: disable=import-error,import-outside-toplevel
|
|
5
|
-
import pytest
|
|
6
|
-
from _appmap.test.helpers import DictIncluding
|
|
7
|
-
|
|
8
|
-
pytestmark = [
|
|
9
|
-
pytest.mark.appmap_enabled,
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@pytest.fixture(autouse=True)
|
|
14
|
-
def setup(with_data_dir): # pylint: disable=unused-argument
|
|
15
|
-
# with_data_dir sets up sys.path so example_class can be imported
|
|
16
|
-
pass
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def test_getter_instrumented(events):
|
|
20
|
-
from example_class import ExampleClass
|
|
21
|
-
|
|
22
|
-
ec = ExampleClass()
|
|
23
|
-
|
|
24
|
-
actual = ExampleClass.read_only.__doc__
|
|
25
|
-
assert actual == "Read-only"
|
|
26
|
-
|
|
27
|
-
assert ec.read_only == "read only"
|
|
28
|
-
|
|
29
|
-
with pytest.raises(AttributeError, match=r".*(has no setter|can't set attribute).*"):
|
|
30
|
-
# E AttributeError: can't set attribute
|
|
31
|
-
|
|
32
|
-
ec.read_only = "not allowed"
|
|
33
|
-
|
|
34
|
-
with pytest.raises(AttributeError, match=r".*(has no deleter|can't delete attribute).*"):
|
|
35
|
-
del ec.read_only
|
|
36
|
-
|
|
37
|
-
assert len(events) == 2
|
|
38
|
-
assert events[0].to_dict() == DictIncluding(
|
|
39
|
-
{
|
|
40
|
-
"event": "call",
|
|
41
|
-
"defined_class": "example_class.ExampleClass",
|
|
42
|
-
"method_id": "read_only (get)",
|
|
43
|
-
}
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def test_accessible_instrumented(events):
|
|
48
|
-
from example_class import ExampleClass
|
|
49
|
-
|
|
50
|
-
ec = ExampleClass()
|
|
51
|
-
assert ExampleClass.fully_accessible.__doc__ == "Fully-accessible"
|
|
52
|
-
|
|
53
|
-
assert ec.fully_accessible == "fully accessible"
|
|
54
|
-
|
|
55
|
-
ec.fully_accessible = "updated"
|
|
56
|
-
# Check the value of the attribute directly, to avoid extra events
|
|
57
|
-
assert ec._fully_accessible == "updated" # pylint: disable=protected-access
|
|
58
|
-
|
|
59
|
-
del ec.fully_accessible
|
|
60
|
-
|
|
61
|
-
# assert len(events) == 6
|
|
62
|
-
assert events[0].to_dict() == DictIncluding(
|
|
63
|
-
{
|
|
64
|
-
"event": "call",
|
|
65
|
-
"defined_class": "example_class.ExampleClass",
|
|
66
|
-
"method_id": "fully_accessible (get)",
|
|
67
|
-
}
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
assert events[2].to_dict() == DictIncluding(
|
|
71
|
-
{
|
|
72
|
-
"event": "call",
|
|
73
|
-
"defined_class": "example_class.ExampleClass",
|
|
74
|
-
"method_id": "fully_accessible (set)",
|
|
75
|
-
}
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
assert events[4].to_dict() == DictIncluding(
|
|
79
|
-
{
|
|
80
|
-
"event": "call",
|
|
81
|
-
"defined_class": "example_class.ExampleClass",
|
|
82
|
-
"method_id": "fully_accessible (del)",
|
|
83
|
-
}
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def test_writable_instrumented(events):
|
|
88
|
-
from example_class import ExampleClass
|
|
89
|
-
|
|
90
|
-
ec = ExampleClass()
|
|
91
|
-
assert ExampleClass.write_only.__doc__ == "Write-only"
|
|
92
|
-
|
|
93
|
-
with pytest.raises(AttributeError, match=r".*(has no getter|unreadable attribute).*"):
|
|
94
|
-
_ = ec.write_only
|
|
95
|
-
|
|
96
|
-
ec.write_only = "updated example"
|
|
97
|
-
|
|
98
|
-
assert len(events) == 2
|
|
99
|
-
assert events[0].to_dict() == DictIncluding(
|
|
100
|
-
{
|
|
101
|
-
"event": "call",
|
|
102
|
-
"defined_class": "example_class.ExampleClass",
|
|
103
|
-
"method_id": "set_write_only (set)",
|
|
104
|
-
}
|
|
105
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|