cognite-extractor-utils 7.5.13__tar.gz → 7.6.0__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.

Potentially problematic release.


This version of cognite-extractor-utils might be problematic. Click here for more details.

Files changed (59) hide show
  1. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/PKG-INFO +1 -1
  2. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/__init__.py +1 -1
  3. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/_inner_util.py +1 -1
  4. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/base.py +120 -40
  5. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/configtools/__init__.py +4 -5
  6. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/configtools/_util.py +3 -2
  7. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/configtools/elements.py +213 -35
  8. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/configtools/loaders.py +68 -16
  9. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/configtools/validators.py +5 -1
  10. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/exceptions.py +11 -2
  11. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/metrics.py +17 -12
  12. cognite_extractor_utils-7.6.0/cognite/extractorutils/statestore/__init__.py +86 -0
  13. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/statestore/_base.py +7 -3
  14. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/statestore/hashing.py +129 -15
  15. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/statestore/watermark.py +77 -87
  16. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/threading.py +30 -4
  17. cognite_extractor_utils-7.6.0/cognite/extractorutils/unstable/__init__.py +8 -0
  18. cognite_extractor_utils-7.6.0/cognite/extractorutils/unstable/configuration/__init__.py +3 -0
  19. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/configuration/exceptions.py +13 -2
  20. cognite_extractor_utils-7.6.0/cognite/extractorutils/unstable/configuration/loaders.py +193 -0
  21. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/configuration/models.py +121 -7
  22. cognite_extractor_utils-7.6.0/cognite/extractorutils/unstable/core/__init__.py +5 -0
  23. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/core/_dto.py +5 -3
  24. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/core/base.py +113 -4
  25. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/core/errors.py +41 -0
  26. cognite_extractor_utils-7.6.0/cognite/extractorutils/unstable/core/logger.py +298 -0
  27. cognite_extractor_utils-7.6.0/cognite/extractorutils/unstable/core/restart_policy.py +43 -0
  28. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/core/runtime.py +119 -36
  29. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/core/tasks.py +53 -1
  30. cognite_extractor_utils-7.6.0/cognite/extractorutils/unstable/scheduling/__init__.py +16 -0
  31. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/scheduling/_scheduler.py +1 -1
  32. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/__init__.py +7 -5
  33. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/_base.py +4 -5
  34. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/assets.py +13 -8
  35. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/data_modeling.py +37 -2
  36. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/events.py +14 -9
  37. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/files.py +80 -21
  38. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/raw.py +12 -7
  39. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/time_series.py +58 -49
  40. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/upload_failure_handler.py +35 -2
  41. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader_extractor.py +29 -6
  42. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader_types.py +15 -1
  43. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/util.py +76 -23
  44. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/pyproject.toml +10 -6
  45. cognite_extractor_utils-7.5.13/cognite/extractorutils/statestore/__init__.py +0 -12
  46. cognite_extractor_utils-7.5.13/cognite/extractorutils/unstable/__init__.py +0 -8
  47. cognite_extractor_utils-7.5.13/cognite/extractorutils/unstable/configuration/__init__.py +0 -0
  48. cognite_extractor_utils-7.5.13/cognite/extractorutils/unstable/configuration/loaders.py +0 -122
  49. cognite_extractor_utils-7.5.13/cognite/extractorutils/unstable/core/__init__.py +0 -0
  50. cognite_extractor_utils-7.5.13/cognite/extractorutils/unstable/core/logger.py +0 -149
  51. cognite_extractor_utils-7.5.13/cognite/extractorutils/unstable/core/restart_policy.py +0 -29
  52. cognite_extractor_utils-7.5.13/cognite/extractorutils/unstable/scheduling/__init__.py +0 -3
  53. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/.gitignore +0 -0
  54. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/LICENSE +0 -0
  55. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/README.md +0 -0
  56. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/py.typed +0 -0
  57. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/core/_messaging.py +0 -0
  58. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/unstable/scheduling/_schedules.py +0 -0
  59. {cognite_extractor_utils-7.5.13 → cognite_extractor_utils-7.6.0}/cognite/extractorutils/uploader/_metrics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite-extractor-utils
3
- Version: 7.5.13
3
+ Version: 7.6.0
4
4
  Summary: Utilities for easier development of extractors for CDF
5
5
  Project-URL: repository, https://github.com/cognitedata/python-extractor-utils
6
6
  Author-email: Mathias Lohne <mathias.lohne@cognite.com>
@@ -16,7 +16,7 @@
16
16
  Cognite extractor utils is a Python package that simplifies the development of new extractors.
