appmap 2.0.7__tar.gz → 2.0.9__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 (163) hide show
  1. {appmap-2.0.7 → appmap-2.0.9}/PKG-INFO +1 -1
  2. {appmap-2.0.7 → appmap-2.0.9}/_appmap/env.py +28 -1
  3. {appmap-2.0.7 → appmap-2.0.9}/_appmap/event.py +3 -1
  4. {appmap-2.0.7 → appmap-2.0.9}/_appmap/instrument.py +3 -2
  5. {appmap-2.0.7 → appmap-2.0.9}/_appmap/recorder.py +24 -2
  6. {appmap-2.0.7 → appmap-2.0.9}/pyproject.toml +1 -1
  7. {appmap-2.0.7 → appmap-2.0.9}/vendor/_appmap/wrapt/wrappers.py +6 -7
  8. {appmap-2.0.7 → appmap-2.0.9}/LICENSE +0 -0
  9. {appmap-2.0.7 → appmap-2.0.9}/README.md +0 -0
  10. {appmap-2.0.7 → appmap-2.0.9}/_appmap/__init__.py +0 -0
  11. {appmap-2.0.7 → appmap-2.0.9}/_appmap/configuration.py +0 -0
  12. {appmap-2.0.7 → appmap-2.0.9}/_appmap/django.py +0 -0
  13. {appmap-2.0.7 → appmap-2.0.9}/_appmap/fastapi.py +0 -0
  14. {appmap-2.0.7 → appmap-2.0.9}/_appmap/flask.py +0 -0
  15. {appmap-2.0.7 → appmap-2.0.9}/_appmap/generation.py +0 -0
  16. {appmap-2.0.7 → appmap-2.0.9}/_appmap/importer.py +0 -0
  17. {appmap-2.0.7 → appmap-2.0.9}/_appmap/labels.py +0 -0
  18. {appmap-2.0.7 → appmap-2.0.9}/_appmap/metadata.py +0 -0
  19. {appmap-2.0.7 → appmap-2.0.9}/_appmap/noappmap.py +0 -0
  20. {appmap-2.0.7 → appmap-2.0.9}/_appmap/py_version_check.py +0 -0
  21. {appmap-2.0.7 → appmap-2.0.9}/_appmap/recording.py +0 -0
  22. {appmap-2.0.7 → appmap-2.0.9}/_appmap/remote_recording.py +0 -0
  23. {appmap-2.0.7 → appmap-2.0.9}/_appmap/singleton.py +0 -0
  24. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/__init__.py +0 -0
  25. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/appmap_test_base.py +0 -0
  26. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/bin/server_runner +0 -0
  27. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/conftest.py +0 -0
  28. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap-all-paths-malformed.yml +0 -0
  29. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap-broken.yml +0 -0
  30. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap-class.yml +0 -0
  31. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap-empty-path.yml +0 -0
  32. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap-exclude-fn.yml +0 -0
  33. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap-func.yml +0 -0
  34. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap-malformed-path.yml +0 -0
  35. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap-no-pyyaml.yml +0 -0
  36. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap.yml +0 -0
  37. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap_testing/__init__.py +0 -0
  38. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/appmap_testing/django_simplelazyobject.py +0 -0
  39. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config/src/package/__init__.py +0 -0
  40. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config/test/__init__.py +0 -0
  41. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config/test/foo.py +0 -0
  42. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config-exclude/.hide/hidden_mod/__init__.py +0 -0
  43. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config-exclude/node_modules/node_mod/__init__.py +0 -0
  44. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config-exclude/src/package/__init__.py +0 -0
  45. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config-exclude/test/__init__.py +0 -0
  46. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config-exclude/venv/venv_mod/__init__.py +0 -0
  47. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config-up/appmap.yml +0 -0
  48. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config-up/project/p1/__init__.py +0 -0
  49. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/config-up/project/p2/sub1/__init__.py +0 -0
  50. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/appmap.yml +0 -0
  51. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/djangoapp/__init__.py +0 -0
  52. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/djangoapp/djangoapp.py +0 -0
  53. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/djangoapp/hello_world.html +0 -0
  54. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/djangoapp/middleware.py +0 -0
  55. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/djangoapp/settings.py +0 -0
  56. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/djangoapp/settings_dev.py +0 -0
  57. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/djangoapp/urls.py +0 -0
  58. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/init/sitecustomize.py +0 -0
  59. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/manage.py +0 -0
  60. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/pytest.ini +0 -0
  61. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/test/__init__.py +0 -0
  62. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/django/test/test_app.py +0 -0
  63. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/example_class.py +0 -0
  64. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/expected.appmap.json +0 -0
  65. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/fastapi/appmap.yml +0 -0
  66. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/fastapi/fastapiapp/__init__.py +0 -0
  67. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/fastapi/fastapiapp/main.py +0 -0
  68. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/fastapi/init/sitecustomize.py +0 -0
  69. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/fastapi/test_app.py +0 -0
  70. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/flask/appmap.yml +0 -0
  71. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/flask/flaskapp.py +0 -0
  72. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/flask/init/sitecustomize.py +0 -0
  73. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/flask/templates/test.html +0 -0
  74. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/flask/test_app.py +0 -0
  75. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/package1/__init__.py +0 -0
  76. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/package1/package2/__init__.py +0 -0
  77. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/package1/package2/__main__.py +0 -0
  78. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/package1/package2/mod1.py +0 -0
  79. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/params.py +0 -0
  80. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/pytest/appmap.yml +0 -0
  81. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/pytest/expected/pytest.appmap.json +0 -0
  82. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/pytest/expected/status_errored.metadata.json +0 -0
  83. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/pytest/expected/status_failed.metadata.json +0 -0
  84. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/pytest/expected/status_xfailed.metadata.json +0 -0
  85. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/pytest/simple.py +0 -0
  86. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/pytest/test_noappmap.py +0 -0
  87. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/pytest/test_simple.py +0 -0
  88. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/remote.appmap.json +0 -0
  89. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/trial/.gitignore +0 -0
  90. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/trial/appmap.yml +0 -0
  91. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/trial/expected/pytest.appmap.json +0 -0
  92. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/trial/init/sitecustomize.py +0 -0
  93. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/trial/test/__init__.py +0 -0
  94. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/trial/test/test_deferred.py +0 -0
  95. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/appmap.yml +0 -0
  96. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/expected/pytest.appmap.json +0 -0
  97. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/expected/status_errored.metadata.json +0 -0
  98. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/expected/status_failed.metadata.json +0 -0
  99. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/expected/status_xfailed.metadata.json +0 -0
  100. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/expected/status_xsucceeded.metadata.json +0 -0
  101. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/expected/unittest.appmap.json +0 -0
  102. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/init/sitecustomize.py +0 -0
  103. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/simple/__init__.py +0 -0
  104. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/simple/test_noappmap.py +0 -0
  105. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/data/unittest/simple/test_simple.py +0 -0
  106. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/helpers.py +0 -0
  107. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/normalize.py +0 -0
  108. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_command.py +0 -0
  109. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_configuration.py +0 -0
  110. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_describe_value.py +0 -0
  111. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_django.py +0 -0
  112. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_django_simplelazyobject.py +0 -0
  113. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_env.py +0 -0
  114. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_events.py +0 -0
  115. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_fastapi.py +0 -0
  116. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_flask.py +0 -0
  117. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_generation.py +0 -0
  118. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_http.py +0 -0
  119. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_importer.py +0 -0
  120. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_labels.py +0 -0
  121. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_metadata.py +0 -0
  122. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_params.py +0 -0
  123. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_recording.py +0 -0
  124. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_runner.py +0 -0
  125. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_sqlalchemy.py +0 -0
  126. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_test_frameworks.py +0 -0
  127. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/test_util.py +0 -0
  128. {appmap-2.0.7 → appmap-2.0.9}/_appmap/test/web_framework.py +0 -0
  129. {appmap-2.0.7 → appmap-2.0.9}/_appmap/testing_framework.py +0 -0
  130. {appmap-2.0.7 → appmap-2.0.9}/_appmap/trace_logger.py +0 -0
  131. {appmap-2.0.7 → appmap-2.0.9}/_appmap/unittest.py +0 -0
  132. {appmap-2.0.7 → appmap-2.0.9}/_appmap/utils.py +0 -0
  133. {appmap-2.0.7 → appmap-2.0.9}/_appmap/web_framework.py +0 -0
  134. {appmap-2.0.7 → appmap-2.0.9}/appmap/__init__.py +0 -0
  135. {appmap-2.0.7 → appmap-2.0.9}/appmap/command/__init__.py +0 -0
  136. {appmap-2.0.7 → appmap-2.0.9}/appmap/command/appmap_agent_init.py +0 -0
  137. {appmap-2.0.7 → appmap-2.0.9}/appmap/command/appmap_agent_status.py +0 -0
  138. {appmap-2.0.7 → appmap-2.0.9}/appmap/command/appmap_agent_validate.py +0 -0
  139. {appmap-2.0.7 → appmap-2.0.9}/appmap/command/runner.py +0 -0
  140. {appmap-2.0.7 → appmap-2.0.9}/appmap/django.py +0 -0
  141. {appmap-2.0.7 → appmap-2.0.9}/appmap/fastapi.py +0 -0
  142. {appmap-2.0.7 → appmap-2.0.9}/appmap/flask.py +0 -0
  143. {appmap-2.0.7 → appmap-2.0.9}/appmap/http.py +0 -0
  144. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/__init__.py +0 -0
  145. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/crypto.yml +0 -0
  146. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/django.yml +0 -0
  147. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/flask.yml +0 -0
  148. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/formats.yml +0 -0
  149. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/http.yml +0 -0
  150. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/jobs.yml +0 -0
  151. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/jwt.yml +0 -0
  152. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/logger.yml +0 -0
  153. {appmap-2.0.7 → appmap-2.0.9}/appmap/labeling/random.yml +0 -0
  154. {appmap-2.0.7 → appmap-2.0.9}/appmap/pytest.py +0 -0
  155. {appmap-2.0.7 → appmap-2.0.9}/appmap/sqlalchemy.py +0 -0
  156. {appmap-2.0.7 → appmap-2.0.9}/appmap/unittest.py +0 -0
  157. {appmap-2.0.7 → appmap-2.0.9}/appmap/uvicorn.py +0 -0
  158. {appmap-2.0.7 → appmap-2.0.9}/appmap.pth +0 -0
  159. {appmap-2.0.7 → appmap-2.0.9}/vendor/_appmap/wrapt/LICENSE +0 -0
  160. {appmap-2.0.7 → appmap-2.0.9}/vendor/_appmap/wrapt/__init__.py +0 -0
  161. {appmap-2.0.7 → appmap-2.0.9}/vendor/_appmap/wrapt/arguments.py +0 -0
  162. {appmap-2.0.7 → appmap-2.0.9}/vendor/_appmap/wrapt/decorators.py +0 -0
  163. {appmap-2.0.7 → appmap-2.0.9}/vendor/_appmap/wrapt/importer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: appmap
