appmap 2.1.5__tar.gz → 2.1.6__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.
Files changed (183) hide show
  1. {appmap-2.1.5 → appmap-2.1.6}/PKG-INFO +1 -1
  2. {appmap-2.1.5 → appmap-2.1.6}/_appmap/event.py +0 -1
  3. {appmap-2.1.5 → appmap-2.1.6}/_appmap/importer.py +1 -1
  4. {appmap-2.1.5 → appmap-2.1.6}/_appmap/instrument.py +0 -1
  5. {appmap-2.1.5 → appmap-2.1.6}/_appmap/recording.py +1 -0
  6. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/djangoapp/settings.py +13 -0
  7. appmap-2.1.6/_appmap/test/data/django/test/test_request.py +8 -0
  8. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_django.py +3 -3
  9. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_properties.py +1 -1
  10. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_recording.py +38 -23
  11. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_test_frameworks.py +14 -0
  12. {appmap-2.1.5 → appmap-2.1.6}/appmap/pytest.py +8 -1
  13. {appmap-2.1.5 → appmap-2.1.6}/pyproject.toml +1 -1
  14. {appmap-2.1.5 → appmap-2.1.6}/vendor/_appmap/wrapt/wrappers.py +40 -21
  15. {appmap-2.1.5 → appmap-2.1.6}/LICENSE +0 -0
  16. {appmap-2.1.5 → appmap-2.1.6}/README.md +0 -0
  17. {appmap-2.1.5 → appmap-2.1.6}/_appmap/__init__.py +0 -0
  18. {appmap-2.1.5 → appmap-2.1.6}/_appmap/configuration.py +0 -0
  19. {appmap-2.1.5 → appmap-2.1.6}/_appmap/django.py +0 -0
  20. {appmap-2.1.5 → appmap-2.1.6}/_appmap/env.py +0 -0
  21. {appmap-2.1.5 → appmap-2.1.6}/_appmap/fastapi.py +0 -0
  22. {appmap-2.1.5 → appmap-2.1.6}/_appmap/flask.py +0 -0
  23. {appmap-2.1.5 → appmap-2.1.6}/_appmap/generation.py +0 -0
  24. {appmap-2.1.5 → appmap-2.1.6}/_appmap/labels.py +0 -0
  25. {appmap-2.1.5 → appmap-2.1.6}/_appmap/metadata.py +0 -0
  26. {appmap-2.1.5 → appmap-2.1.6}/_appmap/noappmap.py +0 -0
  27. {appmap-2.1.5 → appmap-2.1.6}/_appmap/py_version_check.py +0 -0
  28. {appmap-2.1.5 → appmap-2.1.6}/_appmap/recorder.py +0 -0
  29. {appmap-2.1.5 → appmap-2.1.6}/_appmap/remote_recording.py +0 -0
  30. {appmap-2.1.5 → appmap-2.1.6}/_appmap/singleton.py +0 -0
  31. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/__init__.py +0 -0
  32. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/appmap_test_base.py +0 -0
  33. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/bin/server_runner +0 -0
  34. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/conftest.py +0 -0
  35. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap-all-paths-malformed.yml +0 -0
  36. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap-broken.yml +0 -0
  37. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap-class.yml +0 -0
  38. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap-empty-path.yml +0 -0
  39. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap-exclude-fn.yml +0 -0
  40. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap-func.yml +0 -0
  41. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap-malformed-path.yml +0 -0
  42. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap-no-pyyaml.yml +0 -0
  43. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap.yml +0 -0
  44. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap_testing/__init__.py +0 -0
  45. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/appmap_testing/django_simplelazyobject.py +0 -0
  46. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config/src/package/__init__.py +0 -0
  47. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config/test/__init__.py +0 -0
  48. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config/test/foo.py +0 -0
  49. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config-exclude/.hide/hidden_mod/__init__.py +0 -0
  50. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config-exclude/node_modules/node_mod/__init__.py +0 -0
  51. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config-exclude/src/package/__init__.py +0 -0
  52. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config-exclude/test/__init__.py +0 -0
  53. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config-exclude/venv/venv_mod/__init__.py +0 -0
  54. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config-up/appmap.yml +0 -0
  55. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config-up/project/p1/__init__.py +0 -0
  56. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/config-up/project/p2/sub1/__init__.py +0 -0
  57. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/appmap.yml +0 -0
  58. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/djangoapp/__init__.py +0 -0
  59. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/djangoapp/djangoapp.py +0 -0
  60. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/djangoapp/hello_world.html +0 -0
  61. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/djangoapp/middleware.py +0 -0
  62. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/djangoapp/settings_dev.py +0 -0
  63. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/djangoapp/urls.py +0 -0
  64. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/init/sitecustomize.py +0 -0
  65. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/manage.py +0 -0
  66. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/pytest.ini +0 -0
  67. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/test/__init__.py +0 -0
  68. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/test/test_app.py +0 -0
  69. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/django/test/test_unittest_setup.py +0 -0
  70. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/example_class.py +0 -0
  71. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/expected.appmap.json +0 -0
  72. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/fastapi/appmap.yml +0 -0
  73. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/fastapi/fastapiapp/__init__.py +0 -0
  74. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/fastapi/fastapiapp/main.py +0 -0
  75. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/fastapi/init/sitecustomize.py +0 -0
  76. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/fastapi/test_app.py +0 -0
  77. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/flask/appmap.yml +0 -0
  78. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/flask/flaskapp.py +0 -0
  79. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/flask/init/sitecustomize.py +0 -0
  80. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/flask/templates/test.html +0 -0
  81. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/flask/test_app.py +0 -0
  82. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/flask-instrumented/appmap.yml +0 -0
  83. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/flask-instrumented/flaskapp.py +0 -0
  84. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/flask-instrumented/init/sitecustomize.py +0 -0
  85. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/flask-instrumented/test_app.py +0 -0
  86. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/package1/__init__.py +0 -0
  87. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/package1/package2/__init__.py +0 -0
  88. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/package1/package2/__main__.py +0 -0
  89. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/package1/package2/mod1.py +0 -0
  90. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/params.py +0 -0
  91. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/properties_class.py +0 -0
  92. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/appmap-no-test-cases.yml +0 -0
  93. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/appmap.yml +0 -0
  94. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/expected/pytest-numpy1-no-test-cases.appmap.json +0 -0
  95. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/expected/pytest-numpy1.appmap.json +0 -0
  96. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/expected/pytest-numpy2-no-test-cases.appmap.json +0 -0
  97. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/expected/pytest-numpy2.appmap.json +0 -0
  98. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/expected/status_errored.metadata.json +0 -0
  99. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/expected/status_failed.metadata.json +0 -0
  100. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/expected/status_xfailed.metadata.json +0 -0
  101. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/simple.py +0 -0
  102. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/tests/__init__.py +0 -0
  103. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/tests/test_noappmap.py +0 -0
  104. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest/tests/test_simple.py +0 -0
  105. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest-instrumented/appmap.yml +0 -0
  106. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest-instrumented/init/sitecustomize.py +0 -0
  107. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/pytest-instrumented/test_instrumented.py +0 -0
  108. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/remote.appmap.json +0 -0
  109. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/trial/.gitignore +0 -0
  110. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/trial/appmap-no-test-cases.yml +0 -0
  111. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/trial/appmap.yml +0 -0
  112. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/trial/expected/pytest-no-test-cases.appmap.json +0 -0
  113. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/trial/expected/pytest.appmap.json +0 -0
  114. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/trial/init/sitecustomize.py +0 -0
  115. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/trial/test/__init__.py +0 -0
  116. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/trial/test/test_deferred.py +0 -0
  117. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/appmap-no-test-cases.yml +0 -0
  118. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/appmap.yml +0 -0
  119. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/expected/pytest.appmap.json +0 -0
  120. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/expected/status_errored.metadata.json +0 -0
  121. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/expected/status_failed.metadata.json +0 -0
  122. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/expected/status_xfailed.metadata.json +0 -0
  123. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/expected/status_xsucceeded.metadata.json +0 -0
  124. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/expected/unittest-no-test-cases.appmap.json +0 -0
  125. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/expected/unittest.appmap.json +0 -0
  126. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/init/sitecustomize.py +0 -0
  127. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/simple/__init__.py +0 -0
  128. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/simple/test_noappmap.py +0 -0
  129. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/data/unittest/simple/test_simple.py +0 -0
  130. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/helpers.py +0 -0
  131. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/normalize.py +0 -0
  132. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_command.py +0 -0
  133. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_configuration.py +0 -0
  134. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_describe_value.py +0 -0
  135. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_django_simplelazyobject.py +0 -0
  136. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_env.py +0 -0
  137. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_events.py +0 -0
  138. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_fastapi.py +0 -0
  139. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_flask.py +0 -0
  140. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_generation.py +0 -0
  141. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_http.py +0 -0
  142. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_importer.py +0 -0
  143. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_labels.py +0 -0
  144. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_metadata.py +0 -0
  145. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_params.py +0 -0
  146. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_runner.py +0 -0
  147. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_sqlalchemy.py +0 -0
  148. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/test_util.py +0 -0
  149. {appmap-2.1.5 → appmap-2.1.6}/_appmap/test/web_framework.py +0 -0
  150. {appmap-2.1.5 → appmap-2.1.6}/_appmap/testing_framework.py +0 -0
  151. {appmap-2.1.5 → appmap-2.1.6}/_appmap/trace_logger.py +0 -0
  152. {appmap-2.1.5 → appmap-2.1.6}/_appmap/unittest.py +0 -0
  153. {appmap-2.1.5 → appmap-2.1.6}/_appmap/utils.py +0 -0
  154. {appmap-2.1.5 → appmap-2.1.6}/_appmap/web_framework.py +0 -0
  155. {appmap-2.1.5 → appmap-2.1.6}/appmap/__init__.py +0 -0
  156. {appmap-2.1.5 → appmap-2.1.6}/appmap/command/__init__.py +0 -0
  157. {appmap-2.1.5 → appmap-2.1.6}/appmap/command/appmap_agent_init.py +0 -0
  158. {appmap-2.1.5 → appmap-2.1.6}/appmap/command/appmap_agent_status.py +0 -0
  159. {appmap-2.1.5 → appmap-2.1.6}/appmap/command/appmap_agent_validate.py +0 -0
  160. {appmap-2.1.5 → appmap-2.1.6}/appmap/command/runner.py +0 -0
  161. {appmap-2.1.5 → appmap-2.1.6}/appmap/django.py +0 -0
  162. {appmap-2.1.5 → appmap-2.1.6}/appmap/fastapi.py +0 -0
  163. {appmap-2.1.5 → appmap-2.1.6}/appmap/flask.py +0 -0
  164. {appmap-2.1.5 → appmap-2.1.6}/appmap/http.py +0 -0
  165. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/__init__.py +0 -0
  166. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/crypto.yml +0 -0
  167. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/django.yml +0 -0
  168. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/flask.yml +0 -0
  169. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/formats.yml +0 -0
  170. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/http.yml +0 -0
  171. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/jobs.yml +0 -0
  172. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/jwt.yml +0 -0
  173. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/logger.yml +0 -0
  174. {appmap-2.1.5 → appmap-2.1.6}/appmap/labeling/random.yml +0 -0
  175. {appmap-2.1.5 → appmap-2.1.6}/appmap/sqlalchemy.py +0 -0
  176. {appmap-2.1.5 → appmap-2.1.6}/appmap/unittest.py +0 -0
  177. {appmap-2.1.5 → appmap-2.1.6}/appmap/uvicorn.py +0 -0
  178. {appmap-2.1.5 → appmap-2.1.6}/appmap.pth +0 -0
  179. {appmap-2.1.5 → appmap-2.1.6}/vendor/_appmap/wrapt/LICENSE +0 -0
  180. {appmap-2.1.5 → appmap-2.1.6}/vendor/_appmap/wrapt/__init__.py +0 -0
  181. {appmap-2.1.5 → appmap-2.1.6}/vendor/_appmap/wrapt/arguments.py +0 -0
  182. {appmap-2.1.5 → appmap-2.1.6}/vendor/_appmap/wrapt/decorators.py +0 -0
  183. {appmap-2.1.5 → appmap-2.1.6}/vendor/_appmap/wrapt/importer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: appmap