17
17
  """
18
18
 
19
- __version__ = "7.5.13"
19
+ __version__ = "7.6.0"
20
20
  from .base import Extractor
21
21
 
22
22
  __all__ = ["Extractor"]
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  """
16
- A module containing utilities meant for use inside the extractor-utils package
16
+ A module containing utilities meant for use inside the extractor-utils package.
17
17
  """
18
18
 
19
19
  import json
@@ -1,3 +1,7 @@
1
+ """
2
+ Module containing the base class for extractors.
3
+ """
4
+
1
5
  # Copyright 2021 Cognite AS
2
6
  #
3
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,28 +16,45 @@
12
16
  # See the License for the specific language governing permissions and
13
17
  # limitations under the License.
14
18
 
19
+ import contextlib
15
20
  import logging
16
21
  import os
17
22
  import sys
18
23
  from collections.abc import Callable
19
24
  from dataclasses import is_dataclass
20
25
  from enum import Enum
26
+ from textwrap import shorten
21
27
  from threading import Thread
22
28
  from types import TracebackType
23
- from typing import Any, Generic, TypeVar
29
+ from typing import Any, Generic, Literal, TypeVar
24
30
 
25
31
  from dotenv import load_dotenv
26
32
 
27
33
  from cognite.client import CogniteClient
28
34
  from cognite.client.data_classes import ExtractionPipeline, ExtractionPipelineRun
29
- from cognite.extractorutils.configtools import BaseConfig, ConfigResolver, StateStoreConfig
35
+ from cognite.client.exceptions import CogniteAPIError
36
+ from cognite.extractorutils.configtools import (
37
+ BaseConfig,
38
+ ConfigResolver,
39
+ StateStoreConfig,
40
+ )
30
41
  from cognite.extractorutils.exceptions import InvalidConfigError
31
42
  from cognite.extractorutils.metrics import BaseMetrics
32
- from cognite.extractorutils.statestore import AbstractStateStore, LocalStateStore, NoStateStore
43
+ from cognite.extractorutils.statestore import (
44
+ AbstractStateStore,
45
+ LocalStateStore,
46
+ NoStateStore,
47
+ )
33
48
  from cognite.extractorutils.threading import CancellationToken
34
49
 
50
+ ReportStatus = Literal["success", "failure", "seen"]
51
+
35
52
 
36
53
  class ReloadConfigAction(Enum):
54
+ """
55
+ Enum for actions to take when a config file is reloaded.
56
+ """
57
+
37
58
  DO_NOTHING = 1
38
59
  REPLACE_ATTRIBUTE = 2
39
60
  SHUTDOWN = 3
@@ -125,8 +146,10 @@ class Extractor(Generic[CustomConfigClass]):
125
146
 
126
147
  def _initial_load_config(self, override_path: str | None = None) -> None:
127
148
  """
128
- Load a configuration file, either from the specified path, or by a path specified by the user in a command line
129
- arg. Will quit further execution of no path is given.
149
+ Load a configuration file.
150
+
151
+ Either from the specified path, or from a path specified by the user in a command line arg. Will quit further
152
+ execution if no path is specified.
130
153
 
131
154
  Args:
132
155
  override_path: Optional override for file path, ie don't parse command line arguments
@@ -149,6 +172,11 @@ class Extractor(Generic[CustomConfigClass]):
149
172
  Thread(target=config_refresher, name="ConfigReloader", daemon=True).start()
150
173
 
151
174
  def reload_config_callback(self) -> None:
175
+ """
176
+ If the reload_config_action was set to CALLBACK, this method will be called when the config file is reloaded.
177
+
178
+ This method should be overridden in subclasses to provide custom behavior when the config file is reloaded.
179
+ """
152
180
  self.logger.error("Method for reloading configs has not been overridden in subclass")
153
181
 
154
182
  def _reload_config(self) -> None:
@@ -172,9 +200,11 @@ class Extractor(Generic[CustomConfigClass]):
172
200
 
173
201
  def _load_state_store(self) -> None:
174
202
  """
175
- Searches through the config object for a StateStoreConfig. If found, it will use that configuration to generate
176
- a state store, if no such config is found it will either create a LocalStateStore or a NoStateStore depending
177
- on whether the ``use_default_state_store`` argument to the constructor was true or false.
203
+ Searches through the config object for a StateStoreConfig.
204
+
205
+ If found, it will use that configuration to generate a state store, if no such config is found it will either
206
+ create a LocalStateStore or a NoStateStore depending on whether the ``use_default_state_store`` argument to the
207
+ constructor was true or false.
178
208
 
179
209
  Either way, the state_store attribute is guaranteed to be set after calling this method.
180
210
  """
@@ -210,37 +240,63 @@ class Extractor(Generic[CustomConfigClass]):
210
240
 
211
241
  Extractor._statestore_singleton = self.state_store
212
242
 
213
- def _report_success(self) -> None:
243
+ def _report_run(self, status: ReportStatus, message: str) -> None:
214
244
  """
215
- Called on a successful exit of the extractor
245
+ Report the status of the extractor run to the extraction pipeline.
246
+
247
+ Args:
248
+ status: Status of the run, either success or failure or seen
249
+ message: Message to report to the extraction pipeline
216
250
  """
