mirror of
https://github.com/p2p-ld/numpydantic.git
synced 2024-11-09 16:24:28 +00:00
Restructuring linkml generators
add monkeypatches
This commit is contained in:
parent
690f9cd53a
commit
c9e7bb243c
14 changed files with 806 additions and 590 deletions
|
@ -1 +1,7 @@
|
|||
|
||||
# ruff: noqa: E402
|
||||
# ruff: noqa: F401
|
||||
from numpydantic.monkeypatch import apply_patches
|
||||
|
||||
apply_patches()
|
||||
|
||||
from numpydantic.ndarray import NDArray
|
||||
|
|
0
numpydantic/linkml/__init__.py
Normal file
0
numpydantic/linkml/__init__.py
Normal file
189
numpydantic/linkml/ndarraygen.py
Normal file
189
numpydantic/linkml/ndarraygen.py
Normal file
|
@ -0,0 +1,189 @@
|
|||
"""
|
||||
Isolated generator for array classes
|
||||
"""
|
||||
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from linkml_runtime.linkml_model import ClassDefinition, SlotDefinition
|
||||
|
||||
from numpydantic.maps import flat_to_nptyping
|
||||
|
||||
|
||||
class ArrayFormat(ABC):
|
||||
"""
|
||||
Metaclass for different LinkML array source formats
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def is_array(cls, cls_: ClassDefinition) -> bool:
|
||||
"""Check whether a given class matches one of our subclasses definitions"""
|
||||
return any([subcls.check(cls_) for subcls in cls.__subclasses__()])
|
||||
|
||||
@classmethod
|
||||
def get(cls, cls_: ClassDefinition) -> type["ArrayFormat"]:
|
||||
"""Get matching ArrayFormat subclass"""
|
||||
for subcls in cls.__subclasses__():
|
||||
if subcls.check(cls_):
|
||||
return subcls
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def check(cls, cls_: ClassDefinition) -> bool:
|
||||
"""Method for array format subclasses to check if they match a given source class"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def make(cls, cls_: ClassDefinition) -> str:
|
||||
"""
|
||||
Make an annotation string from a given array format source class
|
||||
"""
|
||||
|
||||
|
||||
class LinkMLNDArray(ArrayFormat):
|
||||
"""
|
||||
Tentative linkml-arrays style NDArray
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def check(cls, cls_: ClassDefinition) -> bool:
|
||||
"""Check if linkml:NDArray in implements"""
|
||||
return "linkml:NDArray" in cls_.implements
|
||||
|
||||
@classmethod
|
||||
def make(cls, cls_: ClassDefinition) -> str:
|
||||
"""Make NDArray"""
|
||||
raise NotImplementedError("Havent implemented NDArrays yet!")
|
||||
|
||||
|
||||
class LinkMLDataArray(ArrayFormat):
|
||||
"""
|
||||
Tentative linkml-arrays style annotated array with indices
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def check(cls, cls_: ClassDefinition) -> bool:
|
||||
"""Check if linkml:DataArray in implements"""
|
||||
return "linkml:DataArray" in cls_.implements
|
||||
|
||||
@classmethod
|
||||
def make(cls, cls_: ClassDefinition) -> str:
|
||||
"""Make DataArray"""
|
||||
raise NotImplementedError("Havent generated DataArray types yet!")
|
||||
|
||||
|
||||
class NWBLinkMLArraylike(ArrayFormat):
|
||||
"""
|
||||
Ye Olde nwb-linkml Arraylike class
|
||||
|
||||
Examples:
|
||||
|
||||
TimeSeries:
|
||||
is_a: Arraylike
|
||||
attributes:
|
||||
num_times:
|
||||
name: num_times
|
||||
range: AnyType
|
||||
required: true
|
||||
num_DIM2:
|
||||
name: num_DIM2
|
||||
range: AnyType
|
||||
required: false
|
||||
num_DIM3:
|
||||
name: num_DIM3
|
||||
range: AnyType
|
||||
required: false
|
||||
num_DIM4:
|
||||
name: num_DIM4
|
||||
range: AnyType
|
||||
required: false
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def check(cls, cls_: ClassDefinition) -> bool:
|
||||
"""Check if class is Arraylike"""
|
||||
return cls_.is_a == "Arraylike"
|
||||
|
||||
@classmethod
|
||||
def make(cls, cls_: ClassDefinition) -> str:
|
||||
"""Make Arraylike annotation"""
|
||||
return cls._array_annotation(cls_)
|
||||
|
||||
@classmethod
|
||||
def _array_annotation(cls, cls_: ClassDefinition) -> str:
|
||||
"""
|
||||
Make an annotation for an NDArray :)
|
||||
|
||||
Args:
|
||||
cls_:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# if none of the dimensions are optional, we just have one possible array shape
|
||||
if all([s.required for s in cls_.attributes.values()]): # pragma: no cover
|
||||
return cls._make_npytyping_range(cls_.attributes)
|
||||
# otherwise we need to make permutations
|
||||
# but not all permutations, because we typically just want to be able to exlude the last possible dimensions
|
||||
# the array classes should always be well-defined where the optional dimensions are at the end, so
|
||||
requireds = {k: v for k, v in cls_.attributes.items() if v.required}
|
||||
optionals = [(k, v) for k, v in cls_.attributes.items() if not v.required]
|
||||
|
||||
annotations = []
|
||||
if len(requireds) > 0:
|
||||
# first the base case
|
||||
annotations.append(cls._make_npytyping_range(requireds))
|
||||
# then add back each optional dimension
|
||||
for i in range(len(optionals)):
|
||||
attrs = {**requireds, **{k: v for k, v in optionals[0 : i + 1]}}
|
||||
annotations.append(cls._make_npytyping_range(attrs))
|
||||
|
||||
# now combine with a union:
|
||||
union = "Union[\n" + " " * 8
|
||||
union += (",\n" + " " * 8).join(annotations)
|
||||
union += "\n" + " " * 4 + "]"
|
||||
return union
|
||||
|
||||
@classmethod
|
||||
def _make_npytyping_range(cls, attrs: dict[str, SlotDefinition]) -> str:
|
||||
# slot always starts with...
|
||||
prefix = "NDArray["
|
||||
|
||||
# and then we specify the shape:
|
||||
shape_prefix = 'Shape["'
|
||||
|
||||
# using the cardinality from the attributes
|
||||
dim_pieces = []
|
||||
for attr in attrs.values():
|
||||
shape_part = (
|
||||
str(attr.maximum_cardinality) if attr.maximum_cardinality else "*"
|
||||
)
|
||||
|
||||
# do this with the most heinous chain of string replacements rather than regex
|
||||
# because i am still figuring out what needs to be subbed lol
|
||||
name_part = (
|
||||
attr.name.replace(",", "_")
|
||||
.replace(" ", "_")
|
||||
.replace("__", "_")
|
||||
.replace("|", "_")
|
||||
.replace("-", "_")
|
||||
.replace("+", "plus")
|
||||
)
|
||||
|
||||
dim_pieces.append(" ".join([shape_part, name_part]))
|
||||
|
||||
dimension = ", ".join(dim_pieces)
|
||||
|
||||
shape_suffix = '"], '
|
||||
|
||||
# all dimensions should be the same dtype
|
||||
try:
|
||||
dtype = flat_to_nptyping[list(attrs.values())[0].range]
|
||||
except KeyError as e: # pragma: no cover
|
||||
warnings.warn(str(e), stacklevel=2)
|
||||
range = list(attrs.values())[0].range
|
||||
return f"List[{range}] | {range}"
|
||||
suffix = "]"
|
||||
|
||||
slot = "".join([prefix, shape_prefix, dimension, shape_suffix, dtype, suffix])
|
||||
return slot
|
|
@ -1,30 +1,16 @@
|
|||
"""
|
||||
Subclass of :class:`linkml.generators.PydanticGenerator`
|
||||
Patched subclass of :class:`linkml.generators.PydanticGenerator` to generate NDArrays
|
||||
swiped from ``nwb-linkml``.
|
||||
|
||||
The pydantic generator is a subclass of
|
||||
- :class:`linkml.utils.generator.Generator`
|
||||
- :class:`linkml.generators.oocodegen.OOCodeGenerator`
|
||||
Since this is an override of the full generator originally intended for a specific format,
|
||||
this is a bit more involved than the isolated ndarray type generator. The most relevant
|
||||
parts here are:
|
||||
- The :class:`.ArrayCheck` class, which is used to determine when an array needs to be generated -
|
||||
Use this to hook into nonstandard array formats that don't match the usual ``implements`` pattern
|
||||
|
||||
The default `__main__` method
|
||||
- Instantiates the class
|
||||
- Calls :meth:`~linkml.generators.PydanticGenerator.serialize`
|
||||
|
||||
The `serialize` method:
|
||||
|
||||
- Accepts an optional jinja-style template, otherwise it uses the default template
|
||||
- Uses :class:`linkml_runtime.utils.schemaview.SchemaView` to interact with the schema
|
||||
- Generates linkML Classes
|
||||
- `generate_enums` runs first
|
||||
|
||||
.. note::
|
||||
|
||||
This module is heinous. We will be tidying this up and trying to pull changes upstream,
|
||||
but for now this is just our hacky little secret.
|
||||
|
||||
"""
|
||||
import inspect
|
||||
import sys
|
||||
import warnings
|
||||
from copy import copy
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
@ -52,6 +38,9 @@ from linkml_runtime.utils.schemaview import SchemaView
|
|||
# from nwb_linkml.maps import flat_to_nptyping
|
||||
from pydantic import BaseModel
|
||||
|
||||
from numpydantic.linkml.ndarraygen import ArrayFormat
|
||||
from numpydantic.linkml.template import default_template
|
||||
|
||||
|
||||
def module_case(name: str) -> str:
|
||||
"""
|
||||
|
@ -81,184 +70,20 @@ class LinkML_Meta(BaseModel):
|
|||
tree_root: bool = False
|
||||
|
||||
|
||||
def default_template(
|
||||
pydantic_ver: str = "2", extra_classes: list[type[BaseModel]] | None = None
|
||||
) -> str:
|
||||
"""Constructs a default template for pydantic classes based on the version of pydantic"""
|
||||
### HEADER ###
|
||||
template = """
|
||||
{#-
|
||||
|
||||
Jinja2 Template for a pydantic classes
|
||||
-#}
|
||||
from __future__ import annotations
|
||||
from datetime import datetime, date
|
||||
from enum import Enum
|
||||
from typing import Dict, Optional, Any, Union, ClassVar, Annotated, TypeVar, List, TYPE_CHECKING
|
||||
from pydantic import BaseModel as BaseModel, Field"""
|
||||
if pydantic_ver == "2":
|
||||
template += """
|
||||
from pydantic import ConfigDict, BeforeValidator
|
||||
"""
|
||||
template += """
|
||||
from nptyping import Shape, Float, Float32, Double, Float64, LongLong, Int64, Int, Int32, Int16, Short, Int8, UInt, UInt32, UInt16, UInt8, UInt64, Number, String, Unicode, Unicode, Unicode, String, Bool, Datetime64
|
||||
from nwb_linkml.types import NDArray
|
||||
import sys
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
if TYPE_CHECKING:
|
||||
import numpy as np
|
||||
|
||||
{% for import_module, import_classes in imports.items() %}
|
||||
from {{ import_module }} import (
|
||||
{{ import_classes | join(',\n ') }}
|
||||
)
|
||||
{% endfor %}
|
||||
|
||||
metamodel_version = "{{metamodel_version}}"
|
||||
version = "{{version if version else None}}"
|
||||
"""
|
||||
### BASE MODEL ###
|
||||
if pydantic_ver == "1": # pragma: no cover
|
||||
template += """
|
||||
List = BaseList
|
||||
|
||||
class WeakRefShimBaseModel(BaseModel):
|
||||
__slots__ = '__weakref__'
|
||||
|
||||
class ConfiguredBaseModel(WeakRefShimBaseModel,
|
||||
validate_assignment = False,
|
||||
validate_all = True,
|
||||
underscore_attrs_are_private = True,
|
||||
extra = {% if allow_extra %}'allow'{% else %}'forbid'{% endif %},
|
||||
arbitrary_types_allowed = True,
|
||||
use_enum_values = True):
|
||||
"""
|
||||
else:
|
||||
template += """
|
||||
class ConfiguredBaseModel(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
validate_assignment = True,
|
||||
validate_default = True,
|
||||
extra = {% if allow_extra %}'allow'{% else %}'forbid'{% endif %},
|
||||
arbitrary_types_allowed = True,
|
||||
use_enum_values = True
|
||||
def linkml_classvar(cls: ClassDefinition) -> SlotDefinition:
|
||||
"""A class variable that holds additional linkml attrs"""
|
||||
slot = SlotDefinition(name="linkml_meta")
|
||||
slot.annotations["python_range"] = Annotation(
|
||||
"python_range", "ClassVar[LinkML_Meta]"
|
||||
)
|
||||
"""
|
||||
### Injected Fields
|
||||
template += """
|
||||
{%- if injected_fields != None -%}
|
||||
{% for field in injected_fields %}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
{%- else -%}
|
||||
pass
|
||||
{%- endif -%}
|
||||
"""
|
||||
### Getitem
|
||||
template += """
|
||||
|
||||
def __getitem__(self, i: slice|int) -> 'np.ndarray':
|
||||
if hasattr(self, 'array'):
|
||||
return self.array[i]
|
||||
else:
|
||||
return super().__getitem__(i)
|
||||
|
||||
def __setitem__(self, i: slice|int, value: Any):
|
||||
if hasattr(self, 'array'):
|
||||
self.array[i] = value
|
||||
else:
|
||||
super().__setitem__(i, value)
|
||||
"""
|
||||
|
||||
### Extra classes
|
||||
if extra_classes is not None:
|
||||
template += """{{ '\n\n' }}"""
|
||||
for cls in extra_classes:
|
||||
template += inspect.getsource(cls) + "\n\n"
|
||||
### ENUMS ###
|
||||
template += """
|
||||
{% for e in enums.values() %}
|
||||
class {{ e.name }}(str, Enum):
|
||||
{% if e.description -%}
|
||||
\"\"\"
|
||||
{{ e.description }}
|
||||
\"\"\"
|
||||
{%- endif %}
|
||||
{% for _, pv in e['values'].items() -%}
|
||||
{% if pv.description -%}
|
||||
# {{pv.description}}
|
||||
{%- endif %}
|
||||
{{pv.label}} = "{{pv.value}}"
|
||||
{% endfor %}
|
||||
{% if not e['values'] -%}
|
||||
dummy = "dummy"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
"""
|
||||
### CLASSES ###
|
||||
template += """
|
||||
{%- for c in schema.classes.values() %}
|
||||
class {{ c.name }}
|
||||
{%- if class_isa_plus_mixins[c.name] -%}
|
||||
({{class_isa_plus_mixins[c.name]|join(', ')}})
|
||||
{%- else -%}
|
||||
(ConfiguredBaseModel)
|
||||
{%- endif -%}
|
||||
:
|
||||
{% if c.description -%}
|
||||
\"\"\"
|
||||
{{ c.description }}
|
||||
\"\"\"
|
||||
{%- endif %}
|
||||
{% for attr in c.attributes.values() if c.attributes -%}
|
||||
{{attr.name}}:{{ ' ' }}{%- if attr.equals_string -%}
|
||||
Literal[{{ predefined_slot_values[c.name][attr.name] }}]
|
||||
{%- else -%}
|
||||
{{ attr.annotations['python_range'].value }}
|
||||
{%- endif -%}
|
||||
{%- if attr.annotations['fixed_field'] -%}
|
||||
{{ ' ' }}= {{ attr.annotations['fixed_field'].value }}
|
||||
{%- else -%}
|
||||
{{ ' ' }}= Field(
|
||||
{%- if predefined_slot_values[c.name][attr.name] is string -%}
|
||||
{{ predefined_slot_values[c.name][attr.name] }}
|
||||
{%- elif attr.required -%}
|
||||
...
|
||||
{%- else -%}
|
||||
None
|
||||
{%- endif -%}
|
||||
{%- if attr.title != None %}, title="{{attr.title}}"{% endif -%}
|
||||
{%- if attr.description %}, description=\"\"\"{{attr.description}}\"\"\"{% endif -%}
|
||||
{%- if attr.minimum_value != None %}, ge={{attr.minimum_value}}{% endif -%}
|
||||
{%- if attr.maximum_value != None %}, le={{attr.maximum_value}}{% endif -%}
|
||||
meta_fields = {k: getattr(cls, k, None) for k in LinkML_Meta.model_fields}
|
||||
meta_field_strings = [f"{k}={v}" for k, v in meta_fields.items() if v is not None]
|
||||
meta_field_string = ", ".join(meta_field_strings)
|
||||
slot.annotations["fixed_field"] = Annotation(
|
||||
"fixed_field", f"Field(LinkML_Meta({meta_field_string}), frozen=True)"
|
||||
)
|
||||
{%- endif %}
|
||||
{% else -%}
|
||||
None
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
"""
|
||||
### FWD REFS / REBUILD MODEL ###
|
||||
if pydantic_ver == "1": # pragma: no cover
|
||||
template += """
|
||||
# Update forward refs
|
||||
# see https://pydantic-docs.helpmanual.io/usage/postponed_annotations/
|
||||
{% for c in schema.classes.values() -%}
|
||||
{{ c.name }}.update_forward_refs()
|
||||
{% endfor %}
|
||||
"""
|
||||
else:
|
||||
template += """
|
||||
# Model rebuild
|
||||
# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model
|
||||
{% for c in schema.classes.values() -%}
|
||||
{{ c.name }}.model_rebuild()
|
||||
{% endfor %}
|
||||
"""
|
||||
return template
|
||||
|
||||
return slot
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -380,7 +205,7 @@ class PydanticGenerator(BasePydanticGenerator):
|
|||
if not self.split:
|
||||
# we are compiling this whole thing in one big file so we don't import anything
|
||||
return {}
|
||||
if "is_namespace" in sv.schema.annotations.keys() and sv.schema.annotations[
|
||||
if "is_namespace" in sv.schema.annotations and sv.schema.annotations[
|
||||
"is_namespace"
|
||||
]["value"] in ("True", True):
|
||||
return self._get_namespace_imports(sv)
|
||||
|
@ -447,7 +272,7 @@ class PydanticGenerator(BasePydanticGenerator):
|
|||
|
||||
def _check_anyof(
|
||||
self, s: SlotDefinition, sn: SlotDefinitionName, sv: SchemaView
|
||||
): # pragma: no cover
|
||||
) -> None: # pragma: no cover
|
||||
# Confirm that the original slot range (ignoring the default that comes in from
|
||||
# induced_slot) isn't in addition to setting any_of
|
||||
if len(s.any_of) > 0 and sv.get_slot(sn).range is not None:
|
||||
|
@ -459,94 +284,6 @@ class PydanticGenerator(BasePydanticGenerator):
|
|||
if not base_range_subsumes_any_of:
|
||||
raise ValueError("Slot cannot have both range and any_of defined")
|
||||
|
||||
def _make_npytyping_range(self, attrs: dict[str, SlotDefinition]) -> str:
|
||||
# slot always starts with...
|
||||
prefix = "NDArray["
|
||||
|
||||
# and then we specify the shape:
|
||||
shape_prefix = 'Shape["'
|
||||
|
||||
# using the cardinality from the attributes
|
||||
dim_pieces = []
|
||||
for attr in attrs.values():
|
||||
if attr.maximum_cardinality:
|
||||
shape_part = str(attr.maximum_cardinality)
|
||||
else:
|
||||
shape_part = "*"
|
||||
|
||||
# do this with the most heinous chain of string replacements rather than regex
|
||||
# because i am still figuring out what needs to be subbed lol
|
||||
name_part = (
|
||||
attr.name.replace(",", "_")
|
||||
.replace(" ", "_")
|
||||
.replace("__", "_")
|
||||
.replace("|", "_")
|
||||
.replace("-", "_")
|
||||
.replace("+", "plus")
|
||||
)
|
||||
|
||||
dim_pieces.append(" ".join([shape_part, name_part]))
|
||||
|
||||
dimension = ", ".join(dim_pieces)
|
||||
|
||||
shape_suffix = '"], '
|
||||
|
||||
# all dimensions should be the same dtype
|
||||
try:
|
||||
dtype = flat_to_nptyping[list(attrs.values())[0].range]
|
||||
except KeyError as e: # pragma: no cover
|
||||
warnings.warn(str(e))
|
||||
range = list(attrs.values())[0].range
|
||||
return f"List[{range}] | {range}"
|
||||
suffix = "]"
|
||||
|
||||
slot = "".join([prefix, shape_prefix, dimension, shape_suffix, dtype, suffix])
|
||||
return slot
|
||||
|
||||
def _get_numpy_slot_range(self, cls: ClassDefinition) -> str:
|
||||
# if none of the dimensions are optional, we just have one possible array shape
|
||||
if all([s.required for s in cls.attributes.values()]): # pragma: no cover
|
||||
return self._make_npytyping_range(cls.attributes)
|
||||
# otherwise we need to make permutations
|
||||
# but not all permutations, because we typically just want to be able to exlude the last possible dimensions
|
||||
# the array classes should always be well-defined where the optional dimensions are at the end, so
|
||||
requireds = {k: v for k, v in cls.attributes.items() if v.required}
|
||||
optionals = [(k, v) for k, v in cls.attributes.items() if not v.required]
|
||||
|
||||
annotations = []
|
||||
if len(requireds) > 0:
|
||||
# first the base case
|
||||
annotations.append(self._make_npytyping_range(requireds))
|
||||
# then add back each optional dimension
|
||||
for i in range(len(optionals)):
|
||||
attrs = {**requireds, **{k: v for k, v in optionals[0 : i + 1]}}
|
||||
annotations.append(self._make_npytyping_range(attrs))
|
||||
|
||||
# now combine with a union:
|
||||
union = "Union[\n" + " " * 8
|
||||
union += (",\n" + " " * 8).join(annotations)
|
||||
union += "\n" + " " * 4 + "]"
|
||||
return union
|
||||
|
||||
def _get_linkml_classvar(self, cls: ClassDefinition) -> SlotDefinition:
|
||||
"""A class variable that holds additional linkml attrs"""
|
||||
slot = SlotDefinition(name="linkml_meta")
|
||||
slot.annotations["python_range"] = Annotation(
|
||||
"python_range", "ClassVar[LinkML_Meta]"
|
||||
)
|
||||
meta_fields = {
|
||||
k: getattr(cls, k, None) for k in LinkML_Meta.model_fields.keys()
|
||||
}
|
||||
meta_field_strings = [
|
||||
f"{k}={v}" for k, v in meta_fields.items() if v is not None
|
||||
]
|
||||
meta_field_string = ", ".join(meta_field_strings)
|
||||
slot.annotations["fixed_field"] = Annotation(
|
||||
"fixed_field", f"Field(LinkML_Meta({meta_field_string}), frozen=True)"
|
||||
)
|
||||
|
||||
return slot
|
||||
|
||||
def sort_classes(
|
||||
self, clist: list[ClassDefinition], imports: dict[str, list[str]]
|
||||
) -> list[ClassDefinition]:
|
||||
|
@ -564,7 +301,7 @@ class PydanticGenerator(BasePydanticGenerator):
|
|||
|
||||
clist = list(clist)
|
||||
clist = [c for c in clist if c.name not in self.SKIP_CLASSES]
|
||||
slist = [] # type: List[ClassDefinition]
|
||||
slist = [] # type: list[ClassDefinition]
|
||||
while len(clist) > 0:
|
||||
can_add = False
|
||||
for i in range(len(clist)):
|
||||
|
@ -604,8 +341,8 @@ class PydanticGenerator(BasePydanticGenerator):
|
|||
"""
|
||||
sv = self.schemaview
|
||||
range_cls = sv.get_class(slot_range)
|
||||
if range_cls.is_a == "Arraylike":
|
||||
return self._get_numpy_slot_range(range_cls)
|
||||
if ArrayFormat.is_array(range_cls):
|
||||
return ArrayFormat.get(range_cls).make(range_cls)
|
||||
else:
|
||||
return self._get_class_slot_range_origin(
|
||||
slot_range, inlined, inlined_as_list
|
||||
|
@ -619,7 +356,7 @@ class PydanticGenerator(BasePydanticGenerator):
|
|||
|
||||
Overriding to not use strings in the type hint when a class has an identifier value
|
||||
|
||||
Not testing this method except for what we changes
|
||||
Not testing this method except for what we changed
|
||||
"""
|
||||
sv = self.schemaview
|
||||
range_cls = sv.get_class(slot_range)
|
||||
|
@ -732,6 +469,7 @@ class PydanticGenerator(BasePydanticGenerator):
|
|||
return slot_value
|
||||
|
||||
def serialize(self) -> str:
|
||||
"""Generate LinkML models from schema!"""
|
||||
predefined_slot_values = {}
|
||||
"""splitting up parent class :meth:`.get_predefined_slot_values`"""
|
||||
|
||||
|
@ -787,7 +525,7 @@ class PydanticGenerator(BasePydanticGenerator):
|
|||
del class_def.attributes[attribute]
|
||||
|
||||
# make class attr that stores extra linkml attrs
|
||||
class_def.attributes["linkml_meta"] = self._get_linkml_classvar(class_def)
|
||||
class_def.attributes["linkml_meta"] = linkml_classvar(class_def)
|
||||
|
||||
class_name = class_original.name
|
||||
predefined_slot_values[camelcase(class_name)] = {}
|
183
numpydantic/linkml/template.py
Normal file
183
numpydantic/linkml/template.py
Normal file
|
@ -0,0 +1,183 @@
|
|||
import inspect
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
def default_template(
|
||||
pydantic_ver: str = "2", extra_classes: list[type[BaseModel]] | None = None
|
||||
) -> str:
|
||||
"""Constructs a default template for pydantic classes based on the version of pydantic"""
|
||||
### HEADER ###
|
||||
template = """
|
||||
{#-
|
||||
|
||||
Jinja2 Template for a pydantic classes
|
||||
-#}
|
||||
from __future__ import annotations
|
||||
from datetime import datetime, date
|
||||
from enum import Enum
|
||||
from typing import Dict, Optional, Any, Union, ClassVar, Annotated, TypeVar, List, TYPE_CHECKING
|
||||
from pydantic import BaseModel as BaseModel, Field"""
|
||||
if pydantic_ver == "2":
|
||||
template += """
|
||||
from pydantic import ConfigDict, BeforeValidator
|
||||
"""
|
||||
template += """
|
||||
from nptyping import Shape, Float, Float32, Double, Float64, LongLong, Int64, Int, Int32, Int16, Short, Int8, UInt, UInt32, UInt16, UInt8, UInt64, Number, String, Unicode, Unicode, Unicode, String, Bool, Datetime64
|
||||
from nwb_linkml.types import NDArray
|
||||
import sys
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
if TYPE_CHECKING:
|
||||
import numpy as np
|
||||
|
||||
{% for import_module, import_classes in imports.items() %}
|
||||
from {{ import_module }} import (
|
||||
{{ import_classes | join(',\n ') }}
|
||||
)
|
||||
{% endfor %}
|
||||
|
||||
metamodel_version = "{{metamodel_version}}"
|
||||
version = "{{version if version else None}}"
|
||||
"""
|
||||
### BASE MODEL ###
|
||||
if pydantic_ver == "1": # pragma: no cover
|
||||
template += """
|
||||
List = BaseList
|
||||
|
||||
class WeakRefShimBaseModel(BaseModel):
|
||||
__slots__ = '__weakref__'
|
||||
|
||||
class ConfiguredBaseModel(WeakRefShimBaseModel,
|
||||
validate_assignment = False,
|
||||
validate_all = True,
|
||||
underscore_attrs_are_private = True,
|
||||
extra = {% if allow_extra %}'allow'{% else %}'forbid'{% endif %},
|
||||
arbitrary_types_allowed = True,
|
||||
use_enum_values = True):
|
||||
"""
|
||||
else:
|
||||
template += """
|
||||
class ConfiguredBaseModel(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
validate_assignment = True,
|
||||
validate_default = True,
|
||||
extra = {% if allow_extra %}'allow'{% else %}'forbid'{% endif %},
|
||||
arbitrary_types_allowed = True,
|
||||
use_enum_values = True
|
||||
)
|
||||
"""
|
||||
### Injected Fields
|
||||
template += """
|
||||
{%- if injected_fields != None -%}
|
||||
{% for field in injected_fields %}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
{%- else -%}
|
||||
pass
|
||||
{%- endif -%}
|
||||
"""
|
||||
### Getitem
|
||||
template += """
|
||||
|
||||
def __getitem__(self, i: slice|int) -> 'np.ndarray':
|
||||
if hasattr(self, 'array'):
|
||||
return self.array[i]
|
||||
else:
|
||||
return super().__getitem__(i)
|
||||
|
||||
def __setitem__(self, i: slice|int, value: Any):
|
||||
if hasattr(self, 'array'):
|
||||
self.array[i] = value
|
||||
else:
|
||||
super().__setitem__(i, value)
|
||||
"""
|
||||
|
||||
### Extra classes
|
||||
if extra_classes is not None:
|
||||
template += """{{ '\n\n' }}"""
|
||||
for cls in extra_classes:
|
||||
template += inspect.getsource(cls) + "\n\n"
|
||||
### ENUMS ###
|
||||
template += """
|
||||
{% for e in enums.values() %}
|
||||
class {{ e.name }}(str, Enum):
|
||||
{% if e.description -%}
|
||||
\"\"\"
|
||||
{{ e.description }}
|
||||
\"\"\"
|
||||
{%- endif %}
|
||||
{% for _, pv in e['values'].items() -%}
|
||||
{% if pv.description -%}
|
||||
# {{pv.description}}
|
||||
{%- endif %}
|
||||
{{pv.label}} = "{{pv.value}}"
|
||||
{% endfor %}
|
||||
{% if not e['values'] -%}
|
||||
dummy = "dummy"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
"""
|
||||
### CLASSES ###
|
||||
template += """
|
||||
{%- for c in schema.classes.values() %}
|
||||
class {{ c.name }}
|
||||
{%- if class_isa_plus_mixins[c.name] -%}
|
||||
({{class_isa_plus_mixins[c.name]|join(', ')}})
|
||||
{%- else -%}
|
||||
(ConfiguredBaseModel)
|
||||
{%- endif -%}
|
||||
:
|
||||
{% if c.description -%}
|
||||
\"\"\"
|
||||
{{ c.description }}
|
||||
\"\"\"
|
||||
{%- endif %}
|
||||
{% for attr in c.attributes.values() if c.attributes -%}
|
||||
{{attr.name}}:{{ ' ' }}{%- if attr.equals_string -%}
|
||||
Literal[{{ predefined_slot_values[c.name][attr.name] }}]
|
||||
{%- else -%}
|
||||
{{ attr.annotations['python_range'].value }}
|
||||
{%- endif -%}
|
||||
{%- if attr.annotations['fixed_field'] -%}
|
||||
{{ ' ' }}= {{ attr.annotations['fixed_field'].value }}
|
||||
{%- else -%}
|
||||
{{ ' ' }}= Field(
|
||||
{%- if predefined_slot_values[c.name][attr.name] is string -%}
|
||||
{{ predefined_slot_values[c.name][attr.name] }}
|
||||
{%- elif attr.required -%}
|
||||
...
|
||||
{%- else -%}
|
||||
None
|
||||
{%- endif -%}
|
||||
{%- if attr.title != None %}, title="{{attr.title}}"{% endif -%}
|
||||
{%- if attr.description %}, description=\"\"\"{{attr.description}}\"\"\"{% endif -%}
|
||||
{%- if attr.minimum_value != None %}, ge={{attr.minimum_value}}{% endif -%}
|
||||
{%- if attr.maximum_value != None %}, le={{attr.maximum_value}}{% endif -%}
|
||||
)
|
||||
{%- endif %}
|
||||
{% else -%}
|
||||
None
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
"""
|
||||
### FWD REFS / REBUILD MODEL ###
|
||||
if pydantic_ver == "1": # pragma: no cover
|
||||
template += """
|
||||
# Update forward refs
|
||||
# see https://pydantic-docs.helpmanual.io/usage/postponed_annotations/
|
||||
{% for c in schema.classes.values() -%}
|
||||
{{ c.name }}.update_forward_refs()
|
||||
{% endfor %}
|
||||
"""
|
||||
else:
|
||||
template += """
|
||||
# Model rebuild
|
||||
# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model
|
||||
{% for c in schema.classes.values() -%}
|
||||
{{ c.name }}.model_rebuild()
|
||||
{% endfor %}
|
||||
"""
|
||||
return template
|
|
@ -42,3 +42,35 @@ np_to_python = {
|
|||
},
|
||||
**{n: str for n in (np.character, np.str_, np.string_, np.unicode_)},
|
||||
}
|
||||
|
||||
|
||||
flat_to_nptyping = {
|
||||
"float": "Float",
|
||||
"float32": "Float32",
|
||||
"double": "Double",
|
||||
"float64": "Float64",
|
||||
"long": "LongLong",
|
||||
"int64": "Int64",
|
||||
"int": "Int",
|
||||
"int32": "Int32",
|
||||
"int16": "Int16",
|
||||
"short": "Short",
|
||||
"int8": "Int8",
|
||||
"uint": "UInt",
|
||||
"uint32": "UInt32",
|
||||
"uint16": "UInt16",
|
||||
"uint8": "UInt8",
|
||||
"uint64": "UInt64",
|
||||
"numeric": "Number",
|
||||
"text": "String",
|
||||
"utf": "Unicode",
|
||||
"utf8": "Unicode",
|
||||
"utf_8": "Unicode",
|
||||
"string": "Unicode",
|
||||
"str": "Unicode",
|
||||
"ascii": "String",
|
||||
"bool": "Bool",
|
||||
"isodatetime": "Datetime64",
|
||||
"AnyType": "Any",
|
||||
"object": "Object",
|
||||
}
|
||||
|
|
58
numpydantic/monkeypatch.py
Normal file
58
numpydantic/monkeypatch.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
def patch_npytyping_perf() -> None:
|
||||
"""
|
||||
npytyping makes an expensive call to inspect.stack()
|
||||
that makes imports of pydantic models take ~200x longer than
|
||||
they should:
|
||||
|
||||
References:
|
||||
- https://github.com/ramonhagenaars/nptyping/issues/110
|
||||
"""
|
||||
import inspect
|
||||
from types import FrameType
|
||||
|
||||
from nptyping import base_meta_classes, ndarray, recarray
|
||||
from nptyping.pandas_ import dataframe
|
||||
|
||||
# make a new __module__ methods for the affected classes
|
||||
def new_module_ndarray(cls) -> str:
|
||||
return cls._get_module(inspect.currentframe(), "nptyping.ndarray")
|
||||
|
||||
def new_module_recarray(cls) -> str:
|
||||
return cls._get_module(inspect.currentframe(), "nptyping.recarray")
|
||||
|
||||
def new_module_dataframe(cls) -> str:
|
||||
return cls._get_module(inspect.currentframe(), "nptyping.pandas_.dataframe")
|
||||
|
||||
# and a new _get_module method for the parent class
|
||||
def new_get_module(cls, stack: FrameType, module: str) -> str:
|
||||
return (
|
||||
"typing"
|
||||
if inspect.getframeinfo(stack.f_back).function == "formatannotation"
|
||||
else module
|
||||
)
|
||||
|
||||
# now apply the patches
|
||||
ndarray.NDArrayMeta.__module__ = property(new_module_ndarray)
|
||||
recarray.RecArrayMeta.__module__ = property(new_module_recarray)
|
||||
dataframe.DataFrameMeta.__module__ = property(new_module_dataframe)
|
||||
base_meta_classes.SubscriptableMeta._get_module = new_get_module
|
||||
|
||||
|
||||
def patch_nptyping_warnings() -> None:
|
||||
"""
|
||||
nptyping shits out a bunch of numpy deprecation warnings from using
|
||||
olde aliases
|
||||
|
||||
References:
|
||||
- https://github.com/ramonhagenaars/nptyping/issues/113
|
||||
- https://github.com/ramonhagenaars/nptyping/issues/102
|
||||
"""
|
||||
import warnings
|
||||
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning, module="nptyping.*")
|
||||
|
||||
|
||||
def apply_patches() -> None:
|
||||
"""Apply all monkeypatches!"""
|
||||
patch_npytyping_perf()
|
||||
patch_nptyping_warnings()
|
|
@ -24,8 +24,7 @@ from pydantic_core.core_schema import ListSchema
|
|||
|
||||
from numpydantic.maps import np_to_python
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from numpydantic.proxy import NDArrayProxy
|
||||
from numpydantic.proxy import NDArrayProxy
|
||||
|
||||
COMPRESSION_THRESHOLD = 16 * 1024
|
||||
"""
|
||||
|
@ -33,7 +32,7 @@ Arrays larger than this size (in bytes) will be compressed and b64 encoded when
|
|||
serializing to JSON.
|
||||
"""
|
||||
|
||||
ARRAY_TYPES = Union[np.ndarray, DaskArray, "NDArrayProxy"]
|
||||
ARRAY_TYPES = np.ndarray | DaskArray | NDArrayProxy
|
||||
|
||||
|
||||
def list_of_lists_schema(shape: Shape, array_type_handler: dict) -> ListSchema:
|
||||
|
@ -153,7 +152,17 @@ class NDArrayMeta(_NDArrayMeta, implementation="NDArray"):
|
|||
|
||||
class NDArray(NPTypingType, metaclass=NDArrayMeta):
|
||||
"""
|
||||
Following the example here: https://docs.pydantic.dev/latest/usage/types/custom/#handling-third-party-types
|
||||
Constrained array type allowing npytyping syntax for dtype and shape validation and serialization.
|
||||
|
||||
Integrates with pydantic such that
|
||||
- JSON schema for list of list encoding
|
||||
- Serialized as LoL, with automatic compression for large arrays
|
||||
- Automatic coercion from lists on instantiation
|
||||
|
||||
Also supports validation on :class:`.NDArrayProxy` types for lazy loading.
|
||||
|
||||
References:
|
||||
- https://docs.pydantic.dev/latest/usage/types/custom/#handling-third-party-types
|
||||
"""
|
||||
|
||||
__args__ = (Any, Any)
|
||||
|
|
|
@ -43,11 +43,6 @@ class NDArrayProxy:
|
|||
_source_type: _NDArray,
|
||||
_handler: Callable[[Any], core_schema.CoreSchema],
|
||||
) -> core_schema.CoreSchema:
|
||||
# return core_schema.no_info_after_validator_function(
|
||||
# serialization=core_schema.plain_serializer_function_ser_schema(
|
||||
# lambda array: array.tolist(),
|
||||
# when_used='json'
|
||||
# )
|
||||
# )
|
||||
from numpydantic import NDArray
|
||||
|
||||
return NDArray_.__get_pydantic_core_schema__(cls, _source_type, _handler)
|
||||
return NDArray.__get_pydantic_core_schema__(cls, _source_type, _handler)
|
||||
|
|
329
poetry.lock
generated
329
poetry.lock
generated
|
@ -479,13 +479,13 @@ yaml = ["PyYAML (>=3.10)"]
|
|||
|
||||
[[package]]
|
||||
name = "curies"
|
||||
version = "0.7.6"
|
||||
version = "0.7.7"
|
||||
description = "Idiomatic conversion between URIs and compact URIs (CURIEs)."
|
||||
optional = true
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "curies-0.7.6-py3-none-any.whl", hash = "sha256:3307e757e47ed4384edb705c73cad40ad5e688e2dea263a60e6a5e5a6c33105d"},
|
||||
{file = "curies-0.7.6.tar.gz", hash = "sha256:f86da3539cee349249f5b64db99651053649551920b9fe945c150719c8b9b40e"},
|
||||
{file = "curies-0.7.7-py3-none-any.whl", hash = "sha256:609de3e8cdf39f410e8f4d9f06eb7df379465860f4fb441bf0e79672430f8e2a"},
|
||||
{file = "curies-0.7.7.tar.gz", hash = "sha256:a8d674029f906fb9c3564eafa0862ce96725932bd801fa751e076265b111cb34"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -619,13 +619,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "fsspec"
|
||||
version = "2023.12.2"
|
||||
version = "2024.2.0"
|
||||
description = "File-system specification"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "fsspec-2023.12.2-py3-none-any.whl", hash = "sha256:d800d87f72189a745fa3d6b033b9dc4a34ad069f60ca60b943a63599f5501960"},
|
||||
{file = "fsspec-2023.12.2.tar.gz", hash = "sha256:8548d39e8810b59c38014934f6b31e57f40c1b20f911f4cc2b85389c7e9bf0cb"},
|
||||
{file = "fsspec-2024.2.0-py3-none-any.whl", hash = "sha256:817f969556fa5916bc682e02ca2045f96ff7f586d45110fcb76022063ad2c7d8"},
|
||||
{file = "fsspec-2024.2.0.tar.gz", hash = "sha256:b6ad1a679f760dda52b1168c859d01b7b80648ea6f7f7c7f5a8a91dc3f3ecb84"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
@ -643,7 +643,7 @@ github = ["requests"]
|
|||
gs = ["gcsfs"]
|
||||
gui = ["panel"]
|
||||
hdfs = ["pyarrow (>=1)"]
|
||||
http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "requests"]
|
||||
http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"]
|
||||
libarchive = ["libarchive-c"]
|
||||
oci = ["ocifs"]
|
||||
s3 = ["s3fs"]
|
||||
|
@ -1170,71 +1170,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
|
|||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.4"
|
||||
version = "2.1.5"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
optional = true
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"},
|
||||
{file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"},
|
||||
{file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"},
|
||||
{file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"},
|
||||
{file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"},
|
||||
{file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"},
|
||||
{file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"},
|
||||
{file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
|
||||
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1625,18 +1625,18 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.6.0"
|
||||
version = "2.6.1"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.6.0-py3-none-any.whl", hash = "sha256:1440966574e1b5b99cf75a13bec7b20e3512e8a61b894ae252f56275e2c465ae"},
|
||||
{file = "pydantic-2.6.0.tar.gz", hash = "sha256:ae887bd94eb404b09d86e4d12f93893bdca79d766e738528c6fa1c849f3c6bcf"},
|
||||
{file = "pydantic-2.6.1-py3-none-any.whl", hash = "sha256:0b6a909df3192245cb736509a92ff69e4fef76116feffec68e93a567347bae6f"},
|
||||
{file = "pydantic-2.6.1.tar.gz", hash = "sha256:4fd5c182a2488dc63e6d32737ff19937888001e2a6d86e94b3f233104a5d1fa9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.4.0"
|
||||
pydantic-core = "2.16.1"
|
||||
pydantic-core = "2.16.2"
|
||||
typing-extensions = ">=4.6.1"
|
||||
|
||||
[package.extras]
|
||||
|
@ -1644,90 +1644,90 @@ email = ["email-validator (>=2.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.16.1"
|
||||
version = "2.16.2"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:300616102fb71241ff477a2cbbc847321dbec49428434a2f17f37528721c4948"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5511f962dd1b9b553e9534c3b9c6a4b0c9ded3d8c2be96e61d56f933feef9e1f"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98f0edee7ee9cc7f9221af2e1b95bd02810e1c7a6d115cfd82698803d385b28f"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9795f56aa6b2296f05ac79d8a424e94056730c0b860a62b0fdcfe6340b658cc8"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45f62e4107ebd05166717ac58f6feb44471ed450d07fecd90e5f69d9bf03c48"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462d599299c5971f03c676e2b63aa80fec5ebc572d89ce766cd11ca8bcb56f3f"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ebaa4bf6386a3b22eec518da7d679c8363fb7fb70cf6972161e5542f470798"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:99f9a50b56713a598d33bc23a9912224fc5d7f9f292444e6664236ae471ddf17"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8ec364e280db4235389b5e1e6ee924723c693cbc98e9d28dc1767041ff9bc388"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:653a5dfd00f601a0ed6654a8b877b18d65ac32c9d9997456e0ab240807be6cf7"},
|
||||
{file = "pydantic_core-2.16.1-cp310-none-win32.whl", hash = "sha256:1661c668c1bb67b7cec96914329d9ab66755911d093bb9063c4c8914188af6d4"},
|
||||
{file = "pydantic_core-2.16.1-cp310-none-win_amd64.whl", hash = "sha256:561be4e3e952c2f9056fba5267b99be4ec2afadc27261505d4992c50b33c513c"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:102569d371fadc40d8f8598a59379c37ec60164315884467052830b28cc4e9da"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:735dceec50fa907a3c314b84ed609dec54b76a814aa14eb90da31d1d36873a5e"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e83ebbf020be727d6e0991c1b192a5c2e7113eb66e3def0cd0c62f9f266247e4"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30a8259569fbeec49cfac7fda3ec8123486ef1b729225222f0d41d5f840b476f"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920c4897e55e2881db6a6da151198e5001552c3777cd42b8a4c2f72eedc2ee91"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5247a3d74355f8b1d780d0f3b32a23dd9f6d3ff43ef2037c6dcd249f35ecf4c"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5bea8012df5bb6dda1e67d0563ac50b7f64a5d5858348b5c8cb5043811c19d"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ed3025a8a7e5a59817b7494686d449ebfbe301f3e757b852c8d0d1961d6be864"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06f0d5a1d9e1b7932477c172cc720b3b23c18762ed7a8efa8398298a59d177c7"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:150ba5c86f502c040b822777e2e519b5625b47813bd05f9273a8ed169c97d9ae"},
|
||||
{file = "pydantic_core-2.16.1-cp311-none-win32.whl", hash = "sha256:d6cbdf12ef967a6aa401cf5cdf47850559e59eedad10e781471c960583f25aa1"},
|
||||
{file = "pydantic_core-2.16.1-cp311-none-win_amd64.whl", hash = "sha256:afa01d25769af33a8dac0d905d5c7bb2d73c7c3d5161b2dd6f8b5b5eea6a3c4c"},
|
||||
{file = "pydantic_core-2.16.1-cp311-none-win_arm64.whl", hash = "sha256:1a2fe7b00a49b51047334d84aafd7e39f80b7675cad0083678c58983662da89b"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f478ec204772a5c8218e30eb813ca43e34005dff2eafa03931b3d8caef87d51"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1936ef138bed2165dd8573aa65e3095ef7c2b6247faccd0e15186aabdda7f66"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d3a433ef5dc3021c9534a58a3686c88363c591974c16c54a01af7efd741f13"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd88f40f2294440d3f3c6308e50d96a0d3d0973d6f1a5732875d10f569acef49"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fac641bbfa43d5a1bed99d28aa1fded1984d31c670a95aac1bf1d36ac6ce137"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72bf9308a82b75039b8c8edd2be2924c352eda5da14a920551a8b65d5ee89253"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb4363e6c9fc87365c2bc777a1f585a22f2f56642501885ffc7942138499bf54"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:20f724a023042588d0f4396bbbcf4cffd0ddd0ad3ed4f0d8e6d4ac4264bae81e"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fb4370b15111905bf8b5ba2129b926af9470f014cb0493a67d23e9d7a48348e8"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23632132f1fd608034f1a56cc3e484be00854db845b3a4a508834be5a6435a6f"},
|
||||
{file = "pydantic_core-2.16.1-cp312-none-win32.whl", hash = "sha256:b9f3e0bffad6e238f7acc20c393c1ed8fab4371e3b3bc311020dfa6020d99212"},
|
||||
{file = "pydantic_core-2.16.1-cp312-none-win_amd64.whl", hash = "sha256:a0b4cfe408cd84c53bab7d83e4209458de676a6ec5e9c623ae914ce1cb79b96f"},
|
||||
{file = "pydantic_core-2.16.1-cp312-none-win_arm64.whl", hash = "sha256:d195add190abccefc70ad0f9a0141ad7da53e16183048380e688b466702195dd"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:502c062a18d84452858f8aea1e520e12a4d5228fc3621ea5061409d666ea1706"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8c032ccee90b37b44e05948b449a2d6baed7e614df3d3f47fe432c952c21b60"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920f4633bee43d7a2818e1a1a788906df5a17b7ab6fe411220ed92b42940f818"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f5d37ff01edcbace53a402e80793640c25798fb7208f105d87a25e6fcc9ea06"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:399166f24c33a0c5759ecc4801f040dbc87d412c1a6d6292b2349b4c505effc9"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac89ccc39cd1d556cc72d6752f252dc869dde41c7c936e86beac5eb555041b66"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73802194f10c394c2bedce7a135ba1d8ba6cff23adf4217612bfc5cf060de34c"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fa00fa24ffd8c31fac081bf7be7eb495be6d248db127f8776575a746fa55c95"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:601d3e42452cd4f2891c13fa8c70366d71851c1593ed42f57bf37f40f7dca3c8"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07982b82d121ed3fc1c51faf6e8f57ff09b1325d2efccaa257dd8c0dd937acca"},
|
||||
{file = "pydantic_core-2.16.1-cp38-none-win32.whl", hash = "sha256:d0bf6f93a55d3fa7a079d811b29100b019784e2ee6bc06b0bb839538272a5610"},
|
||||
{file = "pydantic_core-2.16.1-cp38-none-win_amd64.whl", hash = "sha256:fbec2af0ebafa57eb82c18c304b37c86a8abddf7022955d1742b3d5471a6339e"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a497be217818c318d93f07e14502ef93d44e6a20c72b04c530611e45e54c2196"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:694a5e9f1f2c124a17ff2d0be613fd53ba0c26de588eb4bdab8bca855e550d95"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4dfc66abea3ec6d9f83e837a8f8a7d9d3a76d25c9911735c76d6745950e62c"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8655f55fe68c4685673265a650ef71beb2d31871c049c8b80262026f23605ee3"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21e3298486c4ea4e4d5cc6fb69e06fb02a4e22089304308817035ac006a7f506"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71b4a48a7427f14679f0015b13c712863d28bb1ab700bd11776a5368135c7d60"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dca874e35bb60ce4f9f6665bfbfad050dd7573596608aeb9e098621ac331dc"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa496cd45cda0165d597e9d6f01e36c33c9508f75cf03c0a650018c5048f578e"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5317c04349472e683803da262c781c42c5628a9be73f4750ac7d13040efb5d2d"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c29d54ed4501a30cd71015bf982fa95e4a60117b44e1a200290ce687d3e640"},
|
||||
{file = "pydantic_core-2.16.1-cp39-none-win32.whl", hash = "sha256:ba07646f35e4e49376c9831130039d1b478fbfa1215ae62ad62d2ee63cf9c18f"},
|
||||
{file = "pydantic_core-2.16.1-cp39-none-win_amd64.whl", hash = "sha256:2133b0e412a47868a358713287ff9f9a328879da547dc88be67481cdac529118"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d25ef0c33f22649b7a088035fd65ac1ce6464fa2876578df1adad9472f918a76"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99c095457eea8550c9fa9a7a992e842aeae1429dab6b6b378710f62bfb70b394"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b49c604ace7a7aa8af31196abbf8f2193be605db6739ed905ecaf62af31ccae0"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56da23034fe66221f2208c813d8aa509eea34d97328ce2add56e219c3a9f41c"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cebf8d56fee3b08ad40d332a807ecccd4153d3f1ba8231e111d9759f02edfd05"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1ae8048cba95f382dba56766525abca438328455e35c283bb202964f41a780b0"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:780daad9e35b18d10d7219d24bfb30148ca2afc309928e1d4d53de86822593dc"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c94b5537bf6ce66e4d7830c6993152940a188600f6ae044435287753044a8fe2"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:adf28099d061a25fbcc6531febb7a091e027605385de9fe14dd6a97319d614cf"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:644904600c15816a1f9a1bafa6aab0d21db2788abcdf4e2a77951280473f33e1"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bce04f09f0552b66fca0c4e10da78d17cb0e71c205864bab4e9595122cb9d9"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877045a7969ace04d59516d5d6a7dee13106822f99a5d8df5e6822941f7bedc8"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9c46e556ee266ed3fb7b7a882b53df3c76b45e872fdab8d9cf49ae5e91147fd7"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4eebbd049008eb800f519578e944b8dc8e0f7d59a5abb5924cc2d4ed3a1834ff"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c0be58529d43d38ae849a91932391eb93275a06b93b79a8ab828b012e916a206"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b1fc07896fc1851558f532dffc8987e526b682ec73140886c831d773cef44b76"},
|
||||
{file = "pydantic_core-2.16.1.tar.gz", hash = "sha256:daff04257b49ab7f4b3f73f98283d3dbb1a65bf3500d55c7beac3c66c310fe34"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3fab4e75b8c525a4776e7630b9ee48aea50107fea6ca9f593c98da3f4d11bf7c"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8bde5b48c65b8e807409e6f20baee5d2cd880e0fad00b1a811ebc43e39a00ab2"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2924b89b16420712e9bb8192396026a8fbd6d8726224f918353ac19c4c043d2a"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16aa02e7a0f539098e215fc193c8926c897175d64c7926d00a36188917717a05"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:936a787f83db1f2115ee829dd615c4f684ee48ac4de5779ab4300994d8af325b"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:459d6be6134ce3b38e0ef76f8a672924460c455d45f1ad8fdade36796df1ddc8"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9ee4febb249c591d07b2d4dd36ebcad0ccd128962aaa1801508320896575ef"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40a0bd0bed96dae5712dab2aba7d334a6c67cbcac2ddfca7dbcc4a8176445990"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:870dbfa94de9b8866b37b867a2cb37a60c401d9deb4a9ea392abf11a1f98037b"},
|
||||
{file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:308974fdf98046db28440eb3377abba274808bf66262e042c412eb2adf852731"},
|
||||
{file = "pydantic_core-2.16.2-cp310-none-win32.whl", hash = "sha256:a477932664d9611d7a0816cc3c0eb1f8856f8a42435488280dfbf4395e141485"},
|
||||
{file = "pydantic_core-2.16.2-cp310-none-win_amd64.whl", hash = "sha256:8f9142a6ed83d90c94a3efd7af8873bf7cefed2d3d44387bf848888482e2d25f"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:406fac1d09edc613020ce9cf3f2ccf1a1b2f57ab00552b4c18e3d5276c67eb11"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce232a6170dd6532096cadbf6185271e4e8c70fc9217ebe105923ac105da9978"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a90fec23b4b05a09ad988e7a4f4e081711a90eb2a55b9c984d8b74597599180f"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8aafeedb6597a163a9c9727d8a8bd363a93277701b7bfd2749fbefee2396469e"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9957433c3a1b67bdd4c63717eaf174ebb749510d5ea612cd4e83f2d9142f3fc8"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0d7a9165167269758145756db43a133608a531b1e5bb6a626b9ee24bc38a8f7"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dffaf740fe2e147fedcb6b561353a16243e654f7fe8e701b1b9db148242e1272"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ed79883b4328b7f0bd142733d99c8e6b22703e908ec63d930b06be3a0e7113"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cf903310a34e14651c9de056fcc12ce090560864d5a2bb0174b971685684e1d8"},
|
||||
{file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46b0d5520dbcafea9a8645a8164658777686c5c524d381d983317d29687cce97"},
|
||||
{file = "pydantic_core-2.16.2-cp311-none-win32.whl", hash = "sha256:70651ff6e663428cea902dac297066d5c6e5423fda345a4ca62430575364d62b"},
|
||||
{file = "pydantic_core-2.16.2-cp311-none-win_amd64.whl", hash = "sha256:98dc6f4f2095fc7ad277782a7c2c88296badcad92316b5a6e530930b1d475ebc"},
|
||||
{file = "pydantic_core-2.16.2-cp311-none-win_arm64.whl", hash = "sha256:ef6113cd31411eaf9b39fc5a8848e71c72656fd418882488598758b2c8c6dfa0"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:88646cae28eb1dd5cd1e09605680c2b043b64d7481cdad7f5003ebef401a3039"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7b883af50eaa6bb3299780651e5be921e88050ccf00e3e583b1e92020333304b"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bf26c2e2ea59d32807081ad51968133af3025c4ba5753e6a794683d2c91bf6e"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99af961d72ac731aae2a1b55ccbdae0733d816f8bfb97b41909e143de735f522"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02906e7306cb8c5901a1feb61f9ab5e5c690dbbeaa04d84c1b9ae2a01ebe9379"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5362d099c244a2d2f9659fb3c9db7c735f0004765bbe06b99be69fbd87c3f15"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ac426704840877a285d03a445e162eb258924f014e2f074e209d9b4ff7bf380"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b94cbda27267423411c928208e89adddf2ea5dd5f74b9528513f0358bba019cb"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6db58c22ac6c81aeac33912fb1af0e930bc9774166cdd56eade913d5f2fff35e"},
|
||||
{file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396fdf88b1b503c9c59c84a08b6833ec0c3b5ad1a83230252a9e17b7dfb4cffc"},
|
||||
{file = "pydantic_core-2.16.2-cp312-none-win32.whl", hash = "sha256:7c31669e0c8cc68400ef0c730c3a1e11317ba76b892deeefaf52dcb41d56ed5d"},
|
||||
{file = "pydantic_core-2.16.2-cp312-none-win_amd64.whl", hash = "sha256:a3b7352b48fbc8b446b75f3069124e87f599d25afb8baa96a550256c031bb890"},
|
||||
{file = "pydantic_core-2.16.2-cp312-none-win_arm64.whl", hash = "sha256:a9e523474998fb33f7c1a4d55f5504c908d57add624599e095c20fa575b8d943"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ae34418b6b389d601b31153b84dce480351a352e0bb763684a1b993d6be30f17"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:732bd062c9e5d9582a30e8751461c1917dd1ccbdd6cafb032f02c86b20d2e7ec"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b52776a2e3230f4854907a1e0946eec04d41b1fc64069ee774876bbe0eab55"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef551c053692b1e39e3f7950ce2296536728871110e7d75c4e7753fb30ca87f4"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ebb892ed8599b23fa8f1799e13a12c87a97a6c9d0f497525ce9858564c4575a4"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa6c8c582036275997a733427b88031a32ffa5dfc3124dc25a730658c47a572f"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ba0884a91f1aecce75202473ab138724aa4fb26d7707f2e1fa6c3e68c84fbf"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7924e54f7ce5d253d6160090ddc6df25ed2feea25bfb3339b424a9dd591688bc"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69a7b96b59322a81c2203be537957313b07dd333105b73db0b69212c7d867b4b"},
|
||||
{file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7e6231aa5bdacda78e96ad7b07d0c312f34ba35d717115f4b4bff6cb87224f0f"},
|
||||
{file = "pydantic_core-2.16.2-cp38-none-win32.whl", hash = "sha256:41dac3b9fce187a25c6253ec79a3f9e2a7e761eb08690e90415069ea4a68ff7a"},
|
||||
{file = "pydantic_core-2.16.2-cp38-none-win_amd64.whl", hash = "sha256:f685dbc1fdadb1dcd5b5e51e0a378d4685a891b2ddaf8e2bba89bd3a7144e44a"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:55749f745ebf154c0d63d46c8c58594d8894b161928aa41adbb0709c1fe78b77"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b30b0dd58a4509c3bd7eefddf6338565c4905406aee0c6e4a5293841411a1286"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18de31781cdc7e7b28678df7c2d7882f9692ad060bc6ee3c94eb15a5d733f8f7"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5864b0242f74b9dd0b78fd39db1768bc3f00d1ffc14e596fd3e3f2ce43436a33"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8f9186ca45aee030dc8234118b9c0784ad91a0bb27fc4e7d9d6608a5e3d386c"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc6f6c9be0ab6da37bc77c2dda5f14b1d532d5dbef00311ee6e13357a418e646"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa057095f621dad24a1e906747179a69780ef45cc8f69e97463692adbcdae878"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ad84731a26bcfb299f9eab56c7932d46f9cad51c52768cace09e92a19e4cf55"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3b052c753c4babf2d1edc034c97851f867c87d6f3ea63a12e2700f159f5c41c3"},
|
||||
{file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0f686549e32ccdb02ae6f25eee40cc33900910085de6aa3790effd391ae10c2"},
|
||||
{file = "pydantic_core-2.16.2-cp39-none-win32.whl", hash = "sha256:7afb844041e707ac9ad9acad2188a90bffce2c770e6dc2318be0c9916aef1469"},
|
||||
{file = "pydantic_core-2.16.2-cp39-none-win_amd64.whl", hash = "sha256:9da90d393a8227d717c19f5397688a38635afec89f2e2d7af0df037f3249c39a"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f60f920691a620b03082692c378661947d09415743e437a7478c309eb0e4f82"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:47924039e785a04d4a4fa49455e51b4eb3422d6eaacfde9fc9abf8fdef164e8a"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6294e76b0380bb7a61eb8a39273c40b20beb35e8c87ee101062834ced19c545"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe56851c3f1d6f5384b3051c536cc81b3a93a73faf931f404fef95217cf1e10d"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d776d30cde7e541b8180103c3f294ef7c1862fd45d81738d156d00551005784"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:72f7919af5de5ecfaf1eba47bf9a5d8aa089a3340277276e5636d16ee97614d7"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:4bfcbde6e06c56b30668a0c872d75a7ef3025dc3c1823a13cf29a0e9b33f67e8"},
|
||||
{file = "pydantic_core-2.16.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ff7c97eb7a29aba230389a2661edf2e9e06ce616c7e35aa764879b6894a44b25"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9b5f13857da99325dcabe1cc4e9e6a3d7b2e2c726248ba5dd4be3e8e4a0b6d0e"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a7e41e3ada4cca5f22b478c08e973c930e5e6c7ba3588fb8e35f2398cdcc1545"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60eb8ceaa40a41540b9acae6ae7c1f0a67d233c40dc4359c256ad2ad85bdf5e5"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7beec26729d496a12fd23cf8da9944ee338c8b8a17035a560b585c36fe81af20"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22c5f022799f3cd6741e24f0443ead92ef42be93ffda0d29b2597208c94c3753"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:eca58e319f4fd6df004762419612122b2c7e7d95ffafc37e890252f869f3fb2a"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed957db4c33bc99895f3a1672eca7e80e8cda8bd1e29a80536b4ec2153fa9804"},
|
||||
{file = "pydantic_core-2.16.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:459c0d338cc55d099798618f714b21b7ece17eb1a87879f2da20a3ff4c7628e2"},
|
||||
{file = "pydantic_core-2.16.2.tar.gz", hash = "sha256:0ba503850d8b8dcc18391f10de896ae51d37fe5fe43dbfb6a35c5c5cad271a06"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -1853,20 +1853,20 @@ shexjsg = ">=0.8.1"
|
|||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.4.4"
|
||||
version = "8.0.0"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
optional = true
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
|
||||
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
|
||||
{file = "pytest-8.0.0-py3-none-any.whl", hash = "sha256:50fb9cbe836c3f20f0dfa99c565201fb75dc54c8d76373cd1bde06b06657bdb6"},
|
||||
{file = "pytest-8.0.0.tar.gz", hash = "sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<2.0"
|
||||
pluggy = ">=1.3.0,<2.0"
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||
|
@ -2471,13 +2471,13 @@ test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools
|
|||
|
||||
[[package]]
|
||||
name = "sphinx-autobuild"
|
||||
version = "2021.3.14"
|
||||
version = "2024.2.4"
|
||||
description = "Rebuild Sphinx documentation on changes, with live-reload in the browser."
|
||||
optional = true
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"},
|
||||
{file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"},
|
||||
{file = "sphinx_autobuild-2024.2.4-py3-none-any.whl", hash = "sha256:63fd87ab7505872a89aef468ce6503f65e794a195f4ae62269db3b85b72d4854"},
|
||||
{file = "sphinx_autobuild-2024.2.4.tar.gz", hash = "sha256:cb9d2121a176d62d45471624872afc5fad7755ad662738abe400ecf4a7954303"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -2486,7 +2486,7 @@ livereload = "*"
|
|||
sphinx = "*"
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest", "pytest-cov"]
|
||||
test = ["pytest (>=6.0)", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinx-basic-ng"
|
||||
|
@ -2988,12 +2988,13 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.link
|
|||
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
|
||||
|
||||
[extras]
|
||||
dev = ["autodoc-pydantic", "black", "coverage", "coveralls", "dask", "furo", "h5py", "myst-parser", "pytest", "pytest-cov", "pytest-depends", "ruff", "sphinx", "sphinx-autobuild", "sphinx-design"]
|
||||
dev = ["autodoc-pydantic", "black", "coverage", "coveralls", "dask", "furo", "h5py", "linkml", "linkml-runtime", "myst-parser", "pytest", "pytest-cov", "pytest-depends", "ruff", "sphinx", "sphinx-autobuild", "sphinx-design"]
|
||||
docs = ["autodoc-pydantic", "furo", "myst-parser", "sphinx", "sphinx-design"]
|
||||
linkml = ["linkml", "linkml-runtime"]
|
||||
proxy = ["dask", "h5py"]
|
||||
tests = ["coverage", "coveralls", "pytest", "pytest-cov", "pytest-depends"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "b57dc65220117fd2b0ca76f3e55c765789a3b00eb3b28a376b92059abeebab0f"
|
||||
content-hash = "d752a8794037df9c7b736d98cf82ec64e0bd4f7a32a52d996302d4277e7fbfd3"
|
||||
|
|
|
@ -11,8 +11,8 @@ python = "^3.11"
|
|||
pydantic = ">=2.3.0"
|
||||
nptyping = ">=2.5.0"
|
||||
blosc2 = "^2.5.1"
|
||||
dask = { version = "^2024.1.1" }
|
||||
h5py = { version = "^3.10.0" }
|
||||
dask = "^2024.1.1"
|
||||
h5py = "^3.10.0"
|
||||
pytest = { version=">=7.4.0", optional = true}
|
||||
pytest-depends = {version="^1.0.1", optional = true}
|
||||
coverage = {version = ">=6.1.1", optional = true}
|
||||
|
@ -105,3 +105,8 @@ ignore = [
|
|||
]
|
||||
|
||||
fixable = ["ALL"]
|
||||
|
||||
[tool.mypy]
|
||||
plugins = [
|
||||
"pydantic.mypy"
|
||||
]
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
"""
|
||||
Test custom features of the pydantic generator
|
||||
|
||||
Note that since this is largely a subclass, we don't test all of the functionality of the generator
|
||||
because it's tested in the base linkml package.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
import typing
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
def test_arraylike(imported_schema):
|
||||
"""
|
||||
Arraylike classes are converted to slots that specify nptyping arrays
|
||||
|
||||
array: Optional[Union[
|
||||
NDArray[Shape["* x, * y"], Number],
|
||||
NDArray[Shape["* x, * y, 3 z"], Number],
|
||||
NDArray[Shape["* x, * y, 3 z, 4 a"], Number]
|
||||
]] = Field(None)
|
||||
"""
|
||||
# check that we have gotten an NDArray annotation and its shape is correct
|
||||
array = imported_schema["core"].MainTopLevel.model_fields["array"].annotation
|
||||
args = typing.get_args(array)
|
||||
for i, shape in enumerate(("* x, * y", "* x, * y, 3 z", "* x, * y, 3 z, 4 a")):
|
||||
assert isinstance(args[i], NDArrayMeta)
|
||||
assert args[i].__args__[0].__args__
|
||||
assert args[i].__args__[1] == np.number
|
||||
|
||||
# we shouldn't have an actual class for the array
|
||||
assert not hasattr(imported_schema["core"], "MainTopLevel__Array")
|
||||
assert not hasattr(imported_schema["core"], "MainTopLevelArray")
|
||||
|
||||
|
||||
def test_inject_fields(imported_schema):
|
||||
"""
|
||||
Our root model should have the special fields we injected
|
||||
"""
|
||||
base = imported_schema["core"].ConfiguredBaseModel
|
||||
assert "hdf5_path" in base.model_fields
|
||||
assert "object_id" in base.model_fields
|
||||
|
||||
|
||||
def test_linkml_meta(imported_schema):
|
||||
"""
|
||||
We should be able to store some linkml metadata with our classes
|
||||
"""
|
||||
meta = imported_schema["core"].LinkML_Meta
|
||||
assert "tree_root" in meta.model_fields
|
||||
assert imported_schema["core"].MainTopLevel.linkml_meta.default.tree_root == True
|
||||
assert imported_schema["core"].OtherClass.linkml_meta.default.tree_root == False
|
||||
|
||||
|
||||
def test_skip(linkml_schema):
|
||||
"""
|
||||
We can skip slots and classes
|
||||
"""
|
||||
modules = generate_and_import(
|
||||
linkml_schema,
|
||||
split=False,
|
||||
generator_kwargs={
|
||||
"SKIP_SLOTS": ("SkippableSlot",),
|
||||
"SKIP_CLASSES": ("Skippable", "skippable"),
|
||||
},
|
||||
)
|
||||
assert not hasattr(modules["core"], "Skippable")
|
||||
assert "SkippableSlot" not in modules["core"].MainTopLevel.model_fields
|
||||
|
||||
|
||||
def test_inline_with_identifier(imported_schema):
|
||||
"""
|
||||
By default, if a class has an identifier attribute, it is inlined
|
||||
as a string rather than its class. We overrode that to be able to make dictionaries of collections
|
||||
"""
|
||||
main = imported_schema["core"].MainTopLevel
|
||||
inline = main.model_fields["inline_dict"].annotation
|
||||
assert typing.get_origin(typing.get_args(inline)[0]) == dict
|
||||
# god i hate pythons typing interface
|
||||
otherclass, stillanother = typing.get_args(
|
||||
typing.get_args(typing.get_args(inline)[0])[1]
|
||||
)
|
||||
assert otherclass is imported_schema["core"].OtherClass
|
||||
assert stillanother is imported_schema["core"].StillAnotherClass
|
||||
|
||||
|
||||
def test_namespace(imported_schema):
|
||||
"""
|
||||
Namespace schema import all classes from the other schema
|
||||
Returns:
|
||||
|
||||
"""
|
||||
ns = imported_schema["namespace"]
|
||||
|
||||
for classname, modname in (
|
||||
("MainThing", "test_schema.imported"),
|
||||
("Arraylike", "test_schema.imported"),
|
||||
("MainTopLevel", "test_schema.core"),
|
||||
("Skippable", "test_schema.core"),
|
||||
("OtherClass", "test_schema.core"),
|
||||
("StillAnotherClass", "test_schema.core"),
|
||||
):
|
||||
assert hasattr(ns, classname)
|
||||
if imported_schema["split"]:
|
||||
assert getattr(ns, classname).__module__ == modname
|
||||
|
||||
|
||||
def test_get_set_item(imported_schema):
|
||||
"""We can get and set without explicitly addressing array"""
|
||||
cls = imported_schema["core"].MainTopLevel(array=np.array([[1, 2, 3], [4, 5, 6]]))
|
||||
cls[0] = 50
|
||||
assert (cls[0] == 50).all()
|
||||
assert (cls.array[0] == 50).all()
|
||||
|
||||
cls[1, 1] = 100
|
||||
assert cls[1, 1] == 100
|
||||
assert cls.array[1, 1] == 100
|
0
tests/test_linkml/__init__.py
Normal file
0
tests/test_linkml/__init__.py
Normal file
120
tests/test_linkml/test_pydanticgen.py
Normal file
120
tests/test_linkml/test_pydanticgen.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
"""
|
||||
Test custom features of the pydantic generator
|
||||
|
||||
Note that since this is largely a subclass, we don't test all of the functionality of the generator
|
||||
because it's tested in the base linkml package.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
import typing
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
# def test_arraylike(imported_schema):
|
||||
# """
|
||||
# Arraylike classes are converted to slots that specify nptyping arrays
|
||||
#
|
||||
# array: Optional[Union[
|
||||
# NDArray[Shape["* x, * y"], Number],
|
||||
# NDArray[Shape["* x, * y, 3 z"], Number],
|
||||
# NDArray[Shape["* x, * y, 3 z, 4 a"], Number]
|
||||
# ]] = Field(None)
|
||||
# """
|
||||
# # check that we have gotten an NDArray annotation and its shape is correct
|
||||
# array = imported_schema["core"].MainTopLevel.model_fields["array"].annotation
|
||||
# args = typing.get_args(array)
|
||||
# for i, shape in enumerate(("* x, * y", "* x, * y, 3 z", "* x, * y, 3 z, 4 a")):
|
||||
# assert isinstance(args[i], NDArrayMeta)
|
||||
# assert args[i].__args__[0].__args__
|
||||
# assert args[i].__args__[1] == np.number
|
||||
#
|
||||
# # we shouldn't have an actual class for the array
|
||||
# assert not hasattr(imported_schema["core"], "MainTopLevel__Array")
|
||||
# assert not hasattr(imported_schema["core"], "MainTopLevelArray")
|
||||
#
|
||||
#
|
||||
# def test_inject_fields(imported_schema):
|
||||
# """
|
||||
# Our root model should have the special fields we injected
|
||||
# """
|
||||
# base = imported_schema["core"].ConfiguredBaseModel
|
||||
# assert "hdf5_path" in base.model_fields
|
||||
# assert "object_id" in base.model_fields
|
||||
#
|
||||
#
|
||||
# def test_linkml_meta(imported_schema):
|
||||
# """
|
||||
# We should be able to store some linkml metadata with our classes
|
||||
# """
|
||||
# meta = imported_schema["core"].LinkML_Meta
|
||||
# assert "tree_root" in meta.model_fields
|
||||
# assert imported_schema["core"].MainTopLevel.linkml_meta.default.tree_root == True
|
||||
# assert imported_schema["core"].OtherClass.linkml_meta.default.tree_root == False
|
||||
#
|
||||
#
|
||||
# def test_skip(linkml_schema):
|
||||
# """
|
||||
# We can skip slots and classes
|
||||
# """
|
||||
# modules = generate_and_import(
|
||||
# linkml_schema,
|
||||
# split=False,
|
||||
# generator_kwargs={
|
||||
# "SKIP_SLOTS": ("SkippableSlot",),
|
||||
# "SKIP_CLASSES": ("Skippable", "skippable"),
|
||||
# },
|
||||
# )
|
||||
# assert not hasattr(modules["core"], "Skippable")
|
||||
# assert "SkippableSlot" not in modules["core"].MainTopLevel.model_fields
|
||||
#
|
||||
#
|
||||
# def test_inline_with_identifier(imported_schema):
|
||||
# """
|
||||
# By default, if a class has an identifier attribute, it is inlined
|
||||
# as a string rather than its class. We overrode that to be able to make dictionaries of collections
|
||||
# """
|
||||
# main = imported_schema["core"].MainTopLevel
|
||||
# inline = main.model_fields["inline_dict"].annotation
|
||||
# assert typing.get_origin(typing.get_args(inline)[0]) == dict
|
||||
# # god i hate pythons typing interface
|
||||
# otherclass, stillanother = typing.get_args(
|
||||
# typing.get_args(typing.get_args(inline)[0])[1]
|
||||
# )
|
||||
# assert otherclass is imported_schema["core"].OtherClass
|
||||
# assert stillanother is imported_schema["core"].StillAnotherClass
|
||||
#
|
||||
#
|
||||
# def test_namespace(imported_schema):
|
||||
# """
|
||||
# Namespace schema import all classes from the other schema
|
||||
# Returns:
|
||||
#
|
||||
# """
|
||||
# ns = imported_schema["namespace"]
|
||||
#
|
||||
# for classname, modname in (
|
||||
# ("MainThing", "test_schema.imported"),
|
||||
# ("Arraylike", "test_schema.imported"),
|
||||
# ("MainTopLevel", "test_schema.core"),
|
||||
# ("Skippable", "test_schema.core"),
|
||||
# ("OtherClass", "test_schema.core"),
|
||||
# ("StillAnotherClass", "test_schema.core"),
|
||||
# ):
|
||||
# assert hasattr(ns, classname)
|
||||
# if imported_schema["split"]:
|
||||
# assert getattr(ns, classname).__module__ == modname
|
||||
#
|
||||
#
|
||||
# def test_get_set_item(imported_schema):
|
||||
# """We can get and set without explicitly addressing array"""
|
||||
# cls_ = imported_schema["core"].MainTopLevel(array=np.array([[1, 2, 3], [4, 5, 6]]))
|
||||
# cls_[0] = 50
|
||||
# assert (cls_[0] == 50).all()
|
||||
# assert (cls_.array[0] == 50).all()
|
||||
#
|
||||
# cls_[1, 1] = 100
|
||||
# assert cls_[1, 1] == 100
|
||||
# assert cls_.array[1, 1] == 100
|
Loading…
Reference in a new issue