3
- Version: 2.1.5
3
+ Version: 2.1.6
4
4
  Summary: Create AppMap files by recording a Python application.
5
5
  Home-page: https://github.com/applandinc/appmap-python
6
6
  License: MIT
@@ -6,7 +6,6 @@ import threading
6
6
  from functools import lru_cache, partial
7
7
  from inspect import Parameter, Signature
8
8
  from itertools import chain
9
- import types
10
9
 
11
10
  from .env import Env
12
11
  from .recorder import Recorder
@@ -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 partial, reduce
8
+ from functools import reduce
9
9
 
10
10
  from _appmap import wrapt
11
11
 
@@ -119,7 +119,6 @@ def call_instrumented(f, instance, args, kwargs):
119
119
  def instrument(filterable):
120
120
  """return an instrumented function"""
121
121
  logger.debug("hooking %s", filterable.fqname)
122
- fn = filterable.obj
123
122
 
124
123
  make_call_event = event.CallEvent.make(filterable)
125
124
  params = CallEvent.make_params(filterable)
@@ -62,6 +62,7 @@ class NoopRecording:
62
62
 
63
63
  def __init__(self, exit_hook=None):
64
64
  self.exit_hook = exit_hook
65
+ self.events = []
65
66
 
66
67
  def start(self):
