schemez 0.0.1__py3-none-any.whl → 0.1.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.
- schemez/__init__.py +1 -1
- schemez/convert.py +142 -0
- schemez/docstrings.py +157 -0
- schemez/helpers.py +35 -35
- schemez/schema.py +102 -67
- {schemez-0.0.1.dist-info → schemez-0.1.0.dist-info}/METADATA +2 -1
- schemez-0.1.0.dist-info/RECORD +10 -0
- schemez-0.0.1.dist-info/RECORD +0 -8
- {schemez-0.0.1.dist-info → schemez-0.1.0.dist-info}/WHEEL +0 -0
- {schemez-0.0.1.dist-info → schemez-0.1.0.dist-info}/licenses/LICENSE +0 -0
schemez/__init__.py
CHANGED
schemez/convert.py
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
"""BaseModel tools."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import dataclasses
|
6
|
+
import inspect
|
7
|
+
from types import UnionType
|
8
|
+
from typing import (
|
9
|
+
TYPE_CHECKING,
|
10
|
+
Any,
|
11
|
+
TypeAliasType,
|
12
|
+
Union,
|
13
|
+
get_args,
|
14
|
+
get_origin,
|
15
|
+
get_type_hints,
|
16
|
+
)
|
17
|
+
|
18
|
+
from pydantic import BaseModel, Field, create_model
|
19
|
+
|
20
|
+
from schemez.docstrings import get_docstring_info
|
21
|
+
from schemez.schema import Schema
|
22
|
+
|
23
|
+
|
24
|
+
if TYPE_CHECKING:
|
25
|
+
from llmling_agent.common_types import AnyCallable
|
26
|
+
from pydantic.fields import FieldInfo
|
27
|
+
|
28
|
+
|
29
|
+
def get_union_args(tp: Any) -> tuple[Any, ...]:
|
30
|
+
"""Extract arguments of a Union type."""
|
31
|
+
if isinstance(tp, TypeAliasType):
|
32
|
+
tp = tp.__value__
|
33
|
+
|
34
|
+
origin = get_origin(tp)
|
35
|
+
if origin is Union or origin is UnionType:
|
36
|
+
return get_args(tp)
|
37
|
+
return ()
|
38
|
+
|
39
|
+
|
40
|
+
def get_function_model(func: AnyCallable, *, name: str | None = None) -> type[Schema]:
|
41
|
+
"""Convert a function's signature to a Pydantic model.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
func: The function to convert (can be method)
|
45
|
+
name: Optional name for the model
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
Pydantic model representing the function parameters
|
49
|
+
|
50
|
+
Example:
|
51
|
+
>>> def greet(name: str, age: int | None = None) -> str:
|
52
|
+
... '''Greet someone.
|
53
|
+
... Args:
|
54
|
+
... name: Person's name
|
55
|
+
... age: Optional age
|
56
|
+
... '''
|
57
|
+
... return f"Hello {name}"
|
58
|
+
>>> model = get_function_model(greet)
|
59
|
+
"""
|
60
|
+
sig = inspect.signature(func)
|
61
|
+
hints = get_type_hints(func, include_extras=True)
|
62
|
+
fields: dict[str, tuple[type, FieldInfo]] = {}
|
63
|
+
description, param_docs = get_docstring_info(func, sig)
|
64
|
+
|
65
|
+
for param_name, param in sig.parameters.items():
|
66
|
+
# Skip self/cls for methods
|
67
|
+
if param_name in ("self", "cls"):
|
68
|
+
continue
|
69
|
+
|
70
|
+
type_hint = hints.get(param_name, Any)
|
71
|
+
|
72
|
+
# Handle unions (including Optional)
|
73
|
+
if union_args := get_union_args(type_hint): # noqa: SIM102
|
74
|
+
if len(union_args) == 2 and type(None) in union_args: # noqa: PLR2004
|
75
|
+
type_hint = next(t for t in union_args if t is not type(None))
|
76
|
+
|
77
|
+
# Create field with defaults if available
|
78
|
+
field = Field(
|
79
|
+
default=... if param.default is param.empty else param.default,
|
80
|
+
description=param_docs.get(param_name), # TODO: Add docstring parsing
|
81
|
+
)
|
82
|
+
fields[param_name] = (type_hint, field)
|
83
|
+
|
84
|
+
model_name = name or f"{func.__name__}Params"
|
85
|
+
return create_model(model_name, **fields, __base__=Schema, __doc__=description) # type: ignore
|
86
|
+
|
87
|
+
|
88
|
+
def get_ctor_basemodel(cls: type) -> type[Schema]:
|
89
|
+
"""Convert a class constructor to a Pydantic model.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
cls: The class whose constructor to convert
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
Pydantic model for the constructor parameters
|
96
|
+
|
97
|
+
Example:
|
98
|
+
>>> class Person:
|
99
|
+
... def __init__(self, name: str, age: int | None = None):
|
100
|
+
... self.name = name
|
101
|
+
... self.age = age
|
102
|
+
>>> model = get_ctor_basemodel(Person)
|
103
|
+
"""
|
104
|
+
if issubclass(cls, BaseModel):
|
105
|
+
if issubclass(cls, Schema):
|
106
|
+
return cls
|
107
|
+
|
108
|
+
# Create a new Schema-based model with the same fields
|
109
|
+
fields = {}
|
110
|
+
for field_name, field_info in cls.model_fields.items():
|
111
|
+
field_type = field_info.annotation
|
112
|
+
field_default = (
|
113
|
+
field_info.default if field_info.default is not Ellipsis else ...
|
114
|
+
)
|
115
|
+
fields[field_name] = (field_type, field_default)
|
116
|
+
|
117
|
+
return create_model(cls.__name__, **fields, __base__=Schema) # type: ignore
|
118
|
+
|
119
|
+
if dataclasses.is_dataclass(cls):
|
120
|
+
fields = {}
|
121
|
+
hints = get_type_hints(cls)
|
122
|
+
for field in dataclasses.fields(cls):
|
123
|
+
fields[field.name] = (hints[field.name], ...)
|
124
|
+
return create_model(cls.__name__, __base__=Schema, **fields) # type: ignore
|
125
|
+
return get_function_model(cls.__init__, name=cls.__name__)
|
126
|
+
|
127
|
+
|
128
|
+
if __name__ == "__main__":
|
129
|
+
|
130
|
+
class Person:
|
131
|
+
"""Person class."""
|
132
|
+
|
133
|
+
def __init__(self, name: str, age: int | None = None):
|
134
|
+
self.name = name
|
135
|
+
self.age = age
|
136
|
+
|
137
|
+
def func_google(self, name: str, age: int | None = None):
|
138
|
+
"""Do something."""
|
139
|
+
|
140
|
+
model = get_function_model(Person.func_google)
|
141
|
+
instance = model(name="Test", age=30) # type: ignore
|
142
|
+
print(instance, isinstance(instance, BaseModel))
|
schemez/docstrings.py
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
"""Credits to pydantic-ai."""
|
2
|
+
|
3
|
+
from __future__ import annotations as _annotations
|
4
|
+
|
5
|
+
from collections.abc import Callable
|
6
|
+
from contextlib import contextmanager
|
7
|
+
import logging
|
8
|
+
import re
|
9
|
+
from typing import TYPE_CHECKING, Any, Literal, cast
|
10
|
+
|
11
|
+
from griffe import Docstring, DocstringSectionKind, Object as GriffeObject
|
12
|
+
|
13
|
+
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from inspect import Signature
|
16
|
+
|
17
|
+
|
18
|
+
DocstringStyle = Literal["google", "numpy", "sphinx"]
|
19
|
+
DocstringFormat = Literal["google", "numpy", "sphinx", "auto"]
|
20
|
+
AnyCallable = Callable[..., Any]
|
21
|
+
|
22
|
+
|
23
|
+
def get_docstring_info(
|
24
|
+
func: AnyCallable,
|
25
|
+
sig: Signature,
|
26
|
+
*,
|
27
|
+
docstring_format: DocstringFormat = "auto",
|
28
|
+
) -> tuple[str, dict[str, str]]:
|
29
|
+
"""Extract the fn description and parameter descriptions from a fn's docstring.
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
A tuple of (main function description, parameter descriptions).
|
33
|
+
"""
|
34
|
+
doc = func.__doc__
|
35
|
+
if doc is None:
|
36
|
+
return "", {}
|
37
|
+
|
38
|
+
# see https://github.com/mkdocstrings/griffe/issues/293
|
39
|
+
parent = cast(GriffeObject, sig)
|
40
|
+
|
41
|
+
docstring_style = (
|
42
|
+
_infer_docstring_style(doc) if docstring_format == "auto" else docstring_format
|
43
|
+
)
|
44
|
+
docstring = Docstring(doc, lineno=1, parser=docstring_style, parent=parent)
|
45
|
+
with _disable_griffe_logging():
|
46
|
+
sections = docstring.parse()
|
47
|
+
|
48
|
+
params = {}
|
49
|
+
if parameters := next(
|
50
|
+
(p for p in sections if p.kind == DocstringSectionKind.parameters), None
|
51
|
+
):
|
52
|
+
params = {p.name: p.description for p in parameters.value}
|
53
|
+
|
54
|
+
main_desc = ""
|
55
|
+
if main := next((p for p in sections if p.kind == DocstringSectionKind.text), None):
|
56
|
+
main_desc = main.value
|
57
|
+
|
58
|
+
return main_desc, params
|
59
|
+
|
60
|
+
|
61
|
+
def _infer_docstring_style(doc: str) -> DocstringStyle:
|
62
|
+
"""Simplistic docstring style inference."""
|
63
|
+
for pattern, replacements, style in _docstring_style_patterns:
|
64
|
+
matches = (
|
65
|
+
re.search(pattern.format(replacement), doc, re.IGNORECASE | re.MULTILINE)
|
66
|
+
for replacement in replacements
|
67
|
+
)
|
68
|
+
if any(matches):
|
69
|
+
return style
|
70
|
+
# fallback to google style
|
71
|
+
return "google"
|
72
|
+
|
73
|
+
|
74
|
+
# See https://github.com/mkdocstrings/griffe/issues/329#issuecomment-2425017804
|
75
|
+
_docstring_style_patterns: list[tuple[str, list[str], DocstringStyle]] = [
|
76
|
+
(
|
77
|
+
r"\n[ \t]*:{0}([ \t]+\w+)*:([ \t]+.+)?\n",
|
78
|
+
[
|
79
|
+
"param",
|
80
|
+
"parameter",
|
81
|
+
"arg",
|
82
|
+
"argument",
|
83
|
+
"key",
|
84
|
+
"keyword",
|
85
|
+
"type",
|
86
|
+
"var",
|
87
|
+
"ivar",
|
88
|
+
"cvar",
|
89
|
+
"vartype",
|
90
|
+
"returns",
|
91
|
+
"return",
|
92
|
+
"rtype",
|
93
|
+
"raises",
|
94
|
+
"raise",
|
95
|
+
"except",
|
96
|
+
"exception",
|
97
|
+
],
|
98
|
+
"sphinx",
|
99
|
+
),
|
100
|
+
(
|
101
|
+
r"\n[ \t]*{0}:([ \t]+.+)?\n[ \t]+.+",
|
102
|
+
[
|
103
|
+
"args",
|
104
|
+
"arguments",
|
105
|
+
"params",
|
106
|
+
"parameters",
|
107
|
+
"keyword args",
|
108
|
+
"keyword arguments",
|
109
|
+
"other args",
|
110
|
+
"other arguments",
|
111
|
+
"other params",
|
112
|
+
"other parameters",
|
113
|
+
"raises",
|
114
|
+
"exceptions",
|
115
|
+
"returns",
|
116
|
+
"yields",
|
117
|
+
"receives",
|
118
|
+
"examples",
|
119
|
+
"attributes",
|
120
|
+
"functions",
|
121
|
+
"methods",
|
122
|
+
"classes",
|
123
|
+
"modules",
|
124
|
+
"warns",
|
125
|
+
"warnings",
|
126
|
+
],
|
127
|
+
"google",
|
128
|
+
),
|
129
|
+
(
|
130
|
+
r"\n[ \t]*{0}\n[ \t]*---+\n",
|
131
|
+
[
|
132
|
+
"deprecated",
|
133
|
+
"parameters",
|
134
|
+
"other parameters",
|
135
|
+
"returns",
|
136
|
+
"yields",
|
137
|
+
"receives",
|
138
|
+
"raises",
|
139
|
+
"warns",
|
140
|
+
"attributes",
|
141
|
+
"functions",
|
142
|
+
"methods",
|
143
|
+
"classes",
|
144
|
+
"modules",
|
145
|
+
],
|
146
|
+
"numpy",
|
147
|
+
),
|
148
|
+
]
|
149
|
+
|
150
|
+
|
151
|
+
@contextmanager
|
152
|
+
def _disable_griffe_logging():
|
153
|
+
# Hacky, but suggested here: https://github.com/mkdocstrings/griffe/issues/293#issuecomment-2167668117
|
154
|
+
old_level = logging.root.getEffectiveLevel()
|
155
|
+
logging.root.setLevel(logging.ERROR)
|
156
|
+
yield
|
157
|
+
logging.root.setLevel(old_level)
|
schemez/helpers.py
CHANGED
@@ -1,35 +1,35 @@
|
|
1
|
-
"""Helpers for BaseModels."""
|
2
|
-
|
3
|
-
from __future__ import annotations
|
4
|
-
|
5
|
-
import os
|
6
|
-
|
7
|
-
from pydantic import BaseModel
|
8
|
-
|
9
|
-
|
10
|
-
StrPath = str | os.PathLike[str]
|
11
|
-
|
12
|
-
|
13
|
-
def merge_models[T: BaseModel](base: T, overlay: T) -> T:
|
14
|
-
"""Deep merge two Pydantic models."""
|
15
|
-
if not isinstance(overlay, type(base)):
|
16
|
-
msg = f"Cannot merge different types: {type(base)} and {type(overlay)}"
|
17
|
-
raise TypeError(msg)
|
18
|
-
|
19
|
-
merged_data = base.model_dump()
|
20
|
-
overlay_data = overlay.model_dump(exclude_none=True)
|
21
|
-
for field_name, field_value in overlay_data.items():
|
22
|
-
base_value = merged_data.get(field_name)
|
23
|
-
|
24
|
-
match (base_value, field_value):
|
25
|
-
case (list(), list()):
|
26
|
-
merged_data[field_name] = [
|
27
|
-
*base_value,
|
28
|
-
*(item for item in field_value if item not in base_value),
|
29
|
-
]
|
30
|
-
case (dict(), dict()):
|
31
|
-
merged_data[field_name] = base_value | field_value
|
32
|
-
case _:
|
33
|
-
merged_data[field_name] = field_value
|
34
|
-
|
35
|
-
return base.__class__.model_validate(merged_data)
|
1
|
+
"""Helpers for BaseModels."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import os
|
6
|
+
|
7
|
+
from pydantic import BaseModel
|
8
|
+
|
9
|
+
|
10
|
+
StrPath = str | os.PathLike[str]
|
11
|
+
|
12
|
+
|
13
|
+
def merge_models[T: BaseModel](base: T, overlay: T) -> T:
|
14
|
+
"""Deep merge two Pydantic models."""
|
15
|
+
if not isinstance(overlay, type(base)):
|
16
|
+
msg = f"Cannot merge different types: {type(base)} and {type(overlay)}"
|
17
|
+
raise TypeError(msg)
|
18
|
+
|
19
|
+
merged_data = base.model_dump()
|
20
|
+
overlay_data = overlay.model_dump(exclude_none=True)
|
21
|
+
for field_name, field_value in overlay_data.items():
|
22
|
+
base_value = merged_data.get(field_name)
|
23
|
+
|
24
|
+
match (base_value, field_value):
|
25
|
+
case (list(), list()):
|
26
|
+
merged_data[field_name] = [
|
27
|
+
*base_value,
|
28
|
+
*(item for item in field_value if item not in base_value),
|
29
|
+
]
|
30
|
+
case (dict(), dict()):
|
31
|
+
merged_data[field_name] = base_value | field_value
|
32
|
+
case _:
|
33
|
+
merged_data[field_name] = field_value
|
34
|
+
|
35
|
+
return base.__class__.model_validate(merged_data)
|
schemez/schema.py
CHANGED
@@ -1,67 +1,102 @@
|
|
1
|
-
"""Configuration models for Schemez."""
|
2
|
-
|
3
|
-
from __future__ import annotations
|
4
|
-
|
5
|
-
import os
|
6
|
-
from typing import Self
|
7
|
-
|
8
|
-
from pydantic import BaseModel, ConfigDict
|
9
|
-
import upath
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
"""
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
1
|
+
"""Configuration models for Schemez."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import os
|
6
|
+
from typing import TYPE_CHECKING, Any, Self
|
7
|
+
|
8
|
+
from pydantic import BaseModel, ConfigDict
|
9
|
+
import upath
|
10
|
+
|
11
|
+
|
12
|
+
if TYPE_CHECKING:
|
13
|
+
from collections.abc import Callable
|
14
|
+
|
15
|
+
|
16
|
+
StrPath = str | os.PathLike[str]
|
17
|
+
|
18
|
+
|
19
|
+
class Schema(BaseModel):
|
20
|
+
"""Base class configuration models.
|
21
|
+
|
22
|
+
Provides:
|
23
|
+
- Common Pydantic settings
|
24
|
+
- YAML serialization
|
25
|
+
- Basic merge functionality
|
26
|
+
"""
|
27
|
+
|
28
|
+
model_config = ConfigDict(extra="forbid", use_attribute_docstrings=True)
|
29
|
+
|
30
|
+
def merge(self, other: Self) -> Self:
|
31
|
+
"""Merge with another instance by overlaying its non-None values."""
|
32
|
+
from schemez.helpers import merge_models
|
33
|
+
|
34
|
+
return merge_models(self, other)
|
35
|
+
|
36
|
+
@classmethod
|
37
|
+
def from_yaml(cls, content: str, inherit_path: StrPath | None = None) -> Self:
|
38
|
+
"""Create from YAML string."""
|
39
|
+
import yamling
|
40
|
+
|
41
|
+
data = yamling.load_yaml(content, resolve_inherit=inherit_path or False)
|
42
|
+
return cls.model_validate(data)
|
43
|
+
|
44
|
+
@classmethod
|
45
|
+
def for_function(
|
46
|
+
cls, func: Callable[..., Any], *, name: str | None = None
|
47
|
+
) -> type[Schema]:
|
48
|
+
"""Create a schema model from a function's signature.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
func: The function to create a schema from
|
52
|
+
name: Optional name for the model
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
A new schema model class based on the function parameters
|
56
|
+
"""
|
57
|
+
from schemez.convert import get_function_model
|
58
|
+
|
59
|
+
return get_function_model(func, name=name)
|
60
|
+
|
61
|
+
@classmethod
|
62
|
+
def for_class_ctor(cls, target_cls: type) -> type[Schema]:
|
63
|
+
"""Create a schema model from a class constructor.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
target_cls: The class whose constructor to convert
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
A new schema model class based on the constructor parameters
|
70
|
+
"""
|
71
|
+
from schemez.convert import get_ctor_basemodel
|
72
|
+
|
73
|
+
return get_ctor_basemodel(target_cls)
|
74
|
+
|
75
|
+
def model_dump_yaml(self) -> str:
|
76
|
+
"""Dump configuration to YAML string."""
|
77
|
+
import yamling
|
78
|
+
|
79
|
+
return yamling.dump_yaml(self.model_dump(exclude_none=True))
|
80
|
+
|
81
|
+
def save(self, path: StrPath, overwrite: bool = False) -> None:
|
82
|
+
"""Save configuration to a YAML file.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
path: Path to save the configuration to
|
86
|
+
overwrite: Whether to overwrite an existing file
|
87
|
+
|
88
|
+
Raises:
|
89
|
+
OSError: If file cannot be written
|
90
|
+
ValueError: If path is invalid
|
91
|
+
"""
|
92
|
+
yaml_str = self.model_dump_yaml()
|
93
|
+
try:
|
94
|
+
file_path = upath.UPath(path)
|
95
|
+
if file_path.exists() and not overwrite:
|
96
|
+
msg = f"File already exists: {path}"
|
97
|
+
raise FileExistsError(msg) # noqa: TRY301
|
98
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
99
|
+
file_path.write_text(yaml_str)
|
100
|
+
except Exception as exc:
|
101
|
+
msg = f"Failed to save configuration to {path}"
|
102
|
+
raise ValueError(msg) from exc
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: schemez
|
3
|
-
Version: 0.0
|
3
|
+
Version: 0.1.0
|
4
4
|
Summary: Pydantic shim for config stuff
|
5
5
|
Project-URL: Documentation, https://phil65.github.io/schemez/
|
6
6
|
Project-URL: Source, https://github.com/phil65/schemez
|
@@ -46,6 +46,7 @@ Classifier: Topic :: Software Development
|
|
46
46
|
Classifier: Topic :: Utilities
|
47
47
|
Classifier: Typing :: Typed
|
48
48
|
Requires-Python: >=3.12
|
49
|
+
Requires-Dist: griffe>=1.7.3
|
49
50
|
Requires-Dist: pydantic
|
50
51
|
Requires-Dist: universal-pathlib>=0.2.6
|
51
52
|
Description-Content-Type: text/markdown
|
@@ -0,0 +1,10 @@
|
|
1
|
+
schemez/__init__.py,sha256=VpbilATEu92pmAaUaenQPcyPCRA2itKLdszXxI-pakM,80
|
2
|
+
schemez/convert.py,sha256=b6Sz11lq0HvpXfMREOqnnw8rcVg2XzTKhjjPNc4YIoE,4403
|
3
|
+
schemez/docstrings.py,sha256=kmd660wcomXzKac0SSNYxPRNbVCUovrpmE9jwnVRS6c,4115
|
4
|
+
schemez/helpers.py,sha256=_leGedEf5AoeQOV0eyrJpDnvDOPB5XV3pd5YNANASeI,1081
|
5
|
+
schemez/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
schemez/schema.py,sha256=qlkNigpDQJIopjSjfS4yp8vXReCr2o2eWBEDjIN7YjM,3021
|
7
|
+
schemez-0.1.0.dist-info/METADATA,sha256=N5ew7ne2W8qznqDjxcI15hMDPbzzDJRC_Hw4U11ECRw,5722
|
8
|
+
schemez-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
9
|
+
schemez-0.1.0.dist-info/licenses/LICENSE,sha256=AteGCH9r177TxxrOFEiOARrastASsf7yW6MQxlAHdwA,1078
|
10
|
+
schemez-0.1.0.dist-info/RECORD,,
|
schemez-0.0.1.dist-info/RECORD
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
schemez/__init__.py,sha256=8GownnUkTUVUdUuosh5mCOW9OqoY_9-_66_BmQL9yEM,80
|
2
|
-
schemez/helpers.py,sha256=bbmtpB9hz3iyc7u9zpuOPQnUuwj5J751C3RtmXs8PzU,1116
|
3
|
-
schemez/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
schemez/schema.py,sha256=6rubAfdwdcmFRP8SfPQpfyuR674uuIy9u3zo7PndlWU,2070
|
5
|
-
schemez-0.0.1.dist-info/METADATA,sha256=o78fWu1GlwqnJGwjWGZVNGyVETwb66CWcBMzNsQoYv8,5693
|
6
|
-
schemez-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
7
|
-
schemez-0.0.1.dist-info/licenses/LICENSE,sha256=AteGCH9r177TxxrOFEiOARrastASsf7yW6MQxlAHdwA,1078
|
8
|
-
schemez-0.0.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|