everysk-lib 1.8.17__tar.gz → 1.9.1__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 (135) hide show
  1. {everysk_lib-1.8.17/src/everysk_lib.egg-info → everysk_lib-1.9.1}/PKG-INFO +3 -1
  2. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/pyproject.toml +4 -0
  3. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/log.py +111 -83
  4. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/tests.py +3 -2
  5. everysk_lib-1.9.1/src/everysk/sql/__init__.py +9 -0
  6. everysk_lib-1.9.1/src/everysk/sql/connection.py +158 -0
  7. everysk_lib-1.9.1/src/everysk/sql/model.py +381 -0
  8. everysk_lib-1.9.1/src/everysk/sql/query.py +377 -0
  9. everysk_lib-1.9.1/src/everysk/sql/settings.py +20 -0
  10. everysk_lib-1.9.1/src/everysk/sql/utils.py +128 -0
  11. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/tests.py +3 -2
  12. {everysk_lib-1.8.17 → everysk_lib-1.9.1/src/everysk_lib.egg-info}/PKG-INFO +3 -1
  13. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk_lib.egg-info/SOURCES.txt +6 -0
  14. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk_lib.egg-info/requires.txt +3 -0
  15. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/LICENSE.txt +0 -0
  16. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/MANIFEST.in +0 -0
  17. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/README.md +0 -0
  18. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/setup.cfg +0 -0
  19. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/setup.py +0 -0
  20. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/__init__.py +0 -0
  21. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/_version.py +0 -0
  22. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/__init__.py +0 -0
  23. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_requestor.py +0 -0
  24. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/__init__.py +0 -0
  25. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/api_resource.py +0 -0
  26. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/calculation.py +0 -0
  27. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/custom_index.py +0 -0
  28. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/datastore.py +0 -0
  29. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/file.py +0 -0
  30. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/market_data.py +0 -0
  31. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/parser.py +0 -0
  32. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/portfolio.py +0 -0
  33. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/private_security.py +0 -0
  34. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/report.py +0 -0
  35. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/report_template.py +0 -0
  36. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/tests.py +0 -0
  37. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/worker_execution.py +0 -0
  38. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/workflow.py +0 -0
  39. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/workflow_execution.py +0 -0
  40. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/api_resources/workspace.py +0 -0
  41. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/http_client.py +0 -0
  42. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/tests.py +0 -0
  43. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/api/utils.py +0 -0
  44. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/config.py +0 -0
  45. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/_tests/serialize/test_json.py +0 -0
  46. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/_tests/serialize/test_orjson.py +0 -0
  47. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/_tests/serialize/test_pickle.py +0 -0
  48. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/cloud_function/main.py +0 -0
  49. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/cloud_function/tests.py +0 -0
  50. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/compress.py +0 -0
  51. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/datetime/__init__.py +0 -0
  52. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/datetime/calendar.py +0 -0
  53. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/datetime/date.py +0 -0
  54. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/datetime/date_expression.py +0 -0
  55. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/datetime/date_mixin.py +0 -0
  56. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/datetime/date_settings.py +0 -0
  57. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/datetime/datetime.py +0 -0
  58. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/exceptions.py +0 -0
  59. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/fields.py +0 -0
  60. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/firestore.py +0 -0
  61. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/fixtures/_settings.py +0 -0
  62. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/fixtures/other/_settings.py +0 -0
  63. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/fixtures/user_agents.json +0 -0
  64. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/http.py +0 -0
  65. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/lists.py +0 -0
  66. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/number.py +0 -0
  67. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/object.py +0 -0
  68. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/redis.py +0 -0
  69. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/serialize.py +0 -0
  70. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/sftp.py +0 -0
  71. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/signing.py +0 -0
  72. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/slack.py +0 -0
  73. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/string.py +0 -0
  74. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/threads.py +0 -0
  75. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/undefined.py +0 -0
  76. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/unittests.py +0 -0
  77. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/core/workers.py +0 -0
  78. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/__init__.py +0 -0
  79. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/base.py +0 -0
  80. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/engines/__init__.py +0 -0
  81. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/engines/cache.py +0 -0
  82. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/engines/compliance.py +0 -0
  83. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/engines/cryptography.py +0 -0
  84. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/engines/lock.py +0 -0
  85. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/engines/market_data.py +0 -0
  86. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/engines/settings.py +0 -0
  87. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/__init__.py +0 -0
  88. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/base.py +0 -0
  89. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/base_list.py +0 -0
  90. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/custom_index/base.py +0 -0
  91. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/custom_index/settings.py +0 -0
  92. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/datastore/base.py +0 -0
  93. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/datastore/settings.py +0 -0
  94. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/fields.py +0 -0
  95. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/file/base.py +0 -0
  96. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/file/settings.py +0 -0
  97. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/portfolio/base.py +0 -0
  98. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/portfolio/securities.py +0 -0
  99. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/portfolio/security.py +0 -0
  100. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/portfolio/settings.py +0 -0
  101. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/private_security/base.py +0 -0
  102. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/private_security/settings.py +0 -0
  103. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/query.py +0 -0
  104. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/report/base.py +0 -0
  105. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/report/settings.py +0 -0
  106. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/script.py +0 -0
  107. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/secrets/base.py +0 -0
  108. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/secrets/settings.py +0 -0
  109. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/settings.py +0 -0
  110. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/tags.py +0 -0
  111. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/worker_execution/base.py +0 -0
  112. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/worker_execution/settings.py +0 -0
  113. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/workflow_execution/base.py +0 -0
  114. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/workflow_execution/settings.py +0 -0
  115. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/workspace/base.py +0 -0
  116. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/entities/workspace/settings.py +0 -0
  117. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/settings.py +0 -0
  118. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/tests.py +0 -0
  119. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/sdk/worker_base.py +0 -0
  120. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/__init__.py +0 -0
  121. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/applications.py +0 -0
  122. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/endpoints.py +0 -0
  123. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/example_api.py +0 -0
  124. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/middlewares.py +0 -0
  125. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/requests.py +0 -0
  126. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/responses.py +0 -0
  127. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/routing.py +0 -0
  128. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/settings.py +0 -0
  129. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/server/tests.py +0 -0
  130. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/settings.py +0 -0
  131. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/utils.py +0 -0
  132. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk/version.py +0 -0
  133. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk_lib.egg-info/.gitignore +0 -0
  134. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk_lib.egg-info/dependency_links.txt +0 -0
  135. {everysk_lib-1.8.17 → everysk_lib-1.9.1}/src/everysk_lib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: everysk-lib