67
68
  pass
@@ -1,3 +1,9 @@
1
+ from pathlib import Path
2
+
3
+
4
+ # Build paths inside the project like this: BASE_DIR / 'subdir'.
5
+ BASE_DIR = Path(__file__).resolve().parent.parent
6
+
1
7
  # If the SECRET_KEY isn't defined we get the misleading error message
2
8
  # CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.
3
9
  SECRET_KEY = "3*+d^_kjnr2gz)4q2m(&&^%$p4fj5dk3%lz4pl3g4m-%6!gf&)"
@@ -10,3 +16,10 @@ ROOT_URLCONF = "djangoapp.urls"
10
16
 
11
17
  # Turn off deprecation warning
12
18
  USE_TZ = True
19
+
20
+ DATABASES = {
21
+ "default": {
22
+ "ENGINE": "django.db.backends.sqlite3",
23
+ "NAME": BASE_DIR / "db.sqlite3",
24
+ }
25
+ }
@@ -0,0 +1,8 @@
1
+ from django.test import TestCase
2
+ from django.test import Client
3
+
4
+
5
+ class TestRequest(TestCase):
6
+ def test_request_test(self):
7
+ resp = self.client.get("/test")
8
+ assert resp.status_code == 200
@@ -198,7 +198,7 @@ class TestDjangoApp:
198
198
  # To really check middleware reset, the tests must run in order,
