dt-extensions-sdk 1.1.2__tar.gz → 1.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.
Files changed (79) hide show
  1. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/PKG-INFO +1 -1
  2. dt_extensions_sdk-1.1.4/docs/_static/img/migrate-01-new-extension.png +0 -0
  3. dt_extensions_sdk-1.1.4/docs/_static/img/migrate-02-type.png +0 -0
  4. dt_extensions_sdk-1.1.4/docs/_static/img/migrate-03-import.png +0 -0
  5. dt_extensions_sdk-1.1.4/docs/_static/img/migrate-04-import-remote.png +0 -0
  6. dt_extensions_sdk-1.1.4/docs/_static/img/migrate-05-activation.png +0 -0
  7. dt_extensions_sdk-1.1.4/docs/_static/img/migrate-06-activation-config.png +0 -0
  8. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/guides/extension_structure.rst +1 -1
  9. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/guides/installation.rst +1 -1
  10. dt_extensions_sdk-1.1.4/docs/guides/migration.rst +159 -0
  11. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/index.rst +1 -0
  12. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/__about__.py +1 -1
  13. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/communication.py +8 -5
  14. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/extension.py +8 -1
  15. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/pyproject.toml +3 -0
  16. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/sdk/test_extension.py +4 -7
  17. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/.github/workflows/gh-pages-docs.yml +0 -0
  18. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/.github/workflows/publish.yml +0 -0
  19. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/.gitignore +0 -0
  20. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/LICENSE.txt +0 -0
  21. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/README.md +0 -0
  22. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/_static/dt-sdk-header.png +0 -0
  23. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/_static/dt-sdk-logo.png +0 -0
  24. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/_static/favicon.ico +0 -0
  25. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/api/events/event_severity.rst +0 -0
  26. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/api/events/event_type.rst +0 -0
  27. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/api/events/index.rst +0 -0
  28. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/api/extension.rst +0 -0
  29. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/api/metrics/index.rst +0 -0
  30. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/api/metrics/metric.rst +0 -0
  31. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/api/metrics/metric_type.rst +0 -0
  32. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/cli/assemble.rst +0 -0
  33. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/cli/build.rst +0 -0
  34. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/cli/create.rst +0 -0
  35. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/cli/gencerts.rst +0 -0
  36. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/cli/help.rst +0 -0
  37. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/cli/run.rst +0 -0
  38. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/cli/sign.rst +0 -0
  39. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/cli/upload.rst +0 -0
  40. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/cli/wheel.rst +0 -0
  41. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/conf.py +0 -0
  42. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/docs/requirements.txt +0 -0
  43. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/__init__.py +0 -0
  44. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/__init__.py +0 -0
  45. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/__init__.py +0 -0
  46. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/create.py +0 -0
  47. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/extension_template/.gitignore.template +0 -0
  48. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/extension_template/README.md.template +0 -0
  49. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/extension_template/activation.json.template +0 -0
  50. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/extension_template/extension/activationSchema.json.template +0 -0
  51. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/extension_template/extension/extension.yaml.template +0 -0
  52. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/extension_template/extension_name/__init__.py.template +0 -0
  53. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/extension_template/extension_name/__main__.py.template +0 -0
  54. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/create/extension_template/setup.py.template +0 -0
  55. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/main.py +0 -0
  56. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/cli/schema.py +0 -0
  57. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/__init__.py +0 -0
  58. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/activation.py +0 -0
  59. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/callback.py +0 -0
  60. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/event.py +0 -0
  61. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/helper.py +0 -0
  62. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/metric.py +0 -0
  63. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/runtime.py +0 -0
  64. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/vendor/__init__.py +0 -0
  65. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/vendor/mureq/LICENSE +0 -0
  66. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/vendor/mureq/__init__.py +0 -0
  67. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/dynatrace_extension/sdk/vendor/mureq/mureq.py +0 -0
  68. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/__init__.py +0 -0
  69. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/cli/__init__.py +0 -0
  70. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/cli/test_dt_sdk.py +0 -0
  71. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/cli/test_templates.py +0 -0
  72. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/cli/test_types.py +0 -0
  73. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/sdk/__init__.py +0 -0
  74. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/sdk/test_activation.py +0 -0
  75. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/sdk/test_callback.py +0 -0
  76. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/sdk/test_communication.py +0 -0
  77. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/sdk/test_metric.py +0 -0
  78. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/sdk/test_runtime_properties.py +0 -0
  79. {dt_extensions_sdk-1.1.2 → dt_extensions_sdk-1.1.4}/tests/sdk/test_status.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dt-extensions-sdk