3
- Version: 1.8.17
3
+ Version: 1.9.1
4
4
  Summary: Generic lib to share python code on Everysk.
5
5
  License-Expression: LicenseRef-Proprietary
6
6
  Project-URL: Homepage, https://everysk.com/
@@ -117,6 +117,8 @@ Provides-Extra: expression
117
117
  Requires-Dist: lark==1.2.2; extra == "expression"
118
118
  Requires-Dist: numpy==1.26.4; extra == "expression"
119
119
  Requires-Dist: pandas==2.1.4; extra == "expression"
120
+ Provides-Extra: postgresql
121
+ Requires-Dist: psqlpy==0.11.8; extra == "postgresql"
120
122
  Dynamic: license-file
121
123
 
122
124
 
@@ -181,6 +181,10 @@ expression = [
181
181
  "pandas==2.1.4"
182
182
  ]
183
183
 
184
+ postgresql = [
185
+ "psqlpy==0.11.8"
186
+ ]
187
+
184
188
  [build-system]
185
189
  requires = ["setuptools>=70", "wheel", "cibuildwheel", "versioneer[toml]"]
186
190
  build-backend = "setuptools.build_meta"
@@ -11,19 +11,18 @@ __all__ = ['Logger', 'LoggerManager']
11
11
 
12
12
  import json
13
13
  import logging
14
- import traceback as python_traceback
15
14
  import sys
15
+ import traceback as python_traceback
16
16
  from contextlib import AbstractContextManager
17
17
  from contextvars import ContextVar
18
- from datetime import datetime as DateTime
18
+ from datetime import datetime as DateTime # noqa: N812
19
+ from threading import Thread
19
20
  from types import TracebackType
20
21
  from typing import Any
21
- from threading import Thread
22
22
  from zoneinfo import ZoneInfo
23
23
 
24
24
  from everysk.config import settings
25
25
 
26
-
27
26
  DEFAULT_APP_SERVER = settings.LOGGING_APP_SERVER
28
27
  DEFAULT_TRACE_PARTS = {'version': '', 'trace_id': '', 'span_id': '', 'trace_sample': False}
29
28
  HEADER_TRACEPARENT = 'traceparent'
@@ -36,7 +35,7 @@ LOGGER_MANAGER_CONTEXT_VAR_NAME = 'everysk-lib-log-extra-context-var'
36
35
  ###############################################################################
37
36
  def _default(obj: Any) -> str | None:
38
37
  """
39
- This function is used to convert the object to a string inside the json.dumps.
38
+ Function is used to convert the object to a string inside the json.dumps.
40
39
 
41
40
  Args:
42
41
  obj (Any): The object to be converted to a string.
@@ -46,7 +45,8 @@ def _default(obj: Any) -> str | None:
46
45
 
47
46
  return None
48
47
 
49
- def _get_gcp_headers(headers: dict = None) -> dict:
48
+
49
+ def _get_gcp_headers(headers: dict | None = None) -> dict:
50
50
  """
51
51
  Get the headers to be added to the log.
52
52
  The order is if the headers are sent in the log function, set in context or get from the default function.
@@ -57,12 +57,12 @@ def _get_gcp_headers(headers: dict = None) -> dict:
57
57
  Returns:
58
58
  dict: Only the headers that are in the list HEADER_TRACEPARENT and HEADER_X_CLOUD_TRACE_CONTEXT.
59
59
  """
60
- # pylint: disable=protected-access
61
60
  if not headers:
62
- headers = LoggerManager._extra.get().get('http_headers', {})
61
+ headers = LoggerManager._extra.get().get('http_headers', {}) # noqa: SLF001
63
62
 
64
63
  return {key: value for key, value in headers.items() if key in (HEADER_TRACEPARENT, HEADER_X_CLOUD_TRACE_CONTEXT)}
65
64
 
65
+
66
66
  def _get_trace_data(headers: dict) -> dict:
67
67
  """
68
68
  Get the trace_id, span_id and trace_sample from the headers.
@@ -81,14 +81,13 @@ def _get_trace_data(headers: dict) -> dict:
81
81
  _parse_x_cloud_trace_context(trace, trace_parts)
82
82
 
83
83
  if trace_parts['trace_id']:
84
- trace_parts['trace_id'] = f"{settings.LOGGING_GOOGLE_CLOUD_TRACE_ID}/{trace_parts['trace_id']}"
84
+ trace_parts['trace_id'] = f'{settings.LOGGING_GOOGLE_CLOUD_TRACE_ID}/{trace_parts["trace_id"]}'
85
85
 
86
86
  return trace_parts
87
87
 
88
+
88
89
  def _get_traceback() -> str:
89
- """
90
- Get the traceback of the current exception.
91
- """
90
+ """Get the traceback of the current exception."""
92
91
  result = python_traceback.format_exc()
93
92
  # When there is no traceback the result is 'NoneType: None\n'
94
93
  # Like this is the most common case we check for equality and return an empty string
@@ -97,6 +96,7 @@ def _get_traceback() -> str:
97
96
 
98
97
  return result
99
98
 
99
+
100
100
  def _parse_traceparent(traceparent: str, trace_parts: dict) -> None:
101
101
  """
102
102
  Parse the traceparent header and return a dictionary with the version, trace_id, span_id and trace_flags.
@@ -106,13 +106,17 @@ def _parse_traceparent(traceparent: str, trace_parts: dict) -> None:
106
106
 
107
107
  Args:
108
108
  traceparent (str): The traceparent header value.
109
+ trace_parts (dict): The dictionary where the parsed values will be stored.
109
110
  """
110
111
  try:
111
- trace_parts['version'], trace_parts['trace_id'], trace_parts['span_id'], trace_parts['trace_sample'] = traceparent.split('-')
112
+ trace_parts['version'], trace_parts['trace_id'], trace_parts['span_id'], trace_parts['trace_sample'] = (
113
+ traceparent.split('-')
114
+ )
112
115
  trace_parts['trace_sample'] = bool(int(trace_parts['trace_sample']))
113
116
  except ValueError:
114
117
  pass
115
118
 
119
+
116
120
  def _parse_x_cloud_trace_context(trace_context: str, trace_parts: dict) -> None:
117
121
  """
118
122
  Parse the x-cloud-trace-context header and return a dictionary with the trace_id and span_id.
@@ -121,11 +125,12 @@ def _parse_x_cloud_trace_context(trace_context: str, trace_parts: dict) -> None:
121
125
 
122
126
  Args:
123
127
  trace_context (str): The x-cloud-trace-context header value.
128
+ trace_parts (dict): The dictionary where the parsed values will be stored.
124
129
  """
125
130
  try:
126
131
  trace_parts['trace_id'], trace_parts['span_id'] = trace_context.split('/')
127
132
  trace_parts['span_id'], trace_parts['trace_sample'] = trace_parts['span_id'].split(';')
128
- trace_parts['trace_sample'] = trace_parts['trace_sample'].endswith('1') # pylint: disable=no-member
133
+ trace_parts['trace_sample'] = trace_parts['trace_sample'].endswith('1') # pylint: disable=no-member
129
134
  except ValueError:
130
135
  pass
131
136
 
@@ -134,7 +139,6 @@ def _parse_x_cloud_trace_context(trace_context: str, trace_parts: dict) -> None:
134
139
  # Formatter Class Implementation
135
140
  ###############################################################################
136
141
  class Formatter(logging.Formatter):
137
-
138
142
  def _get_default_dict(self, message: str, severity: str) -> dict:
139
143
  """
140
144
  Python logging default values.
@@ -146,7 +150,9 @@ class Formatter(logging.Formatter):
146
150
  """
147
151
  return {'message': message, 'severity': severity}
148
152
 
149
- def _get_default_extra_dict(self, name: str, headers: dict, payload: dict, response: dict, traceback: str, labels: dict) -> dict:
153
+ def _get_default_extra_dict(
154
+ self, name: str, headers: dict, payload: dict, response: dict, traceback: str, labels: dict
155
+ ) -> dict:
150
156
  """
151
157
  Get the default extra data dictionary to be added to the log.
152
158
  Until now we only have the logName, traceback, http headers and http payload.
@@ -155,16 +161,15 @@ class Formatter(logging.Formatter):
155
161
  name (str): The name used to create the log.
156
162
  headers (dict): A dictionary with the HTTP headers.
157
163
  payload (dict): A dictionary with the HTTP payload.
164
+ response (dict): A dictionary with the HTTP response.
165
+ traceback (str): The traceback of the exception.
166
+ labels (dict): A dictionary with the labels to be added to the log.
158
167
  """
159
168
  return {
160
169
  'logName': name,
161
170
  'labels': labels,
162
171
  'traceback': traceback,
163
- 'http': {
164
- 'headers': headers,
165
- 'payload': payload,
166
- 'response': response,
167
- }
172
+ 'http': {'headers': headers, 'payload': payload, 'response': response},
168
173
  }
169
174
 
170
175
  def _get_default_gcp_dict(self, headers: dict, filename: str, line: int, func_name: str) -> dict:
@@ -183,11 +188,7 @@ class Formatter(logging.Formatter):
183
188
  'logging.googleapis.com/trace': trace['trace_id'],
184
189
  'logging.googleapis.com/spanId': trace['span_id'],
185
190
  'logging.googleapis.com/trace_sampled': trace['trace_sample'],
186
- 'logging.googleapis.com/sourceLocation': {
187
- 'file': filename,
188
- 'line': line,
189
- 'function': func_name,
190
- },
191
+ 'logging.googleapis.com/sourceLocation': {'file': filename, 'line': line, 'function': func_name},
191
192
  }
192
193
 
193
194
  def _get_result_dict(self, record: logging.LogRecord) -> dict:
@@ -198,23 +199,27 @@ class Formatter(logging.Formatter):
198
199
  record (logging.LogRecord): Record object with all the information about the log.
199
200
  """
200
201
  result = self._get_default_dict(message=record.getMessage(), severity=record.levelname)
201
- result.update(self._get_default_extra_dict(
202
- name=record.name,
203
- headers=getattr(record, 'http_headers', {}),
204
- payload=getattr(record, 'http_payload', {}),
205
- response=getattr(record, 'http_response', {}),
206
- traceback=getattr(record, 'traceback', ''),
207
- labels=getattr(record, 'labels', {})
208
- ))
209
- result.update(self._get_default_gcp_dict(
210
- headers=result['http']['headers'],
211
- filename=record.pathname,
212
- line=record.lineno,
213
- func_name=record.funcName)
202
+ result.update(
203
+ self._get_default_extra_dict(
204
+ name=record.name,
205
+ headers=getattr(record, 'http_headers', {}),
206
+ payload=getattr(record, 'http_payload', {}),
207
+ response=getattr(record, 'http_response', {}),
208
+ traceback=getattr(record, 'traceback', ''),
209
+ labels=getattr(record, 'labels', {}),
210
+ )
211
+ )
212
+ result.update(
213
+ self._get_default_gcp_dict(
214
+ headers=result['http']['headers'],
215
+ filename=record.pathname,
216
+ line=record.lineno,
217
+ func_name=record.funcName,
218
+ )
214
219
  )
215
220
  return result
216
221
 
217
- def formatMessage(self, record: logging.LogRecord) -> str:
222
+ def formatMessage(self, record: logging.LogRecord) -> str: # noqa: N802
218
223
  """
219
224
  Format the message to be displayed in the terminal or Google Log Explorer.
220
225
 
@@ -224,23 +229,24 @@ class Formatter(logging.Formatter):
224
229
  result = self._get_result_dict(record)
225
230
  return json.dumps(result, default=_default)
226
231
 
232
+
227
233
  ###############################################################################
228
234
  # LoggerManager Class Implementation
229
235
  ###############################################################################
230
236
  class LoggerManager(AbstractContextManager):
231
237
  ## Private attributes
232
- _extra: ContextVar = ContextVar(LOGGER_MANAGER_CONTEXT_VAR_NAME, default={})
238
+ _extra: ContextVar = ContextVar(LOGGER_MANAGER_CONTEXT_VAR_NAME, default={}) # noqa: B039
233
239
  _old_value: dict = None
234
240
 
235
241
  def __init__(
236
- self,
237
- http_headers: dict = None,
238
- http_payload: dict = None,
239
- http_response: dict = None,
240
- labels: dict = None,
241
- stacklevel: int = None,
242
- traceback: str = None,
243
- ) -> None:
242
+ self,
243
+ http_headers: dict | None = None,
244
+ http_payload: dict | None = None,
245
+ http_response: dict | None = None,
246
+ labels: dict | None = None,
247
+ stacklevel: int | None = None,
248
+ traceback: str | None = None,
249
+ ) -> None:
244
250
  """
245
251
  Context class to create a context manager for the Logger object.
246
252
  This class is used to add extra information to the log.
@@ -250,7 +256,10 @@ class LoggerManager(AbstractContextManager):
250
256
  Args:
251
257
  http_headers (dict, optional): The HTTP headers to be added to the log. Defaults to None.
252
258
  http_payload (dict, optional): The HTTP payload to be added to the log. Defaults to None.
259
+ http_response (dict, optional): The HTTP response to be added to the log. Defaults to None.
253
260
  stacklevel (int, optional): The stacklevel to be used on the log. Defaults to None.
261
+ traceback (str, optional): The traceback to be added to the log. Defaults to None.
262
+ labels (dict, optional): A dictionary with the labels to be added to the log. Defaults to None.
254
263
  """
255
264
  self.http_headers = http_headers
256
265
  self.http_payload = http_payload
@@ -293,7 +302,9 @@ class LoggerManager(AbstractContextManager):
293
302
 
294
303
  return self
295
304
 
296
- def __exit__(self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None, __traceback: TracebackType | None) -> bool | None:
305
+ def __exit__(
306
+ self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
307
+ ) -> bool | None:
297
308
  """
298
309
  https://docs.python.org/3/library/stdtypes.html#contextmanager.__exit__
299
310
 
@@ -311,16 +322,17 @@ class LoggerManager(AbstractContextManager):
311
322
  Reset the context to the default value.
312
323
  This method is used to avoid shared values between requests in server.endpoints module.
313
324
  """
314
- cls._extra = ContextVar(LOGGER_MANAGER_CONTEXT_VAR_NAME, default={})
325
+ cls._extra = ContextVar(LOGGER_MANAGER_CONTEXT_VAR_NAME, default={}) # noqa: B039
326
+
315
327
 
316
328
  ###############################################################################
317
329
  # Logger Class Implementation
318
330
  ###############################################################################
319
331
  class Logger:
320
-
321
332
  ## Private attributes
322
- _deprecated_hash: set[str] = set() # This needs to be initialized with an empty set because it will be a global list
323
- _default_stacklevel: int = 3 # 3 -> The place where the log.method was placed; 2 -> This file; 1 -> Python logger
333
+ # This needs to be initialized with an empty set because it will be a global list
334
+ _deprecated_hash: set[str] = set() # noqa: RUF012
335
+ _default_stacklevel: int = 3 # 3 -> The place where the log.method was placed; 2 -> This file; 1 -> Python logger
324
336
  _log: logging.Logger = None
325
337
  _slack_timer: DateTime = None
326
338
 
@@ -329,7 +341,7 @@ class Logger:
329
341
  stacklevel: int = None
330
342
 
331
343
  ## Private methods
332
- def __init__(self, name: str, stacklevel: int = None) -> None:
344
+ def __init__(self, name: str, stacklevel: int | None = None) -> None:
333
345
  """
334
346
  Logger class used to send messages to STDOUT or Google CLoud Logging.
335
347
  Use stacklevel to show correctly the file and the line of the log, lvl 0 means the python
@@ -354,7 +366,7 @@ class Logger:
354
366
 
355
367
  self._log = self._get_python_logger()
356
368
 
357
- def _get_extra_data(self, extra: dict, level: int = None) -> dict:
369
+ def _get_extra_data(self, extra: dict, level: int | None = None) -> dict:
358
370
  """
359
371
  Get the extra data to be added to the log.
360
372
  We check if the extra was sent in the function, if not we check if it
@@ -383,7 +395,7 @@ class Logger:
383
395
  labels = extra.pop('labels', {})
384
396
  if not labels:
385
397
  # If we don't receive labels as param in log functions we get from the context
386
- labels = LoggerManager._extra.get().get('labels', {}) # pylint: disable=protected-access
398
+ labels = LoggerManager._extra.get().get('labels', {}) # noqa: SLF001
387
399
 
388
400
  # Labels are always present in the log even if it is an empty dictionary
389
401
  extra['labels'] = labels
@@ -392,7 +404,7 @@ class Logger:
392
404
  if not traceback:
393
405
  # If we don't receive traceback as param in log functions we get from the context
394
406
  # or from the result of traceback module
395
- traceback = LoggerManager._extra.get().get('traceback', '') or _get_traceback() # pylint: disable=protected-access
407
+ traceback = LoggerManager._extra.get().get('traceback', '') or _get_traceback() # noqa: SLF001
396
408
  if traceback:
397
409
  extra['traceback'] = traceback
398
410
 
@@ -410,7 +422,7 @@ class Logger:
410
422
  """
411
423
  return _get_gcp_headers(http_headers)
412
424
 
413
- def _get_http_payload(self, http_payload: dict, level: int = None) -> dict:
425
+ def _get_http_payload(self, http_payload: dict, level: int | None = None) -> dict:
414
426
  """
415
427
  Get the http payload to be added to the log.
416
428
  The order is if the payload are sent in the log function, set in context or get from the default function.
@@ -421,7 +433,7 @@ class Logger:
421
433
  level (int): The level of the log.
422
434
  """
423
435
  if not http_payload and level in (logging.ERROR, logging.CRITICAL):
424
- http_payload = LoggerManager._extra.get().get('http_payload', {}) # pylint: disable=protected-access
436
+ http_payload = LoggerManager._extra.get().get('http_payload', {}) # noqa: SLF001
425
437
 
426
438
  return http_payload
427
439
 
@@ -434,7 +446,7 @@ class Logger:
434
446
  http_response (dict): The HTTP response sent in the log function.
435
447
  """
436
448
  if not http_response:
437
- http_response = LoggerManager._extra.get().get('http_response', {}) # pylint: disable=protected-access
449
+ http_response = LoggerManager._extra.get().get('http_response', {}) # noqa: SLF001
438
450
 
439
451
  return http_response
440
452
 
@@ -449,7 +461,7 @@ class Logger:
449
461
  # Create the log
450
462
  log = logging.getLogger(self.name)
451
463
  log.setLevel(logging.DEBUG)
452
- log.propagate = False # Don't pass message to others loggers
464
+ log.propagate = False # Don't pass message to others loggers
453
465
 
454
466
  # We should only have one handler per log name
455
467
  if not hasattr(log, 'handler'):
@@ -480,13 +492,15 @@ class Logger:
480
492
  stacklevel (int): The stacklevel to be used on the log.
481
493
  """
482
494
  if not stacklevel:
483
- stacklevel = LoggerManager._extra.get().get('stacklevel', {}) # pylint: disable=protected-access
495
+ stacklevel = LoggerManager._extra.get().get('stacklevel', {}) # noqa: SLF001
484
496
  if not stacklevel:
485
497
  stacklevel = self.stacklevel or self._default_stacklevel
486
498
 
487
499
  return stacklevel
488
500
 
489
- def _send_to_log(self, level: int, msg: str, *args: tuple, extra: dict = None, stacklevel: int = None) -> None:
501
+ def _send_to_log(
502
+ self, level: int, msg: str, *args: tuple, extra: dict | None = None, stacklevel: int | None = None
503
+ ) -> None:
490
504
  """
491
505
  Send the message to the python logger using the correct level.
492
506
  Use stacklevel to show correctly the file and the line of the log, lvl 0 means the python
@@ -504,7 +518,7 @@ class Logger:
504
518
  extra = self._get_extra_data(extra or {}, level)