199
199
  # so disable randomly.
200
200
  result = pytester.runpytest("-svv", "-p", "no:randomly")
201
- result.assert_outcomes(passed=5, failed=0, errors=0)
201
+ result.assert_outcomes(passed=6, failed=0, errors=0)
202
202
  # Look for the http_server_request event in test_app's appmap. If
203
203
  # middleware reset is broken, it won't be there.
204
204
  appmap_file = pytester.path / "tmp" / "appmap" / "pytest" / "test_request.appmap.json"
@@ -212,7 +212,7 @@ class TestDjangoApp:
212
212
  def test_disabled(self, pytester, monkeypatch):
213
213
  monkeypatch.setenv("_APPMAP", "false")
214
214
  result = pytester.runpytest("-svv", "-p", "no:randomly", "-k", "test_request")
215
- result.assert_outcomes(passed=2, failed=0, errors=0)
215
+ result.assert_outcomes(passed=3, failed=0, errors=0)
216
216
  assert not (pytester.path / "tmp").exists()
217
217
 
218
218
  def test_disabled_for_process(self, pytester, monkeypatch):
@@ -222,7 +222,7 @@ class TestDjangoApp:
222
222
 
223
223
  # There are two tests for remote recording. They should both fail,
224
224
  # because process recording should disable remote recording.
