haiway 0.20.1__py3-none-any.whl → 0.21.1__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 CHANGED
@@ -5,15 +5,8 @@ from haiway.context import (
5
5
  MissingState,
6
6
  Observability,
7
7
  ObservabilityAttribute,
8
- ObservabilityAttributesRecording,
9
8
  ObservabilityContext,
10
- ObservabilityEventRecording,
11
9
  ObservabilityLevel,
12
- ObservabilityLogRecording,
13
- ObservabilityMetricRecording,
14
- ObservabilityScopeEntering,
15
- ObservabilityScopeExiting,
16
- ObservabilityTraceIdentifying,
17
10
  ScopeContext,
18
11
  ScopeIdentifier,
19
12
  StateContext,
@@ -28,7 +21,6 @@ from haiway.helpers import (
28
21
  throttle,
29
22
  timeout,
30
23
  traced,
31
- wrap_async,
32
24
  )
33
25
  from haiway.state import AttributePath, AttributeRequirement, State
34
26
  from haiway.types import (
@@ -36,7 +28,6 @@ from haiway.types import (
36
28
  Default,
37
29
  DefaultValue,
38
30
  Missing,
39
- frozenlist,
40
31
  is_missing,
41
32
  not_missing,
42
33
  when_missing,
@@ -51,14 +42,12 @@ from haiway.utils import (
51
42
  as_tuple,
52
43
  async_always,
53
44
  async_noop,
54
- freeze,
55
45
  getenv_base64,
56
46
  getenv_bool,
57
47
  getenv_float,
58
48
  getenv_int,
59
49
  getenv_str,
60
50
  load_env,
61
- mimic_function,
62
51
  noop,
63
52
  setup_logging,
64
53
  without_missing,
@@ -80,15 +69,8 @@ __all__ = (
80
69
  "MissingState",
81
70
  "Observability",
82
71
  "ObservabilityAttribute",
83
- "ObservabilityAttributesRecording",
84
72
  "ObservabilityContext",
85
- "ObservabilityEventRecording",
86
73
  "ObservabilityLevel",
87
- "ObservabilityLogRecording",
88
- "ObservabilityMetricRecording",
89
- "ObservabilityScopeEntering",
90
- "ObservabilityScopeExiting",
91
- "ObservabilityTraceIdentifying",
92
74
  "ScopeContext",
93
75
  "ScopeIdentifier",
94
76
  "State",
@@ -103,8 +85,6 @@ __all__ = (
103
85
  "asynchronous",
104
86
  "cache",
105
87
  "ctx",
106
- "freeze",
107
- "frozenlist",
108
88
  "getenv_base64",
109
89
  "getenv_bool",
110
90
  "getenv_float",
@@ -112,7 +92,6 @@ __all__ = (
112
92
  "getenv_str",
113
93
  "is_missing",
114
94
  "load_env",
115
- "mimic_function",
116
95
  "noop",
117
96
  "not_missing",
118
97
  "process_concurrently",
@@ -123,5 +102,4 @@ __all__ = (
123
102
  "traced",
124
103
  "when_missing",
125
104
  "without_missing",
126
- "wrap_async",
127
105
  )
@@ -1,4 +1,4 @@
1
- from haiway.helpers.asynchrony import asynchronous, wrap_async
1
+ from haiway.helpers.asynchrony import asynchronous
2
2
  from haiway.helpers.caching import CacheMakeKey, CacheRead, CacheWrite, cache
3
3
  from haiway.helpers.concurrent import process_concurrently
4
4
  from haiway.helpers.observability import LoggerObservability
@@ -19,5 +19,4 @@ __all__ = (
19
19
  "throttle",
20
20
  "timeout",
21
21
  "traced",
22
- "wrap_async",
23
22
  )
@@ -7,51 +7,16 @@ from typing import Any, cast, overload
7
7
 
8
8
  from haiway.types.missing import MISSING, Missing
9
9
 
10
- __all__ = (
11
- "asynchronous",
12
- "wrap_async",
13
- )
14
-
15
-
16
- def wrap_async[**Args, Result](
17
- function: Callable[Args, Coroutine[Any, Any, Result]] | Callable[Args, Result],
18
- /,
19
- ) -> Callable[Args, Coroutine[Any, Any, Result]]:
20
- """
21
- Convert a synchronous function to an asynchronous one if it isn't already.
22
-
23
- Takes a function that may be either synchronous or asynchronous and ensures it
24
- returns a coroutine. If the input function is already asynchronous, it is returned
25
- unchanged. If it's synchronous, it wraps it in an async function that executes
26
- the original function and returns its result.
27
-
28
- Parameters
29
- ----------
30
- function: Callable[Args, Coroutine[Any, Any, Result]] | Callable[Args, Result]
31
- The function to ensure is asynchronous, can be either sync or async
32
-
33
- Returns
34
- -------
35
- Callable[Args, Coroutine[Any, Any, Result]]
36
- An asynchronous function that returns a coroutine
37
- """
38
- if iscoroutinefunction(function):
39
- return function
40
-
41
- else:
42
-
43
- async def async_function(*args: Args.args, **kwargs: Args.kwargs) -> Result:
44
- return cast(Callable[Args, Result], function)(*args, **kwargs)
45
-
46
- _mimic_async(function, within=async_function)
47
- return async_function
10
+ __all__ = ("asynchronous",)
48
11
 
49
12
 
50
13
  @overload
51
- def asynchronous[**Args, Result]() -> Callable[
52
- [Callable[Args, Result]],
53
- Callable[Args, Coroutine[Any, Any, Result]],
54
- ]: ...
14
+ def asynchronous[**Args, Result]() -> (
15
+ Callable[
16
+ [Callable[Args, Result]],
17
+ Callable[Args, Coroutine[Any, Any, Result]],
18
+ ]
19
+ ): ...
55
20
 
56
21
 
57
22
  @overload
@@ -142,11 +107,18 @@ def asynchronous[**Args, Result](
142
107
  ) -> Callable[Args, Coroutine[Any, Any, Result]]:
143
108
  assert not iscoroutinefunction(wrapped), "Cannot wrap async function in executor" # nosec: B101
144
109
 
145
- return _ExecutorWrapper(
146
- wrapped,
147
- loop=loop,
148
- executor=cast(Executor | None, None if executor is MISSING else executor),
149
- )
110
+ async def asynchronous(
111
+ *args: Args.args,
112
+ **kwargs: Args.kwargs,
113
+ ) -> Result:
114
+ context: Context = copy_context()
115
+ return await (loop or get_running_loop()).run_in_executor(
116
+ cast(Executor | None, None if executor is MISSING else executor),
117
+ context.run,
118
+ partial(wrapped, *args, **kwargs),
119
+ )
120
+
121
+ return _mimic_async(wrapped, within=asynchronous)
150
122
 
151
123
  if function := function:
152
124
  return wrap(wrapped=function)
@@ -155,77 +127,6 @@ def asynchronous[**Args, Result](
155
127
  return wrap
156
128
 
157
129
 
158
- class _ExecutorWrapper[**Args, Result]:
159
- __slots__ = (
160
- "__annotations__",
161
- "__defaults__",
162
- "__doc__",
163
- "__globals__",
164
- "__kwdefaults__",
165
- "__name__",
166
- "__qualname__",
167
- "__wrapped__",
168
- "_executor",
169
- "_function",
170
- "_loop",
171
- )
172
-
173
- def __init__(
174
- self,
175
- function: Callable[Args, Result],
176
- /,
177
- loop: AbstractEventLoop | None,
178
- executor: Executor | None,
179
- ) -> None:
180
- self._function: Callable[Args, Result] = function
181
- self._loop: AbstractEventLoop | None = loop
182
- self._executor: Executor | None = executor
183
-
184
- # mimic function attributes if able
185
- _mimic_async(function, within=self)
186
-
187
- async def __call__(
188
- self,
189
- *args: Args.args,
190
- **kwargs: Args.kwargs,
191
- ) -> Result:
192
- context: Context = copy_context()
193
- return await (self._loop or get_running_loop()).run_in_executor(
194
- self._executor,
195
- context.run,
196
- partial(self._function, *args, **kwargs),
197
- )
198
-
199
- def __get__(
200
- self,
201
- instance: object,
202
- owner: type | None = None,
203
- /,
204
- ) -> Callable[Args, Coroutine[Any, Any, Result]]:
205
- if owner is None:
206
- return self
207
-
208
- else:
209
- return _mimic_async(
210
- self._function,
211
- within=partial(
212
- self.__method_call__,
213
- instance,
214
- ),
215
- )
216
-
217
- async def __method_call__(
218
- self,
219
- __method_self: object,
220
- *args: Args.args,
221
- **kwargs: Args.kwargs,
222
- ) -> Result:
223
- return await (self._loop or get_running_loop()).run_in_executor(
224
- self._executor,
225
- partial(self._function, __method_self, *args, **kwargs),
226
- )
227
-
228
-
229
130
  def _mimic_async[**Args, Result](
230
131
  function: Callable[Args, Result],
231
132
  /,
@@ -236,7 +137,7 @@ def _mimic_async[**Args, Result](
236
137
  function,
237
138
  "__annotations__",
238
139
  )
239
- setattr( # noqa: B010
140
+ object.__setattr__(
240
141
  within,
241
142
  "__annotations__",
242
143
  {
@@ -259,7 +160,7 @@ def _mimic_async[**Args, Result](
259
160
  "__globals__",
260
161
  ):
261
162
  try:
262
- setattr(
163
+ object.__setattr__(
263
164
  within,
264
165
  attribute,
265
166
  getattr(
@@ -276,7 +177,7 @@ def _mimic_async[**Args, Result](
276
177
  except AttributeError:
277
178
  pass
278
179
 
279
- setattr( # noqa: B010 - mimic functools.wraps behavior for correct signature checks
180
+ object.__setattr__( # mimic functools.wraps behavior for correct signature checks
280
181
  within,
281
182
  "__wrapped__",
282
183
  function,
@@ -7,7 +7,7 @@ from collections import deque
7
7
  from collections.abc import Callable, Coroutine
8
8
  from datetime import timedelta
9
9
  from time import monotonic
10
- from typing import Any, cast, overload
10
+ from typing import Any, overload
11
11
 
12
12
  from haiway.utils.mimic import mimic_function
13
13
 
@@ -97,14 +97,38 @@ def throttle[**Args, Result](
97
97
  function: Callable[Args, Coroutine[Any, Any, Result]],
98
98
  ) -> Callable[Args, Coroutine[Any, Any, Result]]:
99
99
  assert iscoroutinefunction(function) # nosec: B101
100
- return cast(
101
- Callable[Args, Coroutine[Any, Any, Result]],
102
- _AsyncThrottle(
103
- function,
104
- limit=limit,
105
- period=period,
106
- ),
107
- )
100
+ entries: deque[float] = deque()
101
+ lock: Lock = Lock()
102
+ throttle_period: float
103
+ match period:
104
+ case timedelta() as delta:
105
+ throttle_period = delta.total_seconds()
106
+
107
+ case period_seconds:
108
+ throttle_period = period_seconds
109
+
110
+ async def throttle(
111
+ *args: Args.args,
112
+ **kwargs: Args.kwargs,
113
+ ) -> Result:
114
+ async with lock:
115
+ time_now: float = monotonic()
116
+ while entries: # cleanup old entries
117
+ if entries[0] + throttle_period <= time_now:
118
+ entries.popleft()
119
+
120
+ else:
121
+ break
122
+
123
+ if len(entries) >= limit:
124
+ await sleep(entries[0] - time_now)
125
+
126
+ entries.append(monotonic())
127
+
128
+ return await function(*args, **kwargs)
129
+
130
+ # mimic function attributes if able
131
+ return mimic_function(function, within=throttle)
108
132
 
109
133
  if function := function:
110
134
  return _wrap(function)
@@ -199,7 +199,7 @@ class AttributeRequirement[Root]:
199
199
 
200
200
  def check_contains_any(root: Root) -> None:
201
201
  checked: Any = cast(AttributePath[Root, Parameter], path)(root)
202
- if any(element in checked for element in value):
202
+ if not any(element in checked for element in value):
203
203
  raise ValueError(
204
204
  f"{checked} does not contain any of {value} for '{path.__repr__()}'"
205
205
  )
haiway/types/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
1
  from haiway.types.default import Default, DefaultValue
2
- from haiway.types.frozen import frozenlist
3
2
  from haiway.types.missing import MISSING, Missing, is_missing, not_missing, when_missing
4
3
 
5
4
  __all__ = (
@@ -7,7 +6,6 @@ __all__ = (
7
6
  "Default",
8
7
  "DefaultValue",
9
8
  "Missing",
10
- "frozenlist",
11
9
  "is_missing",
12
10
  "not_missing",
13
11
  "when_missing",
haiway/utils/mimic.py CHANGED
@@ -40,7 +40,7 @@ def mimic_function[**Args, Result](
40
40
  "__globals__",
41
41
  ):
42
42
  try:
43
- setattr(
43
+ object.__setattr__(
44
44
  target,
45
45
  attribute,
46
46
  getattr(
@@ -57,7 +57,7 @@ def mimic_function[**Args, Result](
57
57
  except AttributeError:
58
58
  pass
59
59
 
60
- setattr( # noqa: B010 - mimic functools.wraps behavior for correct signature checks
60
+ object.__setattr__( # mimic functools.wraps behavior for correct signature checks
61
61
  target,
62
62
  "__wrapped__",
63
63
  function,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haiway
3
- Version: 0.20.1
3
+ Version: 0.21.1
4
4
  Summary: Framework for dependency injection and state management within structured concurrency model.
5
5
  Project-URL: Homepage, https://miquido.com
6
6
  Project-URL: Repository, https://github.com/miquido/haiway.git
@@ -42,13 +42,17 @@ Requires-Dist: pytest-cov~=6.1; extra == 'dev'
42
42
  Requires-Dist: pytest~=8.3; extra == 'dev'
43
43
  Requires-Dist: ruff~=0.11; extra == 'dev'
44
44
  Provides-Extra: opentelemetry
45
- Requires-Dist: opentelemetry-api; extra == 'opentelemetry'
46
- Requires-Dist: opentelemetry-exporter-otlp-proto-grpc; extra == 'opentelemetry'
47
- Requires-Dist: opentelemetry-sdk; extra == 'opentelemetry'
45
+ Requires-Dist: opentelemetry-api~=1.33; extra == 'opentelemetry'
46
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc~=1.33; extra == 'opentelemetry'
47
+ Requires-Dist: opentelemetry-sdk~=1.33; extra == 'opentelemetry'
48
48
  Description-Content-Type: text/markdown
49
49
 
50
50
  # 🚗 haiway 🚕 🚚 🚙
51
51
 
52
+ ![Python Version](https://img.shields.io/badge/Python-3.12+-blue)
53
+ ![License](https://img.shields.io/badge/License-MIT-green)
54
+ ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/miquido/haiway?utm_source=oss&utm_medium=github&utm_campaign=miquido%2Fhaiway&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews)
55
+
52
56
  haiway is a framework designed to facilitate the development of applications using the functional programming paradigm combined with structured concurrency concepts. Unlike traditional object-oriented frameworks, haiway emphasizes immutability, pure functions, and context-based state management, enabling developers to build scalable and maintainable applications. By leveraging context managers combined with context vars, haiway ensures safe state propagation in concurrent environments and simplifies dependency injection through function implementation propagation.
53
57
 
54
58
  ## 🖥️ Install
@@ -1,4 +1,4 @@
1
- haiway/__init__.py,sha256=swH9O_-cvHnZ8hC2AyxxL02M8cGldI0f_Be_l9C1rUY,2479
1
+ haiway/__init__.py,sha256=keuz9FN8VqLamqrzvjK2IAjkdyyFcnboDrB9xkFPgXk,1861
2
2
  haiway/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  haiway/context/__init__.py,sha256=1N_SvdPkTfIZDZybm3y0rY2dGrDLWTm0ryzUz2XD4f8,1174
4
4
  haiway/context/access.py,sha256=lDvkMoqUOwzBt-P8mI-VggS2wrP_BJLmYrArdj8z4SE,21644
@@ -8,13 +8,13 @@ haiway/context/observability.py,sha256=gLKbMPNvt5ozrfyc4TGahN8A_dFFtyCjUIMZu9_wZ
8
8
  haiway/context/state.py,sha256=tRJRvd07XObhdayz-1OcNxABqcHQRD_k_yUGsn72wDU,9541
9
9
  haiway/context/tasks.py,sha256=pScFgeiyrXSJRDFZiYbBLi3k_DHkSlhB8rgAnYtgyrU,4925
10
10
  haiway/context/types.py,sha256=VDWXJySihfvSSPzY09PaGk6j5S9HgmAUboBGCZ8o_4k,766
11
- haiway/helpers/__init__.py,sha256=_A-JYAPemd-2o4NxxchaZENu8gANL8QDxK5ADzInXUU,670
12
- haiway/helpers/asynchrony.py,sha256=2NtuMklKHyU2o7H_Y_wyN6mxa13wdgI6yETjPbgSHPA,8136
11
+ haiway/helpers/__init__.py,sha256=WzQFUHAX0NtpbdKycHywTyxfMGmid91y0vfmdIHX-NE,640
12
+ haiway/helpers/asynchrony.py,sha256=kcGBTF7Dc2a0THH8usIq2OenspXx3KvuNrL8j7xyh80,5382
13
13
  haiway/helpers/caching.py,sha256=BqgcUGQSAmXsuLi5V8EwlZzuGyutHOn1V4k7BHsGKeg,14347
14
14
  haiway/helpers/concurrent.py,sha256=xGMcan_tiETAHQs1YFmgYpA4YMFo6rIbFKvNeMlRFG8,2551
15
15
  haiway/helpers/observability.py,sha256=3G0eRE1WYTGRujS0mxzYbLR4MlKnoYllE8cu2Eb_23w,11073
16
16
  haiway/helpers/retries.py,sha256=52LA85HejTiSmCmTMAA9c8oUqD_VnhbTn1b3kwlU52c,9032
17
- haiway/helpers/throttling.py,sha256=hwhruBXepve2TJA4vsaO4WwTFkclt5eNiZ8U0mo-mlI,5015
17
+ haiway/helpers/throttling.py,sha256=KBWUSHdKVMC5_nRMmmoPNwfp-3AcerQ6OczJa9gNLM0,5796
18
18
  haiway/helpers/timeouted.py,sha256=GQ8-btb36f0Jq7TnorAPYXyKScNmf0nxHXCYxqGl-o8,3949
19
19
  haiway/helpers/tracing.py,sha256=NHipA5UlngwFcAaKhXg1jTuJ-ti6AqSNxE7u7-92vWo,5409
20
20
  haiway/opentelemetry/__init__.py,sha256=TV-1C14mDAtcHhFZ29ActFQdrGH6x5KuGV9w-JlKYJg,91
@@ -22,12 +22,11 @@ haiway/opentelemetry/observability.py,sha256=5fsHsFgjcxUcA0hIOM18lVvVdYSRO91ER52
22
22
  haiway/state/__init__.py,sha256=AaMqlMhO4zKS_XNevy3A7BHh5PxmguA-Sk_FnaNDY1Q,355
23
23
  haiway/state/attributes.py,sha256=sububiFP23aBB8RGk6OvTUp7BEY6S0kER_uHC09yins,26733
24
24
  haiway/state/path.py,sha256=bv5MI3HmUyku78k0Sz5lc7Q_Bay53iom1l3AL5KZs-4,32143
25
- haiway/state/requirement.py,sha256=_tA6CPyFX6WTjJ0Yk2jeMs-At17ex8Yl8y1-s24idas,12793
25
+ haiway/state/requirement.py,sha256=NbXL7JrB-zGE6KShcRRtq-wJbKu5lHlhFdmfhyJZdkc,12797
26
26
  haiway/state/structure.py,sha256=KKIId-mrHAzGjYKKlvnlscMijVZVM8nDLnAwCFn1sTc,23259
27
27
  haiway/state/validation.py,sha256=eDOZKRrfd-dmdbqoHcLacdCVKmVCEpwt239EG6ljNF8,23557
28
- haiway/types/__init__.py,sha256=73DMgf60Ftf1gLRCSQG66Nyu3_QFjdRJggBtS4-RQkY,342
28
+ haiway/types/__init__.py,sha256=jFr5kf36SvVGdgngvik6_HzG8YNa3NVsdDDSqxVuGm4,281
29
29
  haiway/types/default.py,sha256=59chcOaoGqI2to08RamCCLluimfYbJp5xbYl3fWaLrM,4153
30
- haiway/types/frozen.py,sha256=Zb9P0IpB-YgOrnHwiGqPk8AvyH-ABX7jajS9m3kzjBk,701
31
30
  haiway/types/missing.py,sha256=bVlOJN0Z04gALKj01Ah6_sWbC94mwRatdEcW7neLAsY,4188
32
31
  haiway/utils/__init__.py,sha256=HOylRgBEa0uNxEuPBupaJ28l4wEQiy98cGJi2Gtirr4,972
33
32
  haiway/utils/always.py,sha256=dd6jDQ1j4DpJjTKO1J2Tv5xS8X1LnMC4kQ0D7DtKUvw,1230
@@ -36,11 +35,11 @@ haiway/utils/env.py,sha256=Z0uHJDFegvgzy-gM-f0uPMha9_1ldUglrD5SKNJsvYE,9445
36
35
  haiway/utils/formatting.py,sha256=jgSIGalGUBZVo2ziiNC5Y7vBYbAEwPugOiwEOrNFTcI,4039
37
36
  haiway/utils/freezing.py,sha256=HJH0SOgPCreb9o0wPeaMPMxhS9JDuzzey6UsKhuvUJU,1292
38
37
  haiway/utils/logs.py,sha256=NuwoqKQnMNi1FMIA91cVFnAPefUFeg3UIT50IOl3sJk,1571
39
- haiway/utils/mimic.py,sha256=L5AS4WEL2aPMZAQZlvLvRzHl0cipI7ivky60_eL4iwY,1822
38
+ haiway/utils/mimic.py,sha256=xaZiUKp096QFfdSw7cNIKEWt2UIS7vf880KF54gny38,1831
40
39
  haiway/utils/noop.py,sha256=U8ocfoCgt-pY0owJDPtrRrj53cabeIXH9qCKWMQnoRk,1336
41
40
  haiway/utils/queue.py,sha256=6v2u3pA6A44IuCCTOjmCt3yLyOcm7PCRnrIGo25j-1o,6402
42
41
  haiway/utils/stream.py,sha256=lXaeveTY0-AYG5xVzcQYaiC6SUD5fUtHoMXiQcrQAAM,5723
43
- haiway-0.20.1.dist-info/METADATA,sha256=BJSukCFzSCZuPbK0HmUsBGdhhbC-K3yfH9ocia9rYJA,4527
44
- haiway-0.20.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
45
- haiway-0.20.1.dist-info/licenses/LICENSE,sha256=3phcpHVNBP8jsi77gOO0E7rgKeDeu99Pi7DSnK9YHoQ,1069
46
- haiway-0.20.1.dist-info/RECORD,,
42
+ haiway-0.21.1.dist-info/METADATA,sha256=LdHoQk1LAFTONT6SFzeZ07x9hKvRqNZP4a7hGdhWdiE,4919
43
+ haiway-0.21.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
+ haiway-0.21.1.dist-info/licenses/LICENSE,sha256=3phcpHVNBP8jsi77gOO0E7rgKeDeu99Pi7DSnK9YHoQ,1069
45
+ haiway-0.21.1.dist-info/RECORD,,
haiway/types/frozen.py DELETED
@@ -1,21 +0,0 @@
1
- __all__ = ("frozenlist",)
2
-
3
- type frozenlist[Value] = tuple[Value, ...]
4
- """
5
- A type alias for an immutable sequence of values.
6
-
7
- This type represents an immutable list-like structure (implemented as a tuple)
8
- that can be used when an immutable sequence is required. It provides the same
9
- indexing and iteration capabilities as a list, but cannot be modified after creation.
10
-
11
- The generic parameter Value specifies the type of elements stored in the sequence.
12
-
13
- Examples
14
- --------
15
- ```python
16
- items: frozenlist[int] = (1, 2, 3) # Create a frozen list of integers
17
- first_item = items[0] # Access elements by index
18
- for item in items: # Iterate over elements
19
- process(item)
20
- ```
21
- """