typebind 0.1.0a1__tar.gz → 0.1.0a2__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.
- {typebind-0.1.0a1 → typebind-0.1.0a2}/PKG-INFO +1 -1
- {typebind-0.1.0a1 → typebind-0.1.0a2}/docs/behaviors.md +4 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/pyproject.toml +1 -1
- {typebind-0.1.0a1 → typebind-0.1.0a2}/pyproject.toml.orig +1 -1
- {typebind-0.1.0a1 → typebind-0.1.0a2}/src/typebind/_behavior.py +20 -4
- {typebind-0.1.0a1 → typebind-0.1.0a2}/src/typebind/_models.py +7 -2
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_behavior.py +115 -1
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_models.py +66 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/uv.lock +1 -1
- {typebind-0.1.0a1 → typebind-0.1.0a2}/.python-version +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/CONTRIBUTING.md +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/LICENSE +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/README.md +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/SECURITY.md +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/benchmarks/behavior.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/benchmarks/models.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/docs/discovery.md +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/docs/models.md +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/docs/releasing.md +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/README.md +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/commands.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/decorators.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/discovery.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/manual.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/models.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/core/pyproject.toml +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/core/src/typebind_example_core/__init__.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/core/src/typebind_example_core/py.typed +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/extra/pyproject.toml +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/extra/src/typebind_example_extra/__init__.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/extra/src/typebind_example_extra/py.typed +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/scripts/audit_dependencies.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/scripts/check_dist.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/scripts/check_release.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/src/typebind/__init__.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/src/typebind/_discovery.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/src/typebind/py.typed +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_behavior_typing.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_check_dist.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_decorators.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_discovery.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_discovery_install.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_model_policies.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_release_version.py +0 -0
- {typebind-0.1.0a1 → typebind-0.1.0a2}/tests/test_typing.py +0 -0
|
@@ -50,6 +50,10 @@ family interface. One implementation can implement multiple interfaces, even
|
|
|
50
50
|
when those interfaces bind to different ancestor families. Native generic
|
|
51
51
|
forwarding through intermediate implementation bases is supported; there must
|
|
52
52
|
ultimately be one concrete model target. Models do not inherit these interfaces.
|
|
53
|
+
This includes forwarding a tag parameter inside a model: a base such as
|
|
54
|
+
`Shared[K: str](Implementation[Message[K]])` specialized as
|
|
55
|
+
`Shared[Literal["query"]]` targets `Message[Literal["query"]]`. Assembly uses
|
|
56
|
+
Pydantic's native model specialization; the target retains its exact class identity.
|
|
53
57
|
|
|
54
58
|
Assembly resolves the `model` property's return annotation with `get_type_hints`.
|
|
55
59
|
With `from __future__ import annotations`, a family defined only inside a function
|
|
@@ -45,6 +45,20 @@ def _implementation_class(value: object) -> type[Implementation[Any]]:
|
|
|
45
45
|
return cast(type[Implementation[Any]], value)
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
def _substitute_argument(argument: object, substitutions: dict[TypeVar, object]) -> object:
|
|
49
|
+
"""Bind model parameters through native Pydantic specialization at assembly time."""
|
|
50
|
+
if isinstance(argument, TypeVar):
|
|
51
|
+
return substitutions.get(argument, argument)
|
|
52
|
+
if isinstance(argument, type) and issubclass(argument, DiscriminatedModel):
|
|
53
|
+
parameters = argument.__pydantic_generic_metadata__["parameters"]
|
|
54
|
+
resolved = tuple(substitutions.get(parameter, parameter) for parameter in parameters)
|
|
55
|
+
if resolved != parameters:
|
|
56
|
+
# Pydantic models are classes, not typing aliases. Let their native
|
|
57
|
+
# specialization substitute fields and preserve cached class identity.
|
|
58
|
+
return cast(Any, argument)[resolved]
|
|
59
|
+
return cast(object, argument)
|
|
60
|
+
|
|
61
|
+
|
|
48
62
|
def _implementation_model(implementation: type[Implementation[Any]]) -> type[DiscriminatedModel]:
|
|
49
63
|
"""Resolve native generic bases, including type-variable forwarding through mixins."""
|
|
50
64
|
targets: set[type[DiscriminatedModel]] = set()
|
|
@@ -54,10 +68,7 @@ def _implementation_model(implementation: type[Implementation[Any]]) -> type[Dis
|
|
|
54
68
|
origin: object = get_origin(base) or base
|
|
55
69
|
if not isinstance(origin, type):
|
|
56
70
|
continue
|
|
57
|
-
arguments = tuple(
|
|
58
|
-
substitutions.get(arg, arg) if isinstance(arg, TypeVar) else arg
|
|
59
|
-
for arg in get_args(base)
|
|
60
|
-
)
|
|
71
|
+
arguments = tuple(_substitute_argument(arg, substitutions) for arg in get_args(base))
|
|
61
72
|
if origin is Implementation:
|
|
62
73
|
if len(arguments) != 1:
|
|
63
74
|
raise TypeError(
|
|
@@ -68,6 +79,11 @@ def _implementation_model(implementation: type[Implementation[Any]]) -> type[Dis
|
|
|
68
79
|
elif issubclass(origin, Implementation):
|
|
69
80
|
parent = cast(type[Implementation[Any]], origin)
|
|
70
81
|
parameters = cast(tuple[TypeVar, ...], getattr(parent, "__parameters__", ()))
|
|
82
|
+
if len(arguments) != len(parameters):
|
|
83
|
+
raise TypeError(
|
|
84
|
+
f"{_name(implementation)}: expected explicit type arguments "
|
|
85
|
+
f"for {_name(parent)}"
|
|
86
|
+
)
|
|
71
87
|
visit(parent, dict(zip(parameters, arguments, strict=True)))
|
|
72
88
|
|
|
73
89
|
visit(implementation, {})
|
|
@@ -230,6 +230,7 @@ class DiscriminatedModel(BaseModel):
|
|
|
230
230
|
annotation = cls.model_fields[field_name].annotation
|
|
231
231
|
if not _is_open_tag(annotation):
|
|
232
232
|
_literal_tag(cls, field_name)
|
|
233
|
+
cls._check_tag_field(field_name)
|
|
233
234
|
return
|
|
234
235
|
parent_discriminator = _open_discriminator(parent)
|
|
235
236
|
if parent_discriminator is not None and not _decorated_field(
|
|
@@ -450,7 +451,7 @@ def _branches(
|
|
|
450
451
|
Unspecialized ancestors identify the family boundaries. All tag values come
|
|
451
452
|
from the supplied leaf's resolved Pydantic fields, not generic arguments or
|
|
452
453
|
stored paths. Repeated classes are harmless; conflicting parent/tag claims
|
|
453
|
-
raise ValueError.
|
|
454
|
+
raise ValueError. Abstract models, open families and untagged models raise TypeError.
|
|
454
455
|
"""
|
|
455
456
|
branches: dict[type[DiscriminatedModel], dict[str, type[DiscriminatedModel]]] = {}
|
|
456
457
|
seen: set[type[DiscriminatedModel]] = set()
|
|
@@ -460,7 +461,11 @@ def _branches(
|
|
|
460
461
|
if variant in seen:
|
|
461
462
|
continue
|
|
462
463
|
seen.add(variant)
|
|
463
|
-
if
|
|
464
|
+
if (
|
|
465
|
+
isabstract(variant)
|
|
466
|
+
or _open_discriminator(variant) is not None
|
|
467
|
+
or not _tag_fields(variant)
|
|
468
|
+
):
|
|
464
469
|
raise TypeError(f"{_model_name(variant)} must be a tagged leaf model")
|
|
465
470
|
families = [
|
|
466
471
|
base
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Behavior assembly validates declarations once and binds ordinary objects."""
|
|
2
2
|
|
|
3
|
+
import sys
|
|
3
4
|
from abc import abstractmethod
|
|
4
5
|
from importlib import metadata
|
|
5
|
-
from typing import Annotated, Any, Literal, Protocol, cast
|
|
6
|
+
from typing import Annotated, Any, Literal, Protocol, assert_type, cast
|
|
6
7
|
|
|
7
8
|
import pytest
|
|
8
9
|
from pydantic import BaseModel, TypeAdapter
|
|
@@ -159,6 +160,69 @@ def test_generic_forwarding_and_inherited_implementation() -> None:
|
|
|
159
160
|
)
|
|
160
161
|
|
|
161
162
|
|
|
163
|
+
@pytest.mark.parametrize("through_middle", [False, True])
|
|
164
|
+
def test_nested_model_type_argument_forwarding(through_middle: bool) -> None:
|
|
165
|
+
class Shared[K: str](Implementation[Message[K]], Describable):
|
|
166
|
+
def describe(self) -> str:
|
|
167
|
+
return self.model.identifier
|
|
168
|
+
|
|
169
|
+
class Direct(Shared[Literal["nested"]]):
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
class Middle[K: str](Shared[K]):
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
class Forwarded(Middle[Literal["nested"]]):
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
class Inherited(Forwarded):
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
leaf = Message[Literal["nested"]]
|
|
182
|
+
sample = leaf(identifier="nested")
|
|
183
|
+
assert_type(Direct(sample).model, Message[Literal["nested"]])
|
|
184
|
+
assert_type(Inherited(sample).model, Message[Literal["nested"]])
|
|
185
|
+
implementation = Inherited if through_middle else Direct
|
|
186
|
+
runtime = assemble(
|
|
187
|
+
variants=[leaf], implementations=[implementation], required_interfaces=[Describable]
|
|
188
|
+
)
|
|
189
|
+
view = runtime.adapt(sample, Describable)
|
|
190
|
+
assert type(view) is implementation
|
|
191
|
+
assert view.model is sample
|
|
192
|
+
assert view.describe() == "nested"
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_nested_model_argument_through_whole_model_forwarding() -> None:
|
|
196
|
+
class Whole[T: DiscriminatedModel](Implementation[T]):
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
class Shared[K: str](Whole[Request[K]], Describable):
|
|
200
|
+
def describe(self) -> str:
|
|
201
|
+
return f"{self.model.kind}/{self.model.method}"
|
|
202
|
+
|
|
203
|
+
class Concrete(Shared[Literal["nested"]]):
|
|
204
|
+
pass
|
|
205
|
+
|
|
206
|
+
leaf = Request[Literal["nested"]]
|
|
207
|
+
sample = leaf(identifier="q")
|
|
208
|
+
assert_type(Concrete(sample).model, Request[Literal["nested"]])
|
|
209
|
+
runtime = assemble(
|
|
210
|
+
variants=[leaf], implementations=[Concrete], required_interfaces=[Describable]
|
|
211
|
+
)
|
|
212
|
+
view = runtime.adapt(sample, Describable)
|
|
213
|
+
assert view.model is sample
|
|
214
|
+
assert view.describe() == "request/nested"
|
|
215
|
+
|
|
216
|
+
class Open(Shared[str]):
|
|
217
|
+
pass
|
|
218
|
+
|
|
219
|
+
with pytest.raises(TypeError, match="expected a concrete model"):
|
|
220
|
+
assemble(variants=[leaf], implementations=[Open])
|
|
221
|
+
|
|
222
|
+
with pytest.raises(TypeError, match="expected a concrete model"):
|
|
223
|
+
assemble(variants=[leaf], implementations=[Shared])
|
|
224
|
+
|
|
225
|
+
|
|
162
226
|
def test_no_reflection_discovery_or_validation_in_hot_path(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
163
227
|
runtime = assemble(
|
|
164
228
|
variants=[Query], implementations=[QueryObject], required_interfaces=[Describable]
|
|
@@ -220,6 +284,56 @@ def test_unspecialized_implementation_is_rejected(bare: type[object]) -> None:
|
|
|
220
284
|
assert f"{bare.__module__}.{bare.__qualname__}" in str(error.value)
|
|
221
285
|
|
|
222
286
|
|
|
287
|
+
def test_unspecialized_intermediate_implementation_is_rejected() -> None:
|
|
288
|
+
class Shared[T: DiscriminatedModel](Implementation[T]):
|
|
289
|
+
pass
|
|
290
|
+
|
|
291
|
+
# Deliberately omit the generic arguments, as an unchecked plugin may do.
|
|
292
|
+
bare = type("Unparameterized", (Shared,), {})
|
|
293
|
+
with pytest.raises(TypeError, match="expected explicit type arguments for .*Shared") as error:
|
|
294
|
+
assemble(variants=[Query], implementations=[bare])
|
|
295
|
+
assert "Unparameterized" in str(error.value)
|
|
296
|
+
assert (
|
|
297
|
+
f"Behavior implementation: {bare.__module__}.{bare.__qualname__}" in error.value.__notes__
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@pytest.mark.skipif(
|
|
302
|
+
sys.version_info < (3, 13), reason="Native type parameter defaults require 3.13"
|
|
303
|
+
)
|
|
304
|
+
def test_implementation_type_parameter_defaults() -> None:
|
|
305
|
+
namespace: dict[str, Any] = {
|
|
306
|
+
"__name__": __name__,
|
|
307
|
+
"DiscriminatedModel": DiscriminatedModel,
|
|
308
|
+
"Implementation": Implementation,
|
|
309
|
+
"Query": Query,
|
|
310
|
+
"Describable": Describable,
|
|
311
|
+
}
|
|
312
|
+
# Keep the test module importable on Python 3.12.
|
|
313
|
+
exec(
|
|
314
|
+
"""
|
|
315
|
+
class Shared[T: DiscriminatedModel = Query](Implementation[T]):
|
|
316
|
+
pass
|
|
317
|
+
class Bare(Shared):
|
|
318
|
+
pass
|
|
319
|
+
class Explicit(Shared[()], Describable):
|
|
320
|
+
def describe(self) -> str:
|
|
321
|
+
return self.model.expression
|
|
322
|
+
""",
|
|
323
|
+
namespace,
|
|
324
|
+
)
|
|
325
|
+
with pytest.raises(TypeError, match="Bare.*expected explicit type arguments for .*Shared"):
|
|
326
|
+
assemble(variants=[Query], implementations=[namespace["Bare"]])
|
|
327
|
+
|
|
328
|
+
runtime = assemble(
|
|
329
|
+
variants=[Query], implementations=[namespace["Explicit"]], required_interfaces=[Describable]
|
|
330
|
+
)
|
|
331
|
+
query = Query(identifier="q", expression="default")
|
|
332
|
+
view = runtime.adapt(query, Describable)
|
|
333
|
+
assert view.model is query
|
|
334
|
+
assert view.describe() == "default"
|
|
335
|
+
|
|
336
|
+
|
|
223
337
|
def test_abstract_and_invalid_declarations() -> None:
|
|
224
338
|
class Missing(Implementation[Query], Describable):
|
|
225
339
|
pass
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Exercise declarations, dispatch, isolation, and serialization boundaries."""
|
|
2
2
|
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
3
4
|
from datetime import UTC, datetime
|
|
4
5
|
from types import SimpleNamespace
|
|
5
6
|
from typing import Annotated, Any, Literal, Self, assert_type, cast, get_type_hints
|
|
@@ -292,6 +293,35 @@ def test_invalid_variant_inputs_report_the_value(value: object) -> None:
|
|
|
292
293
|
assert repr(value) in str(error.value)
|
|
293
294
|
|
|
294
295
|
|
|
296
|
+
@pytest.mark.parametrize("binding", [False, True], ids=["adapter", "binding"])
|
|
297
|
+
def test_abstract_tagged_variants_are_rejected(binding: bool) -> None:
|
|
298
|
+
class AbstractQuery(Request[Literal["abstract"]], ABC):
|
|
299
|
+
@abstractmethod
|
|
300
|
+
def execute(self) -> str: ...
|
|
301
|
+
|
|
302
|
+
with pytest.raises(TypeError, match="AbstractQuery.*tagged leaf model"):
|
|
303
|
+
if binding:
|
|
304
|
+
bind_variants([AbstractQuery])
|
|
305
|
+
else:
|
|
306
|
+
build_adapter(Message[Any], variants=[AbstractQuery])
|
|
307
|
+
|
|
308
|
+
class ConcreteQuery(AbstractQuery):
|
|
309
|
+
def execute(self) -> str:
|
|
310
|
+
return self.identifier
|
|
311
|
+
|
|
312
|
+
annotation = Annotated[Message[Any], bind_variants([ConcreteQuery])]
|
|
313
|
+
adapter = (
|
|
314
|
+
TypeAdapter[Message[Any]](annotation)
|
|
315
|
+
if binding
|
|
316
|
+
else build_adapter(Message[Any], variants=[ConcreteQuery])
|
|
317
|
+
)
|
|
318
|
+
result = adapter.validate_python(
|
|
319
|
+
{"kind": "request", "method": "abstract", "identifier": "q", "sequence": 1}
|
|
320
|
+
)
|
|
321
|
+
assert isinstance(result, ConcreteQuery)
|
|
322
|
+
assert result.execute() == "q"
|
|
323
|
+
|
|
324
|
+
|
|
295
325
|
def test_variant_bindings_are_explicit_independent_snapshots() -> None:
|
|
296
326
|
before = Message.model_json_schema()
|
|
297
327
|
variants: list[type[DiscriminatedModel]] = [Query]
|
|
@@ -551,6 +581,42 @@ def test_conditional_tag_exclusion_is_rejected_at_declaration() -> None:
|
|
|
551
581
|
assert "Unsupported settings: exclude_if" in str(error.value)
|
|
552
582
|
|
|
553
583
|
|
|
584
|
+
@pytest.mark.parametrize(
|
|
585
|
+
"setting, value",
|
|
586
|
+
[
|
|
587
|
+
("alias", "k"),
|
|
588
|
+
("validation_alias", "k"),
|
|
589
|
+
("serialization_alias", "k"),
|
|
590
|
+
("exclude", True),
|
|
591
|
+
pytest.param(
|
|
592
|
+
"exclude_if",
|
|
593
|
+
bool,
|
|
594
|
+
marks=pytest.mark.skipif(
|
|
595
|
+
not hasattr(Field(), "exclude_if"), reason="Requires Pydantic's exclude_if"
|
|
596
|
+
),
|
|
597
|
+
),
|
|
598
|
+
],
|
|
599
|
+
)
|
|
600
|
+
def test_specialization_preserves_tag_field_restrictions(setting: str, value: Any) -> None:
|
|
601
|
+
options = {setting: value}
|
|
602
|
+
|
|
603
|
+
class Native[Tag: str](BaseModel):
|
|
604
|
+
kind: Tag
|
|
605
|
+
|
|
606
|
+
native = Native[Annotated[Literal["hidden"], Field(**options)]]
|
|
607
|
+
if getattr(native.model_fields["kind"], setting) is None:
|
|
608
|
+
# Older Pydantic releases ignore these settings in generic type arguments.
|
|
609
|
+
# They remain safe only if tags are still exported and routable.
|
|
610
|
+
leaf = Message[Annotated[Literal["hidden"], Field(**options)]]
|
|
611
|
+
sample = leaf(identifier="q")
|
|
612
|
+
adapter = build_adapter(Message[Any], variants=[leaf])
|
|
613
|
+
assert sample.model_dump(by_alias=True) == {"kind": "hidden", "identifier": "q"}
|
|
614
|
+
assert adapter.validate_json(adapter.dump_json(sample, by_alias=True)) == sample
|
|
615
|
+
return
|
|
616
|
+
with pytest.raises(TypeError, match=f"Unsupported settings: .*{setting}"):
|
|
617
|
+
Message[Annotated[Literal["hidden"], Field(**options)]]
|
|
618
|
+
|
|
619
|
+
|
|
554
620
|
def test_tag_field_is_the_single_source_of_the_discriminator() -> None:
|
|
555
621
|
class Packet[Tag: str](DiscriminatedModel, extra="forbid"):
|
|
556
622
|
operation: Annotated[Tag, Field(description="Wire operation")] = tag_field()
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/core/src/typebind_example_core/__init__.py
RENAMED
|
File without changes
|
{typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/core/src/typebind_example_core/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
{typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/extra/src/typebind_example_extra/__init__.py
RENAMED
|
File without changes
|
{typebind-0.1.0a1 → typebind-0.1.0a2}/examples/plugins/extra/src/typebind_example_extra/py.typed
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|