225
- result.assert_outcomes(passed=3, failed=2, errors=0)
225
+ result.assert_outcomes(passed=4, failed=2, errors=0)
226
226
 
227
227
  assert (pytester.path / "tmp" / "appmap" / "process").exists()
228
228
  assert not (pytester.path / "tmp" / "appmap" / "requests").exists()
@@ -145,4 +145,4 @@ def test_functools_partial(events):
145
145
  from properties_class import PropertiesClass
146
146
 
147
147
  PropertiesClass.static_partial_method()
148
- assert len(events) > 0
148
+ assert len(events) > 0
@@ -9,6 +9,7 @@ from threading import Thread
9
9
  import pytest
10
10
 
11
11
  import appmap
12
+ from _appmap.configuration import Config
12
13
  from _appmap.event import Event
13
14
  from _appmap.recorder import Recorder, ThreadRecorder
14
15
  from _appmap.wrapt import FunctionWrapper
@@ -16,6 +17,17 @@ from _appmap.wrapt import FunctionWrapper
16
17
  from .normalize import normalize_appmap, remove_line_numbers
17
18
 
18
19
 
20
+ def _call_modfunc(q):
21
+ r = appmap.Recording()
22
+ with r:
23
+ f = q.get()
24
+ f()
25
+ events = r.events
26
+ assert len(events) == 2
27
+ assert events[0].event == "call"
28
+ assert events[0].method_id == "modfunc"
29
+
30
+
19
31
  @pytest.mark.appmap_enabled
20
32
  @pytest.mark.usefixtures("with_data_dir")
21
33
  class TestRecordingWhenEnabled:
@@ -47,11 +59,12 @@ class TestRecordingWhenEnabled:
47
59
  ), f"expected path {expected_path}"
48
60
 
49
61
  def test_recording_clears(self):
50
- from example_class import ( # pyright: ignore[reportMissingImports] pylint: disable=import-error
51
- ExampleClass,
52
- )
62
+ # pylint: disable=import-error
63
+ from example_class import ExampleClass # pyright: ignore[reportMissingImports]
64
+ # pylint: enable=import-error
53
65
 
54
- with appmap.Recording():
66
+ rec = appmap.Recording()
67
+ with rec:
55
68
  ExampleClass.static_method()
56
69
 
57
70
  # fresh recording shouldn't contain previous traces
@@ -68,9 +81,9 @@ class TestRecordingWhenEnabled:
68
81
  assert rec.events[2].method_id == "instance_method"
69
82
 
70
83
  def test_recording_shallow(self):
71
- from example_class import ( # pyright: ignore[reportMissingImports] pylint: disable=import-error
72
- ExampleClass,
73
- )
84
+ # pylint: disable=import-error
85
+ from example_class import ExampleClass # pyright: ignore[reportMissingImports]
86
+ # pylint: enable=import-error
74
87
 
75
88
  rec = appmap.Recording()
76
89
  with rec:
@@ -82,9 +95,9 @@ class TestRecordingWhenEnabled:
82
95
  assert len(rec.events) == 8
83
96
 
84
97
  def test_recording_wrapped(self):
85
- from example_class import ( # pyright: ignore[reportMissingImports] pylint: disable=import-error
86
- ExampleClass,
87
- )
98
+ # pylint: disable=import-error
99
+ from example_class import ExampleClass # pyright: ignore[reportMissingImports]
100
+ # pylint: enable=import-error
88
101
 
89
102
  rec = appmap.Recording()
90
103
  with rec:
@@ -117,21 +130,23 @@ class TestRecordingWhenEnabled:
117
130
  f1 = deepcopy(modfunc)
118
131
  f1()
119
132
 
120
- def test_can_pickle(self):
121
- import pickle
133
+ def test_can_pickle(self, monkeypatch):
134
+ # Make sure subprocesses see whatever config is set for us.
135
+ monkeypatch.setenv("APPMAP_CONFIG", str(Config.current._file)) # pylint: disable=protected-access
122
136
 
123
- from example_class import ( # pyright: ignore[reportMissingImports] pylint: disable=import-error
124
- modfunc,
125
- )
137
+ from multiprocessing import Process, Queue
126
138
 
127
- rec = appmap.Recording()
128
- with rec:
129
- assert isinstance(modfunc, FunctionWrapper)
130
- f = pickle.loads(pickle.dumps(modfunc))
131
- f()
132
- evt = rec.events[-2]
133
- assert evt.event == "call"
134
- assert evt.method_id == "modfunc"
139
+ # pylint: disable=import-error
140
+ from example_class import modfunc # pyright: ignore[reportMissingImports]
141
+ # pylint: enable=import-error
142
+
143
+ assert isinstance(modfunc, FunctionWrapper), "modfunc isn't instrumented?"
144
+
145
+ q = Queue()
146
+ q.put(modfunc)
147
+ p = Process(target=_call_modfunc, args=(q,))
148
+ p.start()
149
+ p.join()
135
150
 
136
151
 
137
152
  @pytest.mark.appmap_enabled
@@ -122,6 +122,20 @@ class TestPytestRunnerPytest(_TestTestRunner):
122
122
  verify_expected_appmap(testdir, f"-numpy{numpy_version.major}-no-test-cases")
123
123
  verify_expected_metadata(testdir)
124
124
 
125
+ @pytest.mark.example_dir("django")
126
+ def test_pytest_django(testdir):
127
+ result = testdir.runpytest("-svv", "-k", "test_request_test")
128
+ result.assert_outcomes(passed=1)
129
+ # django.test.TestCase is a subclass of unittest.TestCase, so recorder type is unittest
130
+ assert (
131
+ testdir.path
132
+ / "tmp"
133
+ / "appmap"
134
+ / "unittest"
135
+ / "test_test_request_TestRequest_test_request_test.appmap.json"
136
+ ).exists()
137
+ assert not (testdir.path / "tmp" / "appmap" / "requests").exists()
138
+
125
139
 
126
140
  @pytest.mark.example_dir("trial")
127
141
  class TestPytestRunnerTrial(_TestTestRunner):
@@ -1,6 +1,13 @@
1
1
  from importlib.metadata import version
2
2
 
3
3
  import pytest
4
+ try:
5
+ from pytest_django.django_compat import is_django_unittest
6
+ except ImportError:
7
+
8
+ def is_django_unittest(item):
9
+ return False
10
+
4
11
 
5
12
  from _appmap import noappmap, testing_framework, wrapt
6
13
  from _appmap.env import Env
@@ -51,7 +58,7 @@ if not Env.current.is_appmap_repo and Env.current.enables("tests"):
51
58
  # running the test case. (This nesting of function calls is
52
59
  # verified by the expected appmap in the test for a unittest
53
60
  # TestCase run by pytest.)