3
- Version: 1.1.2
3
+ Version: 1.1.4
4
4
  Project-URL: Documentation, https://github.com/dynatrace-extensions/dt-extensions-python-sdk#readme
5
5
  Project-URL: Issues, https://github.com/dynatrace-extensions/dt-extensions-python-sdk/issues
6
6
  Project-URL: Source, https://github.com/dynatrace-extensions/dt-extensions-python-sdk
@@ -1,7 +1,7 @@
1
1
  Extension Structure
2
2
  ===================
3
3
 
4
- The basic barebone structure of an extension is as follows:
4
+ The basic bare-bone structure of an extension is as follows:
5
5
 
6
6
  .. code:: bash
7
7
 
@@ -9,7 +9,7 @@ Requirements
9
9
  ``dt-extensions-sdk`` requires only the following dependencies to be
10
10
  present in the environment.
11
11
 
12
- - Python >= 3.7, < 4.0
12
+ - Python >= 3.10
13
13
 
14
14
  Installing from PyPI
15
15
  ^^^^^^^^^^^^^^^^^^^^
@@ -0,0 +1,159 @@
1
+ Migrating EF1 Extensions
2
+ ########################
3
+
4
+ | Use this guide to understand how to migrate an existing Python EF1 extension to the new EF2 format.
5
+
6
+ Requirements
7
+ ============
8
+
9
+ * `Python 3.10 or later <https://www.python.org/downloads/>`_
10
+ * ``dt-extensions-sdk`` installed and in your ``PATH`` (you can run with ``dt-sdk --help``).
11
+ * `VSCode <https://code.visualstudio.com>`_ with the `Dynatrace Extensions <https://marketplace.visualstudio.com/items?itemName=DynatracePlatformExtensions.dynatrace-extensions>`_ extension installed.
12
+
13
+ Step by Step
14
+ ============
15
+
16
+
17
+ To migrate an existing python EF1 extension using VSCode, the steps are as follows:
18
+
19
+ 1. Create a new EF2 extension
20
+ 2. Import the EF1 extension using the ``Dynatrace extensions: Convert Python`` command
21
+ 3. Convert the code, moving the class from the original extension to the ``__main__.py`` file of the new extension
22
+
23
+
24
+ Create a new EF2 extension
25
+ """"""""""""""""""""""""""
26
+
27
+ 1. Open an empty folder in VSCode, then run the ``Dynatrace extensions: Initialize Workspace`` command
28
+ 2. Select the schema version (latest recommended), and the certificates you want to use to sign the extension
29
+
30
+ |
31
+
32
+ .. image:: ../_static/img/migrate-01-new-extension.png
33
+ :alt: Init workspace
34
+ |
35
+
36
+ 3. Chose the ``Python Extension 2.0`` project type
37
+ 4. Give your extension a name, it must respect the python module naming convention (all lower case with optional underscores).
38
+
39
+ |
40
+
41
+ .. image:: ../_static/img/migrate-02-type.png
42
+ :alt: Chose project type
43
+ |
44
+
45
+ Import the Python EF1 extension
46
+ """""""""""""""""""""""""""""""
47
+
48
+ | The first step is to convert the old ``plugin.json`` file to the new ``activationSchema.json`` format.
49
+ | This will automatically create the **Settings 2.0 UI** for your extension, which defines the UI for the user to configure the extension.
50
+ |
51
+ | Run the command ``Dynatrace extensions: Convert Python``.
52
+ | You can chose to import an existing python extension from:
53
+ |
54
+ * The extension zip file
55
+ * The plugin.json file
56
+ * From your Dynatrace environment
57
+
58
+ |
59
+
60
+ .. image:: ../_static/img/migrate-03-import.png
61
+ :alt: Import extension
62
+ |
63
+
64
+ | In this example we are importing from a Dynatrace environment, which gives you a list of all python extensions on that environment.
65
+ |
66
+ | If you would like to import from your computer, a file picker will open, and you can select the zip or plugin.json file of your extension.
67
+
68
+ |
69
+
70
+ .. image:: ../_static/img/migrate-04-import-remote.png
71
+ :alt: Import from dynatrace
72
+ |
73
+
74
+ | After you select an extension, your ``activationSchema.json`` will be overwritten with the correct settings.
75
+ |
76
+ | You should also review that file, to make sure your UI looks the way you want it to.
77
+ |
78
+
79
+ .. note::
80
+ | If you are importing a local (OneAgent) extension, delete the entry **activation > remote** from the extension.yaml file.
81
+ | If instead you are importing a remote (Activegate) extension, delete **activation > local**.
82
+ | You can also make your extension work both remotely and locally, by keeping both entries and modifying the **activationSchema.json** file accordingly.
83
+
84
+ Modify the extension code
85
+ """""""""""""""""""""""""
86
+
87
+ .. note::
88
+ | We are working on automating part of this step.
89
+
90
+
91
+ | Move your existing extension code to the ``__main__.py`` file of the new extension.
92
+ | The easiest way to accomplish this is by pasting the code from your existing extension class into the new ``ExtensionImpl`` class.
93
+ |
94
+ | Here are the most important changes you need to make to your code:
95
+ |
96
+
97
+
98
+ .. list-table:: Code conversion reference
99
+ :widths: 40 10 10 40
100
+ :header-rows: 1
101
+
102
+ * - Description
103
+ - EF1 method
104
+ - EF2 method
105
+ - Notes
106
+ * - Logging
107
+ - ``self.logger.info("message")``
108
+ - ``self.logger.info("message")``
109
+ - Stays the same
110
+ * - Obtaining user defined parameters
111
+ - ``self.config.get("param_name", "default_value")``
112
+ - ``self.activation_config.get("param_name", "default_value")``
113
+ - You can find and replace all ``self.config.`` entries with ``self.activation_config.``
114
+ * - Report an event
115
+ - ``self.results_builder.report_custom_info_event``
116
+ - ``self.report_dt_event``
117
+ - Try to keep topology (groups, device, IDs) out of the code, this is defined later in the ``extension.yaml`` file.
118
+ * - Report a metric
119
+ - ``device.absolute("metric_key", metric_value, {"dimension_name": "dimension_value"})``
120
+ - ``self.report_metric("metric_key", metric_value, {"dimension_name": "dimension_value"})``
121
+ - There is no concept of a ``device`` in the python code anymore, send metrics directly.
122
+ * - Create groups and custom devices
123
+ - ``self.topology_builder.create_group``, ``group.create_device``
124
+ - ``n/a``
125
+ - Doesn't exist, topology is defined in the ``extension.yaml`` file.
126
+
127
+
128
+
129
+ Build and upload the extension
130
+ """"""""""""""""""""""""""""""
131
+
132
+ | Build the extension by running the command ``Dynatrace extensions: Build``.
133
+ | If the build is successful, you will see a prompt to upload the extension to your Dynatrace environment and activate it.
134
+ | Accept both prompts
135
+ |
136
+ | Navigate to your Dynatrace environment, to ``Infrastructure Observability > Extensions`` and find your extension.
137
+
138
+ |
139
+ .. image:: ../_static/img/migrate-05-activation.png
140
+ :alt: Extension config
141
+ |
142
+
143
+ | Click ``Add monitoring configuration``
144
+ | Check that your UI looks the way you want it to, and fill in the parameters.
145
+
146
+ .. note::
147
+ | You can copy the ``value`` from the right side JSON Snippet and use it in your ``activation.json`` file to test your extension locall with ``dt-sdk run``
148
+ |
149
+ .. image:: ../_static/img/migrate-06-activation-config.png
150
+ :alt: Extension config
151
+ |
152
+
153
+
154
+ Limitations
155
+ """""""""""
156
+
157
+ * The ``process snapshot`` is not yet supported, but will be added in the future. As a workaround the file ``<dynatrace_log_path>/dynatrace/oneagent/plugin/oneagent_latest_snapshot.log`` can be read and parsed as JSON.
158
+ * The metric metadata and topology must be added to the ``extension.yaml`` file, part of this will be automated in the future.
159
+
@@ -273,6 +273,7 @@ Documentation
273
273
 
274
274
  guides/installation
275
275
  guides/extension_structure
276
+ guides/migration
276
277
 
277
278
  .. toctree::
278
279
  :caption: All Commands
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2023-present Dynatrace LLC
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "1.1.2"
4
+ __version__ = "1.1.4"
@@ -92,7 +92,7 @@ class CommunicationClient(ABC):
92
92
  pass
93
93
 
94
94
  @abstractmethod
95
- def send_events(self, event: dict | list[dict]) -> dict | None:
95
+ def send_events(self, event: dict | list[dict], eec_enrichment: bool) -> dict | None:
96
96
  pass
97
97
 
98
98
  @abstractmethod
@@ -275,13 +275,16 @@ class HttpClient(CommunicationClient):
275
275
  responses.append(mint_response)
276
276
  return responses
277
277
 
278
- def send_events(self, events: dict | list[dict]) -> dict | None:
278
+ def send_events(self, events: dict | list[dict], eec_enrichment: bool = True) -> dict | None:
279
279
  self.logger.debug(f"Sending log events: {events}")
280
280
  event_data = json.dumps(events).encode("utf-8")
281
281
  try:
282
282
  # EEC returns empty body on success
283
283
  return self._make_request(
284
- self._events_url, "POST", event_data, extra_headers={"Content-Type": CONTENT_TYPE_JSON}
284
+ self._events_url,
285
+ "POST",
286
+ event_data,
287
+ extra_headers={"Content-Type": CONTENT_TYPE_JSON, "eec-enrichment": str(eec_enrichment).lower()},
285
288
  ).json()
286
289
  except json.JSONDecodeError:
287
290
  return None
@@ -394,8 +397,8 @@ class DebugClient(CommunicationClient):
394
397
  responses = [MintResponse(lines_invalid=0, lines_ok=len(mint_lines), error=None, warnings=None)]
395
398
  return responses
396
399
 
397
- def send_events(self, events: dict | list[dict]) -> dict | None:
398
- self.logger.info(f"send_events: {events}")
400
+ def send_events(self, events: dict | list[dict], eec_enrichment: bool = True) -> dict | None:
401
+ self.logger.info(f"send_events (enrichment = {eec_enrichment}): {events}")
399
402
  return None
400
403
 
401
404
  def send_sfm_metrics(self, mint_lines: list[str]) -> MintResponse:
@@ -173,6 +173,9 @@ class Extension:
173
173
  self._task_id = "development_task_id"
174
174
  self._monitoring_config_id = "development_config_id"
175
175
 
176
+ # The user can override default EEC enrichment for logs
177
+ self.log_event_enrichment = True
178
+
176
179
  # The Communication client
177
180
  self._client: CommunicationClient = None # type: ignore
178
181
 
@@ -456,6 +459,10 @@ class Extension:
456
459
  if "dt.techrule.id" not in dimensions:
457
460
  dimensions["dt.techrule.id"] = techrule
458
461
 
462
+ if metric_type == MetricType.COUNT and timestamp is None:
463
+ # We must report a timestamp for count metrics
464
+ timestamp = datetime.now()
465
+
459
466
  metric = Metric(key=key, value=value, dimensions=dimensions, metric_type=metric_type, timestamp=timestamp)
460
467
  self._add_metric(metric)
461
468
 
@@ -984,7 +991,7 @@ class Extension:
984
991
  self._metrics.extend(lines)
985
992
 
986
993
  def _send_events_internal(self, events: Union[dict, List[dict]]):
987
- response = self._client.send_events(events)
994
+ response = self._client.send_events(events, self.log_event_enrichment)
988
995
  with self._internal_callbacks_results_lock:
989
996
  self._internal_callbacks_results[self._send_events.__name__] = Status(StatusValue.OK)
990
997
  if not response or "error" not in response or "message" not in response["error"]:
@@ -39,6 +39,9 @@ artifacts = [
39
39
  "dynatrace_extension/cli/create/extension_template/**/*",
40
40
  ]
41
41
 
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["dynatrace_extension"]
44
+
42
45
  [tool.hatch.envs.default]
43
46
  dependencies = [
44
47
  "coverage[toml]>=6.5",
@@ -4,6 +4,8 @@ import unittest
4
4
  from datetime import datetime, timedelta
5
5
  from unittest.mock import MagicMock, mock_open, patch
6
6
 
7
+ import pytest
8
+
7
9
  from dynatrace_extension import Status, StatusValue, get_helper_extension
8
10
  from dynatrace_extension.sdk.activation import ActivationConfig
9
11
  from dynatrace_extension.sdk.communication import HttpClient, MintResponse
@@ -681,13 +683,8 @@ class TestExtension(unittest.TestCase):
681
683
  def initialize(self):
682
684
  raise AttributeError
683
685
 
684
- ext = MyExtension()
685
- ext._running_in_sim = True
686
- ext._client = MagicMock()
687
- ext._heartbeat_iteration()
688
- current_status = ext._build_current_status()
689
- assert current_status.status == StatusValue.GENERIC_ERROR
690
- assert current_status.message == "Python datasource initialization error: AttributeError()"
686
+ with pytest.raises(AttributeError):
687
+ MyExtension()
691
688
 
692
689
  def test_report_mint_and_log_sending_failure(self):
693
690
  extension = get_helper_extension()