haiway 0.26.0__py3-none-any.whl → 0.27.0__py3-none-any.whl
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.
- haiway/__init__.py +10 -0
- haiway/context/__init__.py +2 -0
- haiway/context/access.py +4 -0
- haiway/context/observability.py +11 -1
- haiway/helpers/concurrent.py +8 -9
- haiway/helpers/files.py +4 -4
- haiway/helpers/observability.py +8 -2
- haiway/opentelemetry/observability.py +60 -14
- haiway/utils/__init__.py +12 -0
- haiway/utils/collections.py +31 -31
- haiway/utils/formatting.py +126 -36
- haiway/utils/metadata.py +480 -0
- {haiway-0.26.0.dist-info → haiway-0.27.0.dist-info}/METADATA +1 -1
- {haiway-0.26.0.dist-info → haiway-0.27.0.dist-info}/RECORD +16 -15
- {haiway-0.26.0.dist-info → haiway-0.27.0.dist-info}/WHEEL +0 -0
- {haiway-0.26.0.dist-info → haiway-0.27.0.dist-info}/licenses/LICENSE +0 -0
haiway/__init__.py
CHANGED
@@ -37,8 +37,13 @@ from haiway.types import (
|
|
37
37
|
unwrap_missing,
|
38
38
|
)
|
39
39
|
from haiway.utils import (
|
40
|
+
META_EMPTY,
|
40
41
|
AsyncQueue,
|
41
42
|
AsyncStream,
|
43
|
+
Meta,
|
44
|
+
MetaTags,
|
45
|
+
MetaValue,
|
46
|
+
MetaValues,
|
42
47
|
always,
|
43
48
|
as_dict,
|
44
49
|
as_list,
|
@@ -59,6 +64,7 @@ from haiway.utils import (
|
|
59
64
|
)
|
60
65
|
|
61
66
|
__all__ = (
|
67
|
+
"META_EMPTY",
|
62
68
|
"MISSING",
|
63
69
|
"AsyncQueue",
|
64
70
|
"AsyncStream",
|
@@ -73,6 +79,10 @@ __all__ = (
|
|
73
79
|
"FileAccess",
|
74
80
|
"Immutable",
|
75
81
|
"LoggerObservability",
|
82
|
+
"Meta",
|
83
|
+
"MetaTags",
|
84
|
+
"MetaValue",
|
85
|
+
"MetaValues",
|
76
86
|
"Missing",
|
77
87
|
"MissingContext",
|
78
88
|
"MissingState",
|
haiway/context/__init__.py
CHANGED
@@ -9,6 +9,7 @@ from haiway.context.observability import (
|
|
9
9
|
ObservabilityEventRecording,
|
10
10
|
ObservabilityLevel,
|
11
11
|
ObservabilityLogRecording,
|
12
|
+
ObservabilityMetricKind,
|
12
13
|
ObservabilityMetricRecording,
|
13
14
|
ObservabilityScopeEntering,
|
14
15
|
ObservabilityScopeExiting,
|
@@ -33,6 +34,7 @@ __all__ = (
|
|
33
34
|
"ObservabilityEventRecording",
|
34
35
|
"ObservabilityLevel",
|
35
36
|
"ObservabilityLogRecording",
|
37
|
+
"ObservabilityMetricKind",
|
36
38
|
"ObservabilityMetricRecording",
|
37
39
|
"ObservabilityScopeEntering",
|
38
40
|
"ObservabilityScopeExiting",
|
haiway/context/access.py
CHANGED
@@ -25,6 +25,7 @@ from haiway.context.observability import (
|
|
25
25
|
ObservabilityAttribute,
|
26
26
|
ObservabilityContext,
|
27
27
|
ObservabilityLevel,
|
28
|
+
ObservabilityMetricKind,
|
28
29
|
)
|
29
30
|
|
30
31
|
# Import after other imports to avoid circular dependencies
|
@@ -990,6 +991,7 @@ class ctx:
|
|
990
991
|
metric: str | None = None,
|
991
992
|
value: float | int | None = None,
|
992
993
|
unit: str | None = None,
|
994
|
+
kind: ObservabilityMetricKind | None = None,
|
993
995
|
attributes: Mapping[str, ObservabilityAttribute] | None = None,
|
994
996
|
) -> None:
|
995
997
|
if event is not None:
|
@@ -1003,11 +1005,13 @@ class ctx:
|
|
1003
1005
|
elif metric is not None:
|
1004
1006
|
assert event is None # nosec: B101
|
1005
1007
|
assert value is not None # nosec: B101
|
1008
|
+
assert kind is not None # nosec: B101
|
1006
1009
|
ObservabilityContext.record_metric(
|
1007
1010
|
level,
|
1008
1011
|
metric,
|
1009
1012
|
value=value,
|
1010
1013
|
unit=unit,
|
1014
|
+
kind=kind,
|
1011
1015
|
attributes=attributes or {},
|
1012
1016
|
)
|
1013
1017
|
|
haiway/context/observability.py
CHANGED
@@ -7,7 +7,7 @@ from logging import INFO as INFO_LOGGING
|
|
7
7
|
from logging import WARNING as WARNING_LOGGING
|
8
8
|
from logging import Logger, getLogger
|
9
9
|
from types import TracebackType
|
10
|
-
from typing import Any, ClassVar, Protocol, Self, runtime_checkable
|
10
|
+
from typing import Any, ClassVar, Literal, Protocol, Self, runtime_checkable
|
11
11
|
from uuid import UUID, uuid4
|
12
12
|
|
13
13
|
from haiway.context.identifier import ScopeIdentifier
|
@@ -23,6 +23,7 @@ __all__ = (
|
|
23
23
|
"ObservabilityEventRecording",
|
24
24
|
"ObservabilityLevel",
|
25
25
|
"ObservabilityLogRecording",
|
26
|
+
"ObservabilityMetricKind",
|
26
27
|
"ObservabilityMetricRecording",
|
27
28
|
"ObservabilityScopeEntering",
|
28
29
|
"ObservabilityScopeExiting",
|
@@ -118,6 +119,9 @@ class ObservabilityEventRecording(Protocol):
|
|
118
119
|
) -> None: ...
|
119
120
|
|
120
121
|
|
122
|
+
type ObservabilityMetricKind = Literal["counter", "histogram", "gauge"]
|
123
|
+
|
124
|
+
|
121
125
|
@runtime_checkable
|
122
126
|
class ObservabilityMetricRecording(Protocol):
|
123
127
|
"""
|
@@ -136,6 +140,7 @@ class ObservabilityMetricRecording(Protocol):
|
|
136
140
|
metric: str,
|
137
141
|
value: float | int,
|
138
142
|
unit: str | None,
|
143
|
+
kind: ObservabilityMetricKind,
|
139
144
|
attributes: Mapping[str, ObservabilityAttribute],
|
140
145
|
) -> None: ...
|
141
146
|
|
@@ -278,6 +283,7 @@ def _logger_observability(
|
|
278
283
|
metric: str,
|
279
284
|
value: float | int,
|
280
285
|
unit: str | None,
|
286
|
+
kind: ObservabilityMetricKind,
|
281
287
|
attributes: Mapping[str, ObservabilityAttribute],
|
282
288
|
) -> None:
|
283
289
|
if attributes:
|
@@ -553,6 +559,7 @@ class ObservabilityContext(Immutable):
|
|
553
559
|
*,
|
554
560
|
value: float | int,
|
555
561
|
unit: str | None,
|
562
|
+
kind: ObservabilityMetricKind,
|
556
563
|
attributes: Mapping[str, ObservabilityAttribute],
|
557
564
|
) -> None:
|
558
565
|
"""
|
@@ -571,6 +578,8 @@ class ObservabilityContext(Immutable):
|
|
571
578
|
The numeric value of the metric
|
572
579
|
unit: str | None
|
573
580
|
Optional unit for the metric (e.g., "ms", "bytes")
|
581
|
+
kind: ObservabilityMetricKind
|
582
|
+
The metric kind defining its value handling.
|
574
583
|
attributes: Mapping[str, ObservabilityAttribute]
|
575
584
|
Key-value attributes associated with the metric
|
576
585
|
"""
|
@@ -584,6 +593,7 @@ class ObservabilityContext(Immutable):
|
|
584
593
|
metric=metric,
|
585
594
|
value=value,
|
586
595
|
unit=unit,
|
596
|
+
kind=kind,
|
587
597
|
attributes=attributes,
|
588
598
|
)
|
589
599
|
|
haiway/helpers/concurrent.py
CHANGED
@@ -3,8 +3,8 @@ from collections.abc import (
|
|
3
3
|
AsyncIterable,
|
4
4
|
AsyncIterator,
|
5
5
|
Callable,
|
6
|
-
Collection,
|
7
6
|
Coroutine,
|
7
|
+
Iterable,
|
8
8
|
MutableSequence,
|
9
9
|
Sequence,
|
10
10
|
)
|
@@ -20,7 +20,7 @@ __all__ = (
|
|
20
20
|
|
21
21
|
|
22
22
|
async def process_concurrently[Element]( # noqa: C901, PLR0912
|
23
|
-
source:
|
23
|
+
source: AsyncIterable[Element],
|
24
24
|
/,
|
25
25
|
handler: Callable[[Element], Coroutine[Any, Any, None]],
|
26
26
|
*,
|
@@ -80,8 +80,7 @@ async def process_concurrently[Element]( # noqa: C901, PLR0912
|
|
80
80
|
assert concurrent_tasks > 0 # nosec: B101
|
81
81
|
running: set[Task[None]] = set()
|
82
82
|
try:
|
83
|
-
|
84
|
-
element: Element = await anext(source)
|
83
|
+
async for element in source:
|
85
84
|
running.add(ctx.spawn(handler, element))
|
86
85
|
if len(running) < concurrent_tasks:
|
87
86
|
continue # keep spawning tasks
|
@@ -132,7 +131,7 @@ async def process_concurrently[Element]( # noqa: C901, PLR0912
|
|
132
131
|
async def execute_concurrently[Element, Result](
|
133
132
|
handler: Callable[[Element], Coroutine[Any, Any, Result]],
|
134
133
|
/,
|
135
|
-
elements:
|
134
|
+
elements: Iterable[Element],
|
136
135
|
*,
|
137
136
|
concurrent_tasks: int = 2,
|
138
137
|
) -> Sequence[Result]: ...
|
@@ -142,7 +141,7 @@ async def execute_concurrently[Element, Result](
|
|
142
141
|
async def execute_concurrently[Element, Result](
|
143
142
|
handler: Callable[[Element], Coroutine[Any, Any, Result]],
|
144
143
|
/,
|
145
|
-
elements:
|
144
|
+
elements: Iterable[Element],
|
146
145
|
*,
|
147
146
|
concurrent_tasks: int = 2,
|
148
147
|
return_exceptions: Literal[True],
|
@@ -152,7 +151,7 @@ async def execute_concurrently[Element, Result](
|
|
152
151
|
async def execute_concurrently[Element, Result]( # noqa: C901
|
153
152
|
handler: Callable[[Element], Coroutine[Any, Any, Result]],
|
154
153
|
/,
|
155
|
-
elements:
|
154
|
+
elements: Iterable[Element],
|
156
155
|
*,
|
157
156
|
concurrent_tasks: int = 2,
|
158
157
|
return_exceptions: bool = False,
|
@@ -175,8 +174,8 @@ async def execute_concurrently[Element, Result]( # noqa: C901
|
|
175
174
|
----------
|
176
175
|
handler : Callable[[Element], Coroutine[Any, Any, Result]]
|
177
176
|
A coroutine function that processes each element and returns a result.
|
178
|
-
elements :
|
179
|
-
A
|
177
|
+
elements : Iterable[Element]
|
178
|
+
A source of elements to process. The source size determines
|
180
179
|
the result sequence length.
|
181
180
|
concurrent_tasks : int, default=2
|
182
181
|
Maximum number of concurrent tasks. Must be greater than 0. Higher
|
haiway/helpers/files.py
CHANGED
@@ -188,7 +188,7 @@ class FileAccessing(Protocol):
|
|
188
188
|
locking, and resource management.
|
189
189
|
"""
|
190
190
|
|
191
|
-
|
191
|
+
def __call__(
|
192
192
|
self,
|
193
193
|
path: Path | str,
|
194
194
|
create: bool,
|
@@ -275,7 +275,7 @@ def _close_file_handle(
|
|
275
275
|
os.close(file_handle)
|
276
276
|
|
277
277
|
|
278
|
-
|
278
|
+
def _file_access_context(
|
279
279
|
path: Path | str,
|
280
280
|
create: bool,
|
281
281
|
exclusive: bool,
|
@@ -375,7 +375,7 @@ class FileAccess(State):
|
|
375
375
|
"""
|
376
376
|
|
377
377
|
@classmethod
|
378
|
-
|
378
|
+
def open(
|
379
379
|
cls,
|
380
380
|
path: Path | str,
|
381
381
|
create: bool = False,
|
@@ -412,7 +412,7 @@ class FileAccess(State):
|
|
412
412
|
If the file cannot be opened with the specified parameters, or if
|
413
413
|
a file is already open in the current context scope
|
414
414
|
"""
|
415
|
-
return
|
415
|
+
return ctx.state(cls).accessing(
|
416
416
|
path,
|
417
417
|
create=create,
|
418
418
|
exclusive=exclusive,
|
haiway/helpers/observability.py
CHANGED
@@ -4,8 +4,13 @@ from time import monotonic
|
|
4
4
|
from typing import Any
|
5
5
|
from uuid import UUID, uuid4
|
6
6
|
|
7
|
-
from haiway.context import
|
8
|
-
|
7
|
+
from haiway.context import (
|
8
|
+
Observability,
|
9
|
+
ObservabilityAttribute,
|
10
|
+
ObservabilityLevel,
|
11
|
+
ObservabilityMetricKind,
|
12
|
+
ScopeIdentifier,
|
13
|
+
)
|
9
14
|
from haiway.utils.formatting import format_str
|
10
15
|
|
11
16
|
__all__ = ("LoggerObservability",)
|
@@ -189,6 +194,7 @@ def LoggerObservability( # noqa: C901, PLR0915
|
|
189
194
|
metric: str,
|
190
195
|
value: float | int,
|
191
196
|
unit: str | None,
|
197
|
+
kind: ObservabilityMetricKind,
|
192
198
|
attributes: Mapping[str, ObservabilityAttribute],
|
193
199
|
) -> None:
|
194
200
|
assert root_scope is not None # nosec: B101
|
@@ -12,7 +12,7 @@ from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
|
|
12
12
|
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
|
13
13
|
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
14
14
|
from opentelemetry.metrics._internal import Meter
|
15
|
-
from opentelemetry.metrics._internal.instrument import Counter
|
15
|
+
from opentelemetry.metrics._internal.instrument import Counter, Gauge, Histogram
|
16
16
|
from opentelemetry.sdk._logs import LoggerProvider
|
17
17
|
from opentelemetry.sdk._logs._internal import LogRecord
|
18
18
|
from opentelemetry.sdk._logs._internal.export import (
|
@@ -33,6 +33,7 @@ from haiway.context import (
|
|
33
33
|
Observability,
|
34
34
|
ObservabilityAttribute,
|
35
35
|
ObservabilityLevel,
|
36
|
+
ObservabilityMetricKind,
|
36
37
|
ScopeIdentifier,
|
37
38
|
ctx,
|
38
39
|
)
|
@@ -54,6 +55,8 @@ class ScopeStore:
|
|
54
55
|
"_completed",
|
55
56
|
"_counters",
|
56
57
|
"_exited",
|
58
|
+
"_gauges",
|
59
|
+
"_histograms",
|
57
60
|
"_token",
|
58
61
|
"context",
|
59
62
|
"identifier",
|
@@ -91,6 +94,8 @@ class ScopeStore:
|
|
91
94
|
self.identifier: ScopeIdentifier = identifier
|
92
95
|
self.nested: list[ScopeStore] = []
|
93
96
|
self._counters: dict[str, Counter] = {}
|
97
|
+
self._histograms: dict[str, Histogram] = {}
|
98
|
+
self._gauges: dict[str, Gauge] = {}
|
94
99
|
self._exited: bool = False
|
95
100
|
self._completed: bool = False
|
96
101
|
self.span: Span = span
|
@@ -251,6 +256,7 @@ class ScopeStore:
|
|
251
256
|
*,
|
252
257
|
value: float | int,
|
253
258
|
unit: str | None,
|
259
|
+
kind: ObservabilityMetricKind,
|
254
260
|
attributes: Mapping[str, ObservabilityAttribute],
|
255
261
|
) -> None:
|
256
262
|
"""
|
@@ -267,23 +273,59 @@ class ScopeStore:
|
|
267
273
|
The value to add to the metric
|
268
274
|
unit : str | None
|
269
275
|
The unit of the metric (if any)
|
276
|
+
kind: ObservabilityMetricKind
|
277
|
+
The metric kind defining its value handling.
|
270
278
|
attributes : Mapping[str, ObservabilityAttribute]
|
271
279
|
Attributes to attach to the metric
|
272
280
|
"""
|
273
|
-
|
274
|
-
|
275
|
-
name
|
276
|
-
|
277
|
-
|
281
|
+
match kind:
|
282
|
+
case "counter":
|
283
|
+
if name not in self._counters:
|
284
|
+
self._counters[name] = self.meter.create_counter(
|
285
|
+
name=name,
|
286
|
+
unit=unit or "",
|
287
|
+
)
|
288
|
+
|
289
|
+
self._counters[name].add(
|
290
|
+
value,
|
291
|
+
attributes={
|
292
|
+
key: cast(Any, value)
|
293
|
+
for key, value in attributes.items()
|
294
|
+
if value is not None and value is not MISSING
|
295
|
+
},
|
296
|
+
)
|
278
297
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
298
|
+
case "histogram":
|
299
|
+
if name not in self._histograms:
|
300
|
+
self._histograms[name] = self.meter.create_histogram(
|
301
|
+
name=name,
|
302
|
+
unit=unit or "",
|
303
|
+
)
|
304
|
+
|
305
|
+
self._histograms[name].record(
|
306
|
+
value,
|
307
|
+
attributes={
|
308
|
+
key: cast(Any, value)
|
309
|
+
for key, value in attributes.items()
|
310
|
+
if value is not None and value is not MISSING
|
311
|
+
},
|
312
|
+
)
|
313
|
+
|
314
|
+
case "gauge":
|
315
|
+
if name not in self._gauges:
|
316
|
+
self._gauges[name] = self.meter.create_gauge(
|
317
|
+
name=name,
|
318
|
+
unit=unit or "",
|
319
|
+
)
|
320
|
+
|
321
|
+
self._gauges[name].set(
|
322
|
+
value,
|
323
|
+
attributes={
|
324
|
+
key: cast(Any, value)
|
325
|
+
for key, value in attributes.items()
|
326
|
+
if value is not None and value is not MISSING
|
327
|
+
},
|
328
|
+
)
|
287
329
|
|
288
330
|
def record_attributes(
|
289
331
|
self,
|
@@ -589,6 +631,7 @@ class OpenTelemetry:
|
|
589
631
|
metric: str,
|
590
632
|
value: float | int,
|
591
633
|
unit: str | None,
|
634
|
+
kind: ObservabilityMetricKind,
|
592
635
|
attributes: Mapping[str, ObservabilityAttribute],
|
593
636
|
) -> None:
|
594
637
|
"""
|
@@ -609,6 +652,8 @@ class OpenTelemetry:
|
|
609
652
|
The numeric value of the metric
|
610
653
|
unit: str | None
|
611
654
|
Optional unit for the metric (e.g., "ms", "bytes")
|
655
|
+
kind: ObservabilityMetricKind
|
656
|
+
The metric kind defining its value handling.
|
612
657
|
attributes: Mapping[str, ObservabilityAttribute]
|
613
658
|
Key-value attributes associated with the metric
|
614
659
|
"""
|
@@ -622,6 +667,7 @@ class OpenTelemetry:
|
|
622
667
|
metric,
|
623
668
|
value=value,
|
624
669
|
unit=unit,
|
670
|
+
kind=kind,
|
625
671
|
attributes=attributes,
|
626
672
|
)
|
627
673
|
|
haiway/utils/__init__.py
CHANGED
@@ -11,14 +11,26 @@ from haiway.utils.env import (
|
|
11
11
|
)
|
12
12
|
from haiway.utils.formatting import format_str
|
13
13
|
from haiway.utils.logs import setup_logging
|
14
|
+
from haiway.utils.metadata import (
|
15
|
+
META_EMPTY,
|
16
|
+
Meta,
|
17
|
+
MetaTags,
|
18
|
+
MetaValue,
|
19
|
+
MetaValues,
|
20
|
+
)
|
14
21
|
from haiway.utils.mimic import mimic_function
|
15
22
|
from haiway.utils.noop import async_noop, noop
|
16
23
|
from haiway.utils.queue import AsyncQueue
|
17
24
|
from haiway.utils.stream import AsyncStream
|
18
25
|
|
19
26
|
__all__ = (
|
27
|
+
"META_EMPTY",
|
20
28
|
"AsyncQueue",
|
21
29
|
"AsyncStream",
|
30
|
+
"Meta",
|
31
|
+
"MetaTags",
|
32
|
+
"MetaValue",
|
33
|
+
"MetaValues",
|
22
34
|
"always",
|
23
35
|
"as_dict",
|
24
36
|
"as_list",
|
haiway/utils/collections.py
CHANGED
@@ -14,20 +14,20 @@ __all__ = (
|
|
14
14
|
|
15
15
|
@overload
|
16
16
|
def as_list[T](
|
17
|
-
|
17
|
+
iterable: Iterable[T],
|
18
18
|
/,
|
19
19
|
) -> list[T]: ...
|
20
20
|
|
21
21
|
|
22
22
|
@overload
|
23
23
|
def as_list[T](
|
24
|
-
|
24
|
+
iterable: Iterable[T] | None,
|
25
25
|
/,
|
26
26
|
) -> list[T] | None: ...
|
27
27
|
|
28
28
|
|
29
29
|
def as_list[T](
|
30
|
-
|
30
|
+
iterable: Iterable[T] | None,
|
31
31
|
/,
|
32
32
|
) -> list[T] | None:
|
33
33
|
"""
|
@@ -35,44 +35,44 @@ def as_list[T](
|
|
35
35
|
|
36
36
|
Parameters
|
37
37
|
----------
|
38
|
-
|
39
|
-
The input
|
38
|
+
iterable : Iterable[T] | None
|
39
|
+
The input iterable to be converted to a list.
|
40
40
|
If None is provided, None is returned.
|
41
41
|
|
42
42
|
Returns
|
43
43
|
-------
|
44
44
|
list[T] | None
|
45
|
-
A new list containing all elements of the input
|
45
|
+
A new list containing all elements of the input iterable,
|
46
46
|
or the original list if it was already one.
|
47
47
|
Returns None if None was provided.
|
48
48
|
"""
|
49
49
|
|
50
|
-
if
|
50
|
+
if iterable is None:
|
51
51
|
return None
|
52
52
|
|
53
|
-
elif isinstance(
|
54
|
-
return
|
53
|
+
elif isinstance(iterable, list):
|
54
|
+
return iterable
|
55
55
|
|
56
56
|
else:
|
57
|
-
return list(
|
57
|
+
return list(iterable)
|
58
58
|
|
59
59
|
|
60
60
|
@overload
|
61
61
|
def as_tuple[T](
|
62
|
-
|
62
|
+
iterable: Iterable[T],
|
63
63
|
/,
|
64
64
|
) -> tuple[T, ...]: ...
|
65
65
|
|
66
66
|
|
67
67
|
@overload
|
68
68
|
def as_tuple[T](
|
69
|
-
|
69
|
+
iterable: Iterable[T] | None,
|
70
70
|
/,
|
71
71
|
) -> tuple[T, ...] | None: ...
|
72
72
|
|
73
73
|
|
74
74
|
def as_tuple[T](
|
75
|
-
|
75
|
+
iterable: Iterable[T] | None,
|
76
76
|
/,
|
77
77
|
) -> tuple[T, ...] | None:
|
78
78
|
"""
|
@@ -80,26 +80,26 @@ def as_tuple[T](
|
|
80
80
|
|
81
81
|
Parameters
|
82
82
|
----------
|
83
|
-
|
84
|
-
The input
|
83
|
+
iterable : Iterable[T] | None
|
84
|
+
The input iterable to be converted to a tuple.
|
85
85
|
If None is provided, None is returned.
|
86
86
|
|
87
87
|
Returns
|
88
88
|
-------
|
89
89
|
tuple[T, ...] | None
|
90
|
-
A new tuple containing all elements of the input
|
90
|
+
A new tuple containing all elements of the input iterable,
|
91
91
|
or the original tuple if it was already one.
|
92
92
|
Returns None if None was provided.
|
93
93
|
"""
|
94
94
|
|
95
|
-
if
|
95
|
+
if iterable is None:
|
96
96
|
return None
|
97
97
|
|
98
|
-
elif isinstance(
|
99
|
-
return
|
98
|
+
elif isinstance(iterable, tuple):
|
99
|
+
return iterable
|
100
100
|
|
101
101
|
else:
|
102
|
-
return tuple(
|
102
|
+
return tuple(iterable)
|
103
103
|
|
104
104
|
|
105
105
|
@overload
|
@@ -149,20 +149,20 @@ def as_set[T](
|
|
149
149
|
|
150
150
|
@overload
|
151
151
|
def as_dict[K, V](
|
152
|
-
|
152
|
+
mapping: Mapping[K, V],
|
153
153
|
/,
|
154
154
|
) -> dict[K, V]: ...
|
155
155
|
|
156
156
|
|
157
157
|
@overload
|
158
158
|
def as_dict[K, V](
|
159
|
-
|
159
|
+
mapping: Mapping[K, V] | None,
|
160
160
|
/,
|
161
161
|
) -> dict[K, V] | None: ...
|
162
162
|
|
163
163
|
|
164
164
|
def as_dict[K, V](
|
165
|
-
|
165
|
+
mapping: Mapping[K, V] | None,
|
166
166
|
/,
|
167
167
|
) -> dict[K, V] | None:
|
168
168
|
"""
|
@@ -170,30 +170,30 @@ def as_dict[K, V](
|
|
170
170
|
|
171
171
|
Parameters
|
172
172
|
----------
|
173
|
-
|
174
|
-
The input
|
173
|
+
mapping : Mapping[K, V] | None
|
174
|
+
The input mapping to be converted to a dict.
|
175
175
|
If None is provided, None is returned.
|
176
176
|
|
177
177
|
Returns
|
178
178
|
-------
|
179
179
|
dict[K, V] | None
|
180
|
-
A new dict containing all elements of the input
|
180
|
+
A new dict containing all elements of the input mapping,
|
181
181
|
or the original dict if it was already one.
|
182
182
|
Returns None if None was provided.
|
183
183
|
"""
|
184
184
|
|
185
|
-
if
|
185
|
+
if mapping is None:
|
186
186
|
return None
|
187
187
|
|
188
|
-
elif isinstance(
|
189
|
-
return
|
188
|
+
elif isinstance(mapping, dict):
|
189
|
+
return mapping
|
190
190
|
|
191
191
|
else:
|
192
|
-
return dict(
|
192
|
+
return dict(mapping)
|
193
193
|
|
194
194
|
|
195
195
|
@overload
|
196
|
-
def without_missing
|
196
|
+
def without_missing(
|
197
197
|
mapping: Mapping[str, Any],
|
198
198
|
/,
|
199
199
|
) -> Mapping[str, Any]: ...
|