54
- if hasattr(item, "_testcase"):
61
+ if hasattr(item, "_testcase") and not is_django_unittest(item):
55
62
  setattr(
56
63
  item._testcase, # pylint: disable=protected-access
57
64
  "_appmap_pytest_recording",
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "appmap"
3
- version = "2.1.5"
3
+ version = "2.1.6"
4
4
  description = "Create AppMap files by recording a Python application."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -457,11 +457,8 @@ class ObjectProxy(with_metaclass(_ObjectProxyMetaType)):
457
457
  raise NotImplementedError(
458
458
  'object proxy must define __reduce_ex__()')
459
459
 
460
- # Return the qualname of the wrapped function instead of a tuple. This allows an instance of
461
- # subclasses to be pickled as the function it wraps. This seems to be adequate for generating
462
- # AppMaps.
463
- def __reduce_ex__(self, protocol):
464
- return self.__wrapped__.__qualname__
460
+ def __reduce_ex__(self):
461
+ raise NotImplementedError("object proxy must define __reduce_ex__()")
465
462
 
466
463
  class CallableObjectProxy(ObjectProxy):
467
464
 
@@ -508,6 +505,20 @@ class PartialCallableObjectProxy(ObjectProxy):
508
505
 
509
506
  return self.__wrapped__(*_args, **_kwargs)
510
507
 
508
+ def _unpickle_functionwrapper(modname, qualname):
509
+ """
510
+ Given the module name and qualname of a function, return a FunctionWrapper instance for it. This
511
+ simply imports the module, then fetches the appropriate function. Provided AppMap
512
+ instrumentation has been configured correctly when unpickling, the attribute for the function
513
+ will be a FunctionWrapper. If it hasn't been configured correctly, the attribute will simply be
514
+ the original function. (This means the application will function correctly, but no events will
515
+ get generated when the function is called.)
516
+ """
517
+ _, _, original = resolve_path(modname, qualname)
518
+
519
+ return original
520
+
521
+
511
522
  class _FunctionWrapperBase(ObjectProxy):
512
523
  __slots__ = (
513
524
  "_self_instance",
@@ -656,6 +667,20 @@ class _FunctionWrapperBase(ObjectProxy):
656
667
  else:
657
668
  return issubclass(subclass, self.__wrapped__)
658
669
 
670
+ # Implement this here, rather than in ObjectProxy, because _unpickle_functionwrapper will only
671
+ # create new instances of subclasses of _FunctionWrapperBase via the agent's import hooks.
672
+ def __reduce_ex__(self, _):
673
+ modname = self.__wrapped__.__module__
674
+ qualname = getattr(self.__wrapped__, "__qualname__", None)
675
+ if qualname is None:
676
+ qualname = self.__wrapped__.__name__
677
+
678
+ return (
679
+ _unpickle_functionwrapper,
680
+ (modname, qualname),
681
+ )
682
+
683
+
659
684
  class BoundFunctionWrapper(_FunctionWrapperBase):
660
685
 
661
686
  def __new__(cls, *args, **kwargs):
@@ -738,24 +763,18 @@ class BoundFunctionWrapper(_FunctionWrapperBase):
738
763
  return self._self_wrapper(self.__wrapped__, instance, args,
739
764
  kwargs)
740
765
 
741
- class FunctionWrapper(_FunctionWrapperBase):
766
+ def __getattribute__(self, name):
767
+ if name == "__func__":
768
+ # The __func__ attribute of a bound method is the unbound method. The corresponding
769
+ # attribute of a BoundFunctionWrapper is the associated FunctionWrapper (saved in
770
+ # _self_parent when the BFW is created).
771
+ return self._self_parent
742
772
 
743
- __bound_function_wrapper__ = BoundFunctionWrapper
773
+ return super().__getattribute__(name)
744
774
 
745
- # The code here is pretty complicated (see the comment below), and it's not completely clear to
746
- # me whether it actually keeps any state. If it does, __reduce_ex__ needs to return a tuple so a
747
- # new FunctionWrapper will be created. If it doesn't, then __reduce_ex__ can simply return a
748
- # string, which would cause deepcopy to return the original FunctionWrapper.
749
- #
750
-
751
- # def __reduce_ex__(self, protocol):
752
- # return self.__wrapped__.__qualname__
753
-
754
- # return FunctionWrapper, (
755
- # self.__wrapped__,
756
- # self._self_wrapper,
757
- # self._self_enabled,
758
- # )
775
+
776
+ class FunctionWrapper(_FunctionWrapperBase):
777
+ __bound_function_wrapper__ = BoundFunctionWrapper
759
778
 
760
779
  def __init__(self, wrapped, wrapper, enabled=None):
761
780
  # What it is we are wrapping here could be anything. We need to
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes