hexkit 4.5.0__tar.gz → 4.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.
Files changed (64) hide show
  1. {hexkit-4.5.0/src/hexkit.egg-info → hexkit-4.6.0}/PKG-INFO +1 -1
  2. {hexkit-4.5.0 → hexkit-4.6.0}/pyproject.toml +2 -2
  3. hexkit-4.6.0/src/hexkit/opentelemetry_setup.py +170 -0
  4. {hexkit-4.5.0 → hexkit-4.6.0/src/hexkit.egg-info}/PKG-INFO +1 -1
  5. hexkit-4.5.0/src/hexkit/opentelemetry_setup.py +0 -63
  6. {hexkit-4.5.0 → hexkit-4.6.0}/LICENSE +0 -0
  7. {hexkit-4.5.0 → hexkit-4.6.0}/MANIFEST.in +0 -0
  8. {hexkit-4.5.0 → hexkit-4.6.0}/README.md +0 -0
  9. {hexkit-4.5.0 → hexkit-4.6.0}/setup.cfg +0 -0
  10. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/__init__.py +0 -0
  11. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/__main__.py +0 -0
  12. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/base.py +0 -0
  13. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/config.py +0 -0
  14. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/correlation.py +0 -0
  15. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/custom_types.py +0 -0
  16. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/log.py +0 -0
  17. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/protocols/__init__.py +0 -0
  18. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/protocols/dao.py +0 -0
  19. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/protocols/daopub.py +0 -0
  20. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/protocols/daosub.py +0 -0
  21. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/protocols/eventpub.py +0 -0
  22. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/protocols/eventsub.py +0 -0
  23. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/protocols/objstorage.py +0 -0
  24. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/__init__.py +0 -0
  25. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/akafka/__init__.py +0 -0
  26. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/akafka/config.py +0 -0
  27. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/akafka/provider/__init__.py +0 -0
  28. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/akafka/provider/daosub.py +0 -0
  29. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/akafka/provider/eventpub.py +0 -0
  30. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/akafka/provider/eventsub.py +0 -0
  31. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/akafka/provider/utils.py +0 -0
  32. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/akafka/testcontainer.py +0 -0
  33. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/akafka/testutils.py +0 -0
  34. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongodb/__init__.py +0 -0
  35. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongodb/migrations/__init__.py +0 -0
  36. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongodb/migrations/_manager.py +0 -0
  37. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongodb/migrations/_utils.py +0 -0
  38. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongodb/provider.py +0 -0
  39. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongodb/testutils.py +0 -0
  40. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongokafka/__init__.py +0 -0
  41. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongokafka/provider/__init__.py +0 -0
  42. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongokafka/provider/config.py +0 -0
  43. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongokafka/provider/daopub.py +0 -0
  44. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongokafka/provider/persistent_pub.py +0 -0
  45. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/mongokafka/testutils.py +0 -0
  46. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/__init__.py +0 -0
  47. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/provider.py +0 -0
  48. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/test_files/__init__.py +0 -0
  49. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/test_files/test_file1.yaml +0 -0
  50. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/test_files/test_file2.yaml +0 -0
  51. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/test_files/test_file3.yaml +0 -0
  52. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/test_files/test_file4.yaml +0 -0
  53. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/testutils/__init__.py +0 -0
  54. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/testutils/_fixtures.py +0 -0
  55. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/testutils/_typical_workflow.py +0 -0
  56. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/s3/testutils/_utils.py +0 -0
  57. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/testing/__init__.py +0 -0
  58. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/providers/testing/eventpub.py +0 -0
  59. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/py.typed +0 -0
  60. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit/utils.py +0 -0
  61. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit.egg-info/SOURCES.txt +0 -0
  62. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit.egg-info/dependency_links.txt +0 -0
  63. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit.egg-info/requires.txt +0 -0
  64. {hexkit-4.5.0 → hexkit-4.6.0}/src/hexkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hexkit
3
- Version: 4.5.0
3
+ Version: 4.6.0
4
4
  Summary: A Toolkit for Building Microservices using the Hexagonal Architecture
5
5
  Author-email: "German Human Genome Phenome Archive (GHGA)" <contact@ghga.de>
6
6
  License: Apache 2.0
@@ -1,6 +1,6 @@
1
1
  [build-system]
2
2
  requires = [
3
- "setuptools>=78.1",
3
+ "setuptools>=80.3",
4
4
  ]
5
5
  build-backend = "setuptools.build_meta"
6
6
 
@@ -24,7 +24,7 @@ classifiers = [
24
24
  "Intended Audience :: Developers",
25
25
  ]
26
26
  name = "hexkit"
27
- version = "4.5.0"
27
+ version = "4.6.0"
28
28
  description = "A Toolkit for Building Microservices using the Hexagonal Architecture"
29
29
  dependencies = [
30
30
  "opentelemetry-api >=1.31.1, <2",
@@ -0,0 +1,170 @@
1
+ # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
+ # for the German Human Genome-Phenome Archive (GHGA)
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """OpenTelemetry specific configuration code. This is gated behind the opentelemetry extra."""
16
+
17
+ import logging
18
+ import os
19
+ from typing import Annotated, Callable, Literal, Optional
20
+
21
+ from opentelemetry import trace
22
+ from opentelemetry.environment_variables import (
23
+ OTEL_LOGS_EXPORTER,
24
+ OTEL_METRICS_EXPORTER,
25
+ OTEL_TRACES_EXPORTER,
26
+ )
27
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
28
+ from opentelemetry.sdk.environment_variables import (
29
+ OTEL_EXPORTER_OTLP_PROTOCOL,
30
+ OTEL_SDK_DISABLED,
31
+ )
32
+ from opentelemetry.sdk.resources import SERVICE_NAME, Resource
33
+ from opentelemetry.sdk.trace import TracerProvider
34
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
35
+ from opentelemetry.sdk.trace.sampling import ParentBasedTraceIdRatio
36
+ from pydantic import Field
37
+ from pydantic_settings import BaseSettings
38
+ from typing_extensions import deprecated
39
+
40
+ TRACER: Optional["SpanTracer"] = None
41
+
42
+ logger = logging.getLogger(__name__)
43
+
44
+
45
+ class SpanTracer:
46
+ """Custom tracer class providing a decorator to autpopulate span names."""
47
+
48
+ def __init__(self, name):
49
+ self.tracer = trace.get_tracer(name)
50
+
51
+ def start_span(
52
+ self, *, record_exception: bool = False, set_status_on_exception: bool = False
53
+ ):
54
+ """Decorator function starting a span populated with the __qualname__ of the wrapped function"""
55
+
56
+ def outer_wrapper(function: Callable):
57
+ def traced_function(*args, **kwargs):
58
+ name = function.__qualname__
59
+ with self.tracer.start_as_current_span(
60
+ name,
61
+ record_exception=record_exception,
62
+ set_status_on_exception=set_status_on_exception,
63
+ ):
64
+ return function(*args, **kwargs)
65
+
66
+ return traced_function
67
+
68
+ return outer_wrapper
69
+
70
+
71
+ class OpenTelemetryConfig(BaseSettings):
72
+ """OpenTelemetry specific configuration options"""
73
+
74
+ enable_opentelemetry: bool = Field(
75
+ default=False,
76
+ description="If set to true, this will run necessary setup code."
77
+ "If set to false, environment variables are set that should also effectively "
78
+ "disable autoinstrumentation.",
79
+ )
80
+ samplig_rate: Annotated[float, Field(strict=True, ge=0, le=1)] = Field(
81
+ default=1.0,
82
+ description="Determines which proportion of spans should be sampled. "
83
+ "A value of 1.0 means all and is equivalent to the previous behaviour. "
84
+ "Setting this to 0 will result in no spans being sampled, but this does not "
85
+ "automatically set `enable_opentelemetry` to False.",
86
+ )
87
+ protocol: Literal["grpc", "http/protobuf"] = Field(
88
+ default="http/protobuf",
89
+ description="Specifies which protocol should be used by exporters.",
90
+ )
91
+
92
+
93
+ def configure_opentelemetry(*, service_name: str, config: OpenTelemetryConfig):
94
+ """Configure all needed parts of OpenTelemetry.
95
+
96
+ Setup of the TracerProvider is done programmatically, all other configuration exports
97
+ OpenTelemetry specific environment variables.
98
+ """
99
+ configure_tracer(service_name=service_name, config=config)
100
+
101
+
102
+ @deprecated(
103
+ "This function will be removed in v5. Use `configure_opentelemetry instead.`"
104
+ )
105
+ def configure_tracer(*, service_name: str, config: OpenTelemetryConfig):
106
+ """Configure all needed parts of OpenTelemetry.
107
+
108
+ Setup of the TracerProvider is done programmatically, all other configuration exports
109
+ OpenTelemetry specific environment variables.
110
+ """
111
+ global TRACER
112
+ # opentelemetry distro sets this to grpc, but in the current context http/protobuf is preferred
113
+ os.environ[OTEL_EXPORTER_OTLP_PROTOCOL] = config.protocol
114
+ # Disable OpenTelemetry metrics and logs explicitly as they are not processed in the backend currently
115
+ # This overwrites the defaults of `otlp` set in opentelemetry distro
116
+ os.environ[OTEL_METRICS_EXPORTER] = "none"
117
+ os.environ[OTEL_LOGS_EXPORTER] = "none"
118
+
119
+ if config.enable_opentelemetry:
120
+ if TRACER is not None:
121
+ logger.warning(
122
+ "OpenTelemetry configuration code should only be run once. "
123
+ "If it has been run with a different service name than %s before, "
124
+ "the tracer and resource name will likely be wrong in some cases.",
125
+ service_name,
126
+ )
127
+ resource = Resource(attributes={SERVICE_NAME: service_name})
128
+ # Replace the default static sampler with a probabilistic one that honors parent
129
+ # span sampling decisions
130
+ # This should consistently yield full traces within a service but not necessarily
131
+ # across service boundaries
132
+ # With the default sampling rate, behaviour does not change, but this allows to
133
+ # introduce head sampling by adjusting a config option on the service side later on
134
+ sampler = ParentBasedTraceIdRatio(rate=config.samplig_rate)
135
+
136
+ # Initialize service specific TracerProvider
137
+ trace_provider = TracerProvider(resource=resource, sampler=sampler)
138
+ processor = BatchSpanProcessor(OTLPSpanExporter())
139
+ trace_provider.add_span_processor(processor)
140
+ trace.set_tracer_provider(trace_provider)
141
+ TRACER = SpanTracer(service_name)
142
+ else:
143
+ # Currently OTEL_SDK_DISABLED doesn't seem to be honored by all implementations yet
144
+ # It seems to be working well enough for the Python implementation, but to be on
145
+ # the safe side, let's explicitly disable the trace exporter for now
146
+ os.environ[OTEL_TRACES_EXPORTER] = "none"
147
+ os.environ[OTEL_SDK_DISABLED] = "true"
148
+
149
+
150
+ def start_span(
151
+ *,
152
+ record_exception: bool = False,
153
+ set_status_on_exception: bool = False,
154
+ ) -> Callable:
155
+ """Returns decorated or undecorated function depending on if TRACER is instantiated.
156
+
157
+ Should be used as a decorator.
158
+ """
159
+
160
+ def wrapper(function: Callable):
161
+ # Caller did not have any time to populate from config yet or otel is disabled
162
+ if TRACER is None:
163
+ return function
164
+ # Return decorated function
165
+ return TRACER.start_span(
166
+ record_exception=record_exception,
167
+ set_status_on_exception=set_status_on_exception,
168
+ )(function)
169
+
170
+ return wrapper
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hexkit
3
- Version: 4.5.0
3
+ Version: 4.6.0
4
4
  Summary: A Toolkit for Building Microservices using the Hexagonal Architecture
5
5
  Author-email: "German Human Genome Phenome Archive (GHGA)" <contact@ghga.de>
6
6
  License: Apache 2.0
@@ -1,63 +0,0 @@
1
- # Copyright 2021 - 2025 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
2
- # for the German Human Genome-Phenome Archive (GHGA)
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
- """OpenTelemetry specific configuration code. This is gated behind the opentelemetry extra."""
16
-
17
- import os
18
- from typing import Callable
19
-
20
- from opentelemetry import trace
21
- from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
22
- from opentelemetry.sdk.environment_variables import OTEL_EXPORTER_OTLP_PROTOCOL
23
- from opentelemetry.sdk.resources import SERVICE_NAME, Resource
24
- from opentelemetry.sdk.trace import TracerProvider
25
- from opentelemetry.sdk.trace.export import BatchSpanProcessor
26
-
27
-
28
- def configure_tracer(service_name: str, protocol: str = "http/protobuf"):
29
- """Set up a global tracer for a specific service using the given exporter protocol."""
30
- # opentelemetry distro sets this to grpc, but in the current context http/protobuf is preferred
31
- os.environ.setdefault(OTEL_EXPORTER_OTLP_PROTOCOL, protocol)
32
-
33
- resource = Resource(attributes={SERVICE_NAME: service_name})
34
- trace_provider = TracerProvider(resource=resource)
35
- processor = BatchSpanProcessor(OTLPSpanExporter())
36
- trace_provider.add_span_processor(processor)
37
- trace.set_tracer_provider(trace_provider)
38
-
39
-
40
- class SpanTracer:
41
- """Custom tracer class providing a decorator to autpopulate span names."""
42
-
43
- def __init__(self, name):
44
- self.tracer = trace.get_tracer(name)
45
-
46
- def start_span(
47
- self, *, record_exception: bool = False, set_status_on_exception: bool = False
48
- ):
49
- """Decorator function starting a span populated with the __qualname__ of the wrapped function"""
50
-
51
- def outer_wrapper(function: Callable):
52
- def traced_function(*args, **kwargs):
53
- name = function.__qualname__
54
- with self.tracer.start_as_current_span(
55
- name,
56
- record_exception=record_exception,
57
- set_status_on_exception=set_status_on_exception,
58
- ):
59
- return function(*args, **kwargs)
60
-
61
- return traced_function
62
-
63
- return outer_wrapper
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