505
519
  self._log.log(level, msg, *args, extra=extra, stacklevel=stacklevel)
506
520
 
507
- def _show_deprecated(self, _id: str, show_once: bool) -> bool:
521
+ def _show_deprecated(self, _id: str, *, show_once: bool) -> bool:
508
522
  """
509
523
  If show_once is False this always return True, otherwise
510
524
  checks if this _id is in the self._deprecated_hash set.
@@ -522,7 +536,7 @@ class Logger:
522
536
  return True
523
537
 
524
538
  ## Public methods
525
- def critical(self, msg: str, *args: tuple, extra: dict = None, stacklevel: int = None)-> None:
539
+ def critical(self, msg: str, *args: tuple, extra: dict | None = None, stacklevel: int | None = None) -> None:
526
540
  """
527
541
  Log a message with severity CRITICAL on this logger.
528
542
  Use stacklevel to show correctly the file and the line of the log, lvl 0 means the python
@@ -537,7 +551,7 @@ class Logger:
537
551
  """
538
552
  self._send_to_log(logging.CRITICAL, msg, *args, extra=extra, stacklevel=stacklevel)
539
553
 
540
- def debug(self, msg: str, *args: tuple, extra: dict = None, stacklevel: int = None)-> None:
554
+ def debug(self, msg: str, *args: tuple, extra: dict | None = None, stacklevel: int | None = None) -> None:
541
555
  """
542
556
  Log a message with severity DEBUG on this logger.
543
557
  Use stacklevel to show correctly the file and the line of the log, lvl 0 means the python
@@ -552,7 +566,9 @@ class Logger:
552
566
  """
553
567
  self._send_to_log(logging.DEBUG, msg, *args, extra=extra, stacklevel=stacklevel)
554
568
 
555
- def deprecated(self, msg: str, *args, show_once: bool = True, extra: dict = None, stacklevel: int = None) -> None:
569
+ def deprecated( # noqa: D417
570
+ self, msg: str, *args, show_once: bool = True, extra: dict | None = None, stacklevel: int | None = None
571
+ ) -> None:
556
572
  """
557
573
  Shows a DeprecationWarning message with severity 'WARNING'.
558
574
  If show_once is True, then the message will be showed only once.
@@ -568,7 +584,7 @@ class Logger:
568
584
  msg = f'DeprecationWarning: {msg}'
569
585
  self.warning(msg, *args, extra=extra, stacklevel=stacklevel)
570
586
 
571
- def error(self, msg: str, *args: tuple, extra: dict = None, stacklevel: int = None)-> None:
587
+ def error(self, msg: str, *args: tuple, extra: dict | None = None, stacklevel: int | None = None) -> None:
572
588
  """
573
589
  Log a message with severity ERROR on this logger.
574
590
  Use stacklevel to show correctly the file and the line of the log, lvl 0 means the python
@@ -583,7 +599,7 @@ class Logger:
583
599
  """
584
600
  self._send_to_log(logging.ERROR, msg, *args, extra=extra, stacklevel=stacklevel)
585
601
 
586
- def exception(self, msg: str, *args: tuple, extra: dict = None, stacklevel: int = None)-> None:
602
+ def exception(self, msg: str, *args: tuple, extra: dict | None = None, stacklevel: int | None = None) -> None:
587
603
  """
588
604
  Log a message with severity ERROR on this logger.
589
605
  Use stacklevel to show correctly the file and the line of the log, lvl 0 means the python
@@ -598,7 +614,7 @@ class Logger:
598
614
  """
599
615
  self.error(msg, *args, extra=extra, stacklevel=stacklevel)
600
616
 
601
- def info(self, msg: str, *args: tuple, extra: dict = None, stacklevel: int = None)-> None:
617
+ def info(self, msg: str, *args: tuple, extra: dict | None = None, stacklevel: int | None = None) -> None:
602
618
  """
603
619
  Log a message with severity INFO on this logger.
604
620
  Use stacklevel to show correctly the file and the line of the log, lvl 0 means the python
@@ -618,14 +634,27 @@ class Logger:
618
634
  Check if the slack message can be sent.
619
635
  We keep a track of the last message sent to avoid sending too many messages in less than 3 seconds.
620
636
  """
621
- now = DateTime.now(ZoneInfo('UTC')) # pylint: disable=no-member
622
- if self._slack_timer and (now - self._slack_timer).seconds < 2:
637
+ # We only send the message if we are in PROD and not in unittest
638
+ if settings.PROFILE != 'PROD' or 'unittest' in sys.modules:
639
+ return False
640
+
641
+ # We check if the last message was sent in less than 3 seconds to avoid sending too many messages
642
+ now = DateTime.now(ZoneInfo('UTC')) # pylint: disable=no-member
643
+ if self._slack_timer and (now - self._slack_timer).seconds < 3: # noqa: PLR2004
623
644
  return False
624
645
 
625
646
  self._slack_timer = now
626
647
  return True
627
648
 
628
- def slack(self, title: str, message: str, color: str, url: str = None, extra: dict = None, stacklevel: int = None) -> None:
649
+ def slack(
650
+ self,
651
+ title: str,
652
+ message: str,
653
+ color: str,
654
+ url: str | None = None,
655
+ extra: dict | None = None,
656
+ stacklevel: int | None = None,
657
+ ) -> None:
629
658
  """
630
659
  Send a message to a Slack channel using Slack WebHooks.
631
660
  https://api.slack.com/messaging/webhooks
@@ -646,14 +675,13 @@ class Logger:
646
675
  url = settings.SLACK_URL
647
676
 
648
677
  # We send the message only if url is set and we are in PROD and not in unittest
649
- if url and settings.PROFILE == 'PROD' and 'unittest' not in sys.modules:
650
- # We check if the last message was sent in less than 3 seconds to avoid sending too many messages
651
- if self._can_send_slack():
652
- # The import must be here to avoid circular import inside http module
653
- from everysk.core.slack import Slack # pylint: disable=import-outside-toplevel
654
- client = Slack(title=title, message=message, color=color, url=url)
655
- # This will send the message to Slack without block the request
656
- Thread(target=client.send).start()
678
+ if url and self._can_send_slack():
679
+ # The import must be here to avoid circular import inside http module
680
+ from everysk.core.slack import Slack # noqa: PLC0415
681
+
682
+ client = Slack(title=title, message=message, color=color, url=url)
683
+ # This will send the message to Slack without block the request
684
+ Thread(target=client.send).start()
657
685
 
658
686
  log_message = f'Slack message: {title} -> {message}'
659
687
  if color == 'danger':
@@ -665,7 +693,7 @@ class Logger:
665
693
  elif color == 'warning':
666
694
  self.warning(log_message, extra=extra, stacklevel=stacklevel)
667
695
 
668
- def warning(self, msg: str, *args: tuple, extra: dict = None, stacklevel: int = None)-> None:
696
+ def warning(self, msg: str, *args: tuple, extra: dict | None = None, stacklevel: int | None = None) -> None:
669
697
  """
670
698
  Log a message with severity WARNING on this logger.
671
699
  Use stacklevel to show correctly the file and the line of the log, lvl 0 means the python
@@ -116,12 +116,13 @@ except ModuleNotFoundError as error:
116
116
  from everysk.core._tests.log import (
117
117
  LoggerExtraDataTestCase as EveryskLibLoggerExtraDataTestCase,
118
118
  LoggerFormatterTestCase as EveryskLibLoggerFormatterTestCase,
119
+ LoggerJsonTestCase as EveryskLibLoggerJsonTestCase,
119
120
  LoggerManagerTestCase as EveryskLibLoggerManagerTestCase,
120
121
  LoggerMethodsTestCase as EveryskLibLoggerMethodsTestCase,
122
+ LoggerStackLevelTestCase as EveryskLibLoggerStackLevelTestCase,
123
+ LoggerStdoutTestCase as EveryskLibLoggerStdoutTestCase,
121
124
  LoggerTestCase as EveryskLibLoggerTestCase,
122
125
  LoggerTraceTestCase as EveryskLibLogTraceTestCase,
123
- LoggerStdoutTestCase as EveryskLibLoggerStdoutTestCase,
124
- LoggerJsonTestCase as EveryskLibLoggerJsonTestCase,
125
126
  )
126
127
  try:
127
128
  from everysk.core._tests.log import LoggerSlackTestCase as EveryskLibLoggerSlackTestCase # We need requests to run this test
@@ -0,0 +1,9 @@
1
+ ###############################################################################
2
+ #
3
+ # (C) Copyright 2025 EVERYSK TECHNOLOGIES
4
+ #
5
+ # This is an unpublished work containing confidential and proprietary
6
+ # information of EVERYSK TECHNOLOGIES. Disclosure, use, or reproduction
7
+ # without authorization of EVERYSK TECHNOLOGIES is prohibited.
8
+ #
9
+ ###############################################################################