251
+ MAX_MESSAGE_LENGTH_FOR_EXTRACTION_PIPELINE_RUN = 1000
217
252
  if self.extraction_pipeline:
218
- self.logger.info("Reporting new successful run")
219
- self.cognite_client.extraction_pipelines.runs.create(
220
- ExtractionPipelineRun(
253
+ try:
254
+ message = message or ""
255
+ shortened_message = shorten(
256
+ message,
257
+ width=MAX_MESSAGE_LENGTH_FOR_EXTRACTION_PIPELINE_RUN,
258
+ placeholder="...",
259
+ )
260
+
261
+ run = ExtractionPipelineRun(
221
262
  extpipe_external_id=self.extraction_pipeline.external_id,
222
- status="success",
223
- message=self.success_message,
263
+ status=status,
264
+ message=shortened_message,
224
265
  )
225
- )
266
+
267
+ self.logger.info(f"Reporting new {status} run: {message}")
268
+ self.cognite_client.extraction_pipelines.runs.create(run)
269
+ except CogniteAPIError as e:
270
+ self.logger.exception(f"Error while reporting run - status {status} - message {message} . Error: {e!s}")
271
+
272
+ def _report_success(self, message: str | None = None) -> None:
273
+ """
274
+ Called on a successful exit of the extractor.
275
+
276
+ Args:
277
+ message: Message to report to the extraction pipeline. If not provided, Extractor.success_message is taken.
278
+ """
279
+ message = message or self.success_message
280
+ self._report_run("success", message)
281
+
282
+ def _report_failure(self, message: str) -> None:
283
+ """
284
+ Called on an unsuccessful exit of the extractor.
285
+
286
+ Args:
287
+ message: Message to report to the extraction pipeline
288
+ """
289
+ self._report_run("failure", message)
226
290
 
227
291
  def _report_error(self, exception: BaseException) -> None:
228
292
  """
229
- Called on an unsuccessful exit of the extractor
293
+ Called on an unsuccessful exit of the extractor.
230
294
 
231
295
  Args:
232
296
  exception: Exception object that caused the extractor to fail
233
297
  """
234
- self.logger.error("Unexpected error during extraction", exc_info=exception)
235
- if self.extraction_pipeline:
236
- message = f"{type(exception).__name__}: {str(exception)}"[:1000]
237
-
238
- self.logger.info(f"Reporting new failed run: {message}")
239
- self.cognite_client.extraction_pipelines.runs.create(
240
- ExtractionPipelineRun(
241
- extpipe_external_id=self.extraction_pipeline.external_id, status="failure", message=message
242
- )
243
- )
298
+ message = f"{type(exception).__name__}: {exception!s}"
299
+ self._report_run("failure", message)
244
300
 
245
301
  def __enter__(self) -> "Extractor":
246
302
  """
@@ -249,7 +305,6 @@ class Extractor(Generic[CustomConfigClass]):
249
305
  Returns:
250
306
  self
251
307
  """
252
-
253
308
  if str(os.getenv("COGNITE_FUNCTION_RUNTIME", False)).lower() != "true":
254
309
  # Environment Variables
255
310
  env_file_found = load_dotenv(dotenv_path="./.env", override=True)
@@ -263,7 +318,9 @@ class Extractor(Generic[CustomConfigClass]):
263
318
  try:
264
319
  self._initial_load_config(override_path=self.config_file_path)
265
320
  except InvalidConfigError as e:
266
- print("Critical error: Could not read config file", file=sys.stderr) # noqa: T201
321
+ print( # noqa: T201
322
+ "Critical error: Could not read config file", file=sys.stderr
323
+ )
267
324
  print(str(e), file=sys.stderr) # noqa: T201
268
325
  sys.exit(1)
269
326
 
@@ -284,10 +341,8 @@ class Extractor(Generic[CustomConfigClass]):
284
341
 
285
342
  self.extraction_pipeline = self.config.cognite.get_extraction_pipeline(self.cognite_client)
286
343
 
287
- try:
344
+ with contextlib.suppress(AttributeError):
288
345
  self.config.metrics.start_pushers(self.cognite_client) # type: ignore
289
- except AttributeError:
290
- pass
291
346
 
292
347
  def heartbeat_loop() -> None:
293
348
  while not self.cancellation_token.is_cancelled:
@@ -324,7 +379,10 @@ class Extractor(Generic[CustomConfigClass]):
324
379
  return self
325
380
 
326
381
  def __exit__(
327
- self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
382
+ self,
383
+ exc_type: type[BaseException] | None,
384
+ exc_val: BaseException | None,
385
+ exc_tb: TracebackType | None,
328
386
  ) -> bool:
329
387
  """
330
388
  Shuts down the extractor. Makes sure states are preserved, that all uploads of data and metrics are done, etc.
@@ -345,10 +403,8 @@ class Extractor(Generic[CustomConfigClass]):
345
403
  if self.state_store:
346
404
  self.state_store.synchronize()
347
405
 
348
- try:
406
+ with contextlib.suppress(AttributeError):
349
407
  self.config.metrics.stop_pushers() # type: ignore
350
- except AttributeError:
351
- pass
352
408
 
353
409
  if exc_val:
354
410
  self._report_error(exc_val)
@@ -360,26 +416,50 @@ class Extractor(Generic[CustomConfigClass]):
360
416
 
361
417
  def run(self) -> None:
362
418
  """
363
- Run the extractor. Ensures that the Extractor is set up correctly (``run`` called within a ``with``) and calls
364
- the ``run_handle``.
419
+ Run the extractor.
420
+
421
+ Ensures that the Extractor is set up correctly (``run`` called within a ``with``) and calls the ``run_handle``.
365
422
 
366
- Can be overrided in subclasses.
423
+ Can be overriden in subclasses.
367
424
  """
368
425
  if not self.started:
369
426
  raise ValueError("You must run the extractor in a context manager")
370
427
  if self.run_handle:
371
- self.run_handle(self.cognite_client, self.state_store, self.config, self.cancellation_token)
428
+ self.run_handle(
429
+ self.cognite_client,
430
+ self.state_store,
431
+ self.config,
432
+ self.cancellation_token,
433
+ )
372
434
  else:
373
435
  raise ValueError("No run_handle defined")
374
436
 
375
437
  @classmethod
376
438
  def get_current_config(cls) -> CustomConfigClass:
439
+ """
440
+ Get the current configuration singleton.
441
+
442
+ Returns:
443
+ The current configuration singleton
444
+
445
+ Raises:
446
+ ValueError: If no configuration singleton has been created, meaning no config file has been loaded.
447
+ """
377
448
  if Extractor._config_singleton is None: # type: ignore
378
449
  raise ValueError("No config singleton created. Have a config file been loaded?")
379
450
  return Extractor._config_singleton # type: ignore
380
451
 
381
452
  @classmethod
382
453
  def get_current_statestore(cls) -> AbstractStateStore:
454
+ """
455
+ Get the current state store singleton.
456
+
457
+ Returns:
458
+ The current state store singleton
459
+
460
+ Raises:
461
+ ValueError: If no state store singleton has been created, meaning no state store has been loaded.
462
+ """
383
463
  if Extractor._statestore_singleton is None:
384
464
  raise ValueError("No state store singleton created. Have a state store been loaded?")
385
465
  return Extractor._statestore_singleton
@@ -13,8 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  """
16
- Module containing tools for loading and verifying config files, and a YAML loader to automatically serialize these
17
- dataclasses from a config file.
16
+ Module containing tools for loading and verifying config files.
18
17
 
19
18
  Configs are described as ``dataclass`` es, and use the ``BaseConfig`` class as a superclass to get a few things
20
19
  built-in: config version, Cognite project and logging. Use type hints to specify types, use the ``Optional`` type to
@@ -114,10 +113,13 @@ __all__ = [
114
113
  "CastableInt",
115
114
  "CertificateConfig",
116
115
  "CogniteConfig",
116
+ "ConfigResolver",
117
117
  "ConfigType",
118
118
  "ConnectionConfig",
119
119
  "EitherIdConfig",
120
120
  "FileSizeConfig",
121
+ "KeyVaultAuthenticationMethod",
122
+ "KeyVaultLoader",
121
123
  "LocalStateStoreConfig",
122
124
  "LoggingConfig",
123
125
  "MetricsConfig",
@@ -126,9 +128,6 @@ __all__ = [
126
128
  "RawStateStoreConfig",
127
129
  "StateStoreConfig",
128
130
  "TimeIntervalConfig",
129
- "ConfigResolver",
130
- "KeyVaultAuthenticationMethod",
131
- "KeyVaultLoader",
132
131
  "load_yaml",
133
132
  "load_yaml_dict",
134
133
  ]
@@ -27,8 +27,9 @@ from cognite.extractorutils.exceptions import InvalidConfigError
27
27
 
28
28
  def _to_snake_case(dictionary: dict[str, Any], case_style: str) -> dict[str, Any]:
29
29
  """
30
- Ensure that all keys in the dictionary follows the snake casing convention (recursively, so any sub-dictionaries are
31
- changed too).
30
+ Ensure that all keys in the dictionary follows the snake casing convention.
31
+
32
+ This function will recursively fix any list or dictionaries. The input dictionary is never modified in place.
32
33
 
33
34
  Args:
34
35
  dictionary: Dictionary to update.