3
- Version: 2.0.7
3
+ Version: 2.0.9
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
@@ -37,6 +37,7 @@ class Env(metaclass=SingletonMeta):
37
37
  else:
38
38
  self._env.pop(k, None)
39
39
 
40
+ self.log_file_creation_failed = False
40
41
  self._configure_logging()
41
42
  enabled = self._env.get("_APPMAP", "false")
42
43
  self._enabled = enabled is None or enabled.lower() != "false"
@@ -133,6 +134,21 @@ class Env(metaclass=SingletonMeta):
133
134
  def getLogger(self, name) -> trace_logger.TraceLogger:
134
135
  return cast(trace_logger.TraceLogger, logging.getLogger(name))
135
136
 
137
+ def determine_log_file(self):
138
+ log_file = "appmap.log"
139
+
140
+ # Try creating the log file in the current directory
141
+ try:
142
+ with open(log_file, 'a', encoding='UTF8'):
143
+ pass
144
+ except IOError:
145
+ # The circumstances in which creation is going to fail
146
+ # are also those in which the user doesn't care whether
147
+ # there's a log file (e.g. when starting a REPL).
148
+ return None
149
+ return log_file
150
+
151
+
136
152
  def _configure_logging(self):
137
153
  trace_logger.install()
138
154
 
@@ -179,13 +195,19 @@ class Env(metaclass=SingletonMeta):
179
195
  log_level = self.get("APPMAP_LOG_LEVEL", "info").upper()
180
196
  loggers = config_dict["loggers"]
181
197
  loggers["appmap"]["level"] = loggers["_appmap"]["level"] = log_level
198
+
199
+ log_file = self.determine_log_file()
200
+ # Use NullHandler if log_file is None to avoid complicating the configuration
201
+ # with the absence of the "default" handler.
182
202
  config_dict["handlers"] = {
183
203
  "default": {
184
204
  "class": "logging.handlers.RotatingFileHandler",
185
205
  "formatter": "default",
186
- "filename": "appmap.log",
206
+ "filename": log_file,
187
207
  "maxBytes": 50 * 1024 * 1024,
188
208
  "backupCount": 1,
209
+ } if log_file is not None else {
210
+ "class": "logging.NullHandler"
189
211
  },
190
212
  "stderr": {
191
213
  "class": "logging.StreamHandler",
@@ -194,6 +216,7 @@ class Env(metaclass=SingletonMeta):
194
216
  "stream": "ext://sys.stderr",
195
217
  },
196
218
  }
219
+ self.log_file_creation_failed = log_file is None
197
220
 
198
221
  if log_config is not None:
199
222
  name, level = log_config.split("=", 2)
@@ -213,3 +236,7 @@ def initialize(**kwargs):
213
236
  Env.reset(**kwargs)
214
237
  logger = logging.getLogger(__name__)
215
238
  logger.info("appmap enabled: %s", Env.current.enabled)
239
+ if Env.current.log_file_creation_failed:
240
+ # Writing to stderr makes the REPL fail in vscode-python.
241
+ # https://github.com/microsoft/vscode-python/blob/c71c85ebf3749d5fac76899feefb21ee321a4b5b/src/client/common/process/rawProcessApis.ts#L268-L269
242
+ logger.info("appmap.log cannot be created")
@@ -111,7 +111,9 @@ def describe_value(name, val, max_depth=5):
111
111
  "object_id": id(val),
112
112
  "value": display_string(val),
113
113
  }
114
- ret.update(_describe_schema(name, val, 0, max_depth))
114
+ if Env.current.display_params:
115
+ ret.update(_describe_schema(name, val, 0, max_depth))
116
+
115
117
  if any(_is_list_or_dict(type(val))):
116
118
  ret["size"] = len(val)
117
119
 
@@ -6,7 +6,7 @@ from contextlib import contextmanager
6
6
  from . import event
7
7
  from .env import Env
8
8
  from .event import CallEvent
9
- from .recorder import Recorder, AppMapTooManyEvents
9
+ from .recorder import Recorder, AppMapLimitExceeded
10
10
  from .utils import appmap_tls
11
11
 
12
12
  logger = Env.current.getLogger(__name__)
@@ -90,6 +90,7 @@ def call_instrumented(f, instance, args, kwargs):
90
90
  call_event_id = call_event.id
91
91
  start_time = time.time()
92
92
  try:
93
+ Recorder.check_time(start_time)
93
94
  ret = f.fn(*args, **kwargs)
94
95
  elapsed_time = time.time() - start_time
95
96
 
@@ -98,7 +99,7 @@ def call_instrumented(f, instance, args, kwargs):
98
99
  )
99
100
  Recorder.add_event(return_event)
100
101
  return ret
101
- except AppMapTooManyEvents:
102
+ except AppMapLimitExceeded:
102
103
  raise
103
104
  except Exception: # noqa: E722
104
105
  elapsed_time = time.time() - start_time
@@ -1,4 +1,5 @@
1
1
  import threading
2
+ import time
2
3
  import traceback
3
4
  from abc import ABC, abstractmethod
4
5
 
@@ -17,11 +18,23 @@ _MAX_EVENTS = Env.current.get("APPMAP_MAX_EVENTS")
17
18
  if _MAX_EVENTS is not None:
18
19
  _MAX_EVENTS = int(_MAX_EVENTS)
19
20
 
21
+ _MAX_TIME = Env.current.get("APPMAP_MAX_TIME")
22
+ if _MAX_TIME is not None:
23
+ _MAX_TIME = int(_MAX_TIME)
20
24
 
21
- class AppMapTooManyEvents(RuntimeError):
25
+
26
+ class AppMapLimitExceeded(RuntimeError):
27
+ """Class of events thrown when some limit has been exceeded"""
28
+
29
+
30
+ class AppMapTooManyEvents(AppMapLimitExceeded):
22
31
  """Thrown when a recorder has more than APPMAP_MAX_EVENTS"""
23
32
 
24
33
 
34
+ class AppMapSessionTooLong(AppMapLimitExceeded):
35
+ """Throw when an individual recording session has exceeded APPMAP_MAX_TIME"""
36
+
37
+
25
38
  class Recorder(ABC):
26
39
  """
27
40
  A base class for Recorders.
@@ -97,6 +110,13 @@ class Recorder(ABC):
97
110
  def stop_recording(cls):
98
111
  return cls.get_current()._stop_recording() # pylint: disable=protected-access
99
112
 
113
+ @classmethod
114
+ def check_time(cls, event_time):
115
+ if _MAX_TIME is None:
116
+ return
117
+ if event_time - cls.get_current()._start_time > _MAX_TIME:
118
+ raise AppMapSessionTooLong(f"Session exceeded {_MAX_TIME} seconds")
119
+
100
120
  @classmethod
101
121
  def add_event(cls, event):
102
122
  """
@@ -124,6 +144,7 @@ class Recorder(ABC):
124
144
  self._events = []
125
145
  self._enabled = enabled
126
146
  self.start_tb = None
147
+ self._start_time = None
127
148
 
128
149
  @abstractmethod
129
150
  def _start_recording(self):
@@ -134,6 +155,7 @@ class Recorder(ABC):
134
155
  raise RuntimeError("Recording already in progress")
135
156
  self.start_tb = traceback.extract_stack()
136
157
  self._enabled = True
158
+ self._start_time = time.time()
137
159
 
138
160
  @abstractmethod
139
161
  def _stop_recording(self):
@@ -150,7 +172,7 @@ class Recorder(ABC):
150
172
  self._events.append(event)
151
173
  if _MAX_EVENTS is not None and len(self._events) > _MAX_EVENTS:
152
174
  Recorder._aborting = True
153
- raise AppMapTooManyEvents()
175
+ raise AppMapTooManyEvents(f"Session exceeded {_MAX_EVENTS} events")
154
176
 
155
177
  @staticmethod
156
178
  def _initialize():
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "appmap"
3
- version = "2.0.7"
3
+ version = "2.0.9"
4
4
  description = "Create AppMap files by recording a Python application."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -457,9 +457,11 @@ 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.
460
463
  def __reduce_ex__(self, protocol):
461
- raise NotImplementedError(
462
- 'object proxy must define __reduce_ex__()')
464
+ return self.__wrapped__.__qualname__
463
465
 
464
466
  class CallableObjectProxy(ObjectProxy):
465
467
 
@@ -740,12 +742,9 @@ class FunctionWrapper(_FunctionWrapperBase):
740
742
  # new FunctionWrapper will be created. If it doesn't, then __reduce_ex__ can simply return a
741
743
  # string, which would cause deepcopy to return the original FunctionWrapper.
742
744
  #
743
- # Update: We'll return the qualname of the wrapped function instead of a tuple allows a
744
- # FunctionWrapper to be pickled (as the function it wraps). This seems to be adequate for
745
- # generating AppMaps, so go with that.
746
745
 
747
- def __reduce_ex__(self, protocol):
748
- return self.__wrapped__.__qualname__
746
+ # def __reduce_ex__(self, protocol):
747
+ # return self.__wrapped__.__qualname__
749
748
 
750
749
  # return FunctionWrapper, (
751
750
  # self.__wrapped__,
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
File without changes