linkml 1.9.4rc1__py3-none-any.whl → 1.9.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- linkml/cli/main.py +5 -1
- linkml/converter/__init__.py +0 -0
- linkml/generators/__init__.py +2 -0
- linkml/generators/common/build.py +5 -20
- linkml/generators/common/template.py +289 -3
- linkml/generators/docgen.py +55 -10
- linkml/generators/erdiagramgen.py +9 -5
- linkml/generators/graphqlgen.py +32 -6
- linkml/generators/jsonldcontextgen.py +78 -12
- linkml/generators/jsonschemagen.py +29 -12
- linkml/generators/mermaidclassdiagramgen.py +21 -3
- linkml/generators/owlgen.py +13 -2
- linkml/generators/panderagen/dataframe_class.py +13 -0
- linkml/generators/panderagen/dataframe_field.py +50 -0
- linkml/generators/panderagen/linkml_pandera_validator.py +186 -0
- linkml/generators/panderagen/panderagen.py +22 -5
- linkml/generators/panderagen/panderagen_class_based/class.jinja2 +70 -13
- linkml/generators/panderagen/panderagen_class_based/custom_checks.jinja2 +27 -0
- linkml/generators/panderagen/panderagen_class_based/enums.jinja2 +3 -3
- linkml/generators/panderagen/panderagen_class_based/pandera.jinja2 +12 -2
- linkml/generators/panderagen/panderagen_class_based/slots.jinja2 +19 -17
- linkml/generators/panderagen/slot_generator_mixin.py +143 -16
- linkml/generators/panderagen/transforms/__init__.py +19 -0
- linkml/generators/panderagen/transforms/collection_dict_model_transform.py +62 -0
- linkml/generators/panderagen/transforms/list_dict_model_transform.py +66 -0
- linkml/generators/panderagen/transforms/model_transform.py +8 -0
- linkml/generators/panderagen/transforms/nested_struct_model_transform.py +27 -0
- linkml/generators/panderagen/transforms/simple_dict_model_transform.py +86 -0
- linkml/generators/plantumlgen.py +17 -11
- linkml/generators/pydanticgen/pydanticgen.py +53 -2
- linkml/generators/pydanticgen/template.py +45 -233
- linkml/generators/pydanticgen/templates/attribute.py.jinja +1 -0
- linkml/generators/pydanticgen/templates/base_model.py.jinja +16 -2
- linkml/generators/pydanticgen/templates/imports.py.jinja +1 -1
- linkml/generators/rdfgen.py +11 -2
- linkml/generators/rustgen/__init__.py +3 -0
- linkml/generators/rustgen/build.py +97 -0
- linkml/generators/rustgen/cli.py +83 -0
- linkml/generators/rustgen/rustgen.py +1186 -0
- linkml/generators/rustgen/template.py +910 -0
- linkml/generators/rustgen/templates/Cargo.toml.jinja +42 -0
- linkml/generators/rustgen/templates/anything.rs.jinja +149 -0
- linkml/generators/rustgen/templates/as_key_value.rs.jinja +86 -0
- linkml/generators/rustgen/templates/class_module.rs.jinja +8 -0
- linkml/generators/rustgen/templates/enum.rs.jinja +70 -0
- linkml/generators/rustgen/templates/file.rs.jinja +75 -0
- linkml/generators/rustgen/templates/import.rs.jinja +4 -0
- linkml/generators/rustgen/templates/imports.rs.jinja +8 -0
- linkml/generators/rustgen/templates/lib_shim.rs.jinja +52 -0
- linkml/generators/rustgen/templates/poly.rs.jinja +9 -0
- linkml/generators/rustgen/templates/poly_containers.rs.jinja +439 -0
- linkml/generators/rustgen/templates/poly_trait.rs.jinja +15 -0
- linkml/generators/rustgen/templates/poly_trait_impl.rs.jinja +5 -0
- linkml/generators/rustgen/templates/poly_trait_impl_orsubtype.rs.jinja +5 -0
- linkml/generators/rustgen/templates/poly_trait_property.rs.jinja +8 -0
- linkml/generators/rustgen/templates/poly_trait_property_impl.rs.jinja +134 -0
- linkml/generators/rustgen/templates/poly_trait_property_match.rs.jinja +10 -0
- linkml/generators/rustgen/templates/property.rs.jinja +28 -0
- linkml/generators/rustgen/templates/pyproject.toml.jinja +10 -0
- linkml/generators/rustgen/templates/serde_utils.rs.jinja +490 -0
- linkml/generators/rustgen/templates/slot_range_as_union.rs.jinja +64 -0
- linkml/generators/rustgen/templates/struct.rs.jinja +81 -0
- linkml/generators/rustgen/templates/struct_or_subtype_enum.rs.jinja +111 -0
- linkml/generators/rustgen/templates/stub_gen.rs.jinja +71 -0
- linkml/generators/rustgen/templates/stub_utils.rs.jinja +76 -0
- linkml/generators/rustgen/templates/typealias.rs.jinja +13 -0
- linkml/generators/sqltablegen.py +18 -16
- linkml/generators/yarrrmlgen.py +173 -0
- linkml/linter/config/datamodel/config.py +160 -293
- linkml/linter/config/datamodel/config.yaml +34 -26
- linkml/linter/config/default.yaml +4 -0
- linkml/linter/config/recommended.yaml +4 -0
- linkml/linter/linter.py +1 -2
- linkml/linter/rules.py +37 -0
- linkml/utils/schema_builder.py +2 -0
- linkml/utils/schemaloader.py +76 -3
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5.dist-info}/METADATA +2 -2
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5.dist-info}/RECORD +82 -40
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5.dist-info}/entry_points.txt +2 -1
- linkml/generators/panderagen/panderagen_class_based/mixins.jinja2 +0 -26
- /linkml/{utils/converter.py → converter/cli.py} +0 -0
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5.dist-info}/WHEEL +0 -0
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from functools import wraps
|
|
3
|
+
|
|
4
|
+
import pandera
|
|
5
|
+
import polars as pl
|
|
6
|
+
from pandera.api.polars.types import PolarsData
|
|
7
|
+
|
|
8
|
+
from linkml.generators.panderagen.transforms import (
|
|
9
|
+
CollectionDictModelTransform,
|
|
10
|
+
ListDictModelTransform,
|
|
11
|
+
NestedStructModelTransform,
|
|
12
|
+
SimpleDictModelTransform,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def handle_validation_exceptions(func):
|
|
17
|
+
@wraps(func)
|
|
18
|
+
def wrapper(*args, **kwargs):
|
|
19
|
+
try:
|
|
20
|
+
return func(*args, **kwargs)
|
|
21
|
+
except pl.exceptions.PanicException:
|
|
22
|
+
data = args[2] if len(args) > 2 else kwargs.get("data")
|
|
23
|
+
return data.lazyframe.select(pl.lit(False))
|
|
24
|
+
except pandera.errors.SchemaError as e:
|
|
25
|
+
raise e
|
|
26
|
+
except Exception:
|
|
27
|
+
data = args[2] if len(args) > 2 else kwargs.get("data")
|
|
28
|
+
return data.lazyframe.select(pl.lit(False))
|
|
29
|
+
|
|
30
|
+
return wrapper
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LinkmlPanderaValidator:
|
|
34
|
+
@classmethod
|
|
35
|
+
def get_id_column_name(cls):
|
|
36
|
+
return cls._id_name
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def _simple_dict_fields(cls, column_name):
|
|
40
|
+
details = cls._INLINE_DETAILS[column_name] # <-- THESE ARE GOING ON THE OUTER CLASS
|
|
41
|
+
|
|
42
|
+
return (details["id"], details["other"])
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def _prepare_simple_dict(cls, data: PolarsData):
|
|
46
|
+
"""Returns just the simple dict column transformed to an inlined list form
|
|
47
|
+
|
|
48
|
+
note that this method uses collect and iter_rows so is very inefficient
|
|
49
|
+
"""
|
|
50
|
+
column_name = data.key
|
|
51
|
+
polars_schema = cls.get_nested_range(column_name).to_schema()
|
|
52
|
+
(id_column, other_column) = cls._simple_dict_fields(column_name)
|
|
53
|
+
|
|
54
|
+
simple_dict_transformer = SimpleDictModelTransform(polars_schema, id_column, other_column)
|
|
55
|
+
|
|
56
|
+
one_column_df = data.lazyframe.select(pl.col(column_name)).collect()
|
|
57
|
+
|
|
58
|
+
list_of_structs = [simple_dict_transformer.transform(e) for [e] in one_column_df.iter_rows()]
|
|
59
|
+
|
|
60
|
+
return pl.DataFrame(pl.Series(list_of_structs).alias(column_name))
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
@handle_validation_exceptions
|
|
64
|
+
def _check_simple_dict(cls, data: PolarsData):
|
|
65
|
+
"""
|
|
66
|
+
The 'simple dict' format, in which the key serves as a local identifier is not a good match for a PolaRS
|
|
67
|
+
DataFrame. At present the format is
|
|
68
|
+
"""
|
|
69
|
+
df = cls._prepare_simple_dict(data)
|
|
70
|
+
|
|
71
|
+
column_name = data.key
|
|
72
|
+
|
|
73
|
+
polars_schema = cls.get_nested_range(column_name).to_schema()
|
|
74
|
+
simple_transform = SimpleDictModelTransform(polars_schema, *cls._simple_dict_fields(column_name))
|
|
75
|
+
df = simple_transform.explode_unnest_dataframe(df, column_name)
|
|
76
|
+
|
|
77
|
+
nested_cls = cls.get_nested_range(column_name)
|
|
78
|
+
nested_cls.validate(df)
|
|
79
|
+
return data.lazyframe.select(pl.lit(True))
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
@handle_validation_exceptions
|
|
83
|
+
def _check_collection_struct(cls, data: PolarsData):
|
|
84
|
+
column_name = data.key
|
|
85
|
+
nested_cls = cls.get_nested_range(column_name)
|
|
86
|
+
|
|
87
|
+
df = CollectionDictModelTransform.prepare_dataframe(data, column_name, nested_cls)
|
|
88
|
+
|
|
89
|
+
collection_transform = CollectionDictModelTransform(nested_cls.to_schema(), nested_cls.get_id_column_name())
|
|
90
|
+
df = collection_transform.explode_unnest_dataframe(df, column_name)
|
|
91
|
+
|
|
92
|
+
nested_cls.validate(df)
|
|
93
|
+
return data.lazyframe.select(pl.lit(True))
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
@handle_validation_exceptions
|
|
97
|
+
def _check_nested_list_struct(cls, data: PolarsData):
|
|
98
|
+
"""Use this in a custom check. Pass the nested model as pandera_model."""
|
|
99
|
+
column_name = data.key
|
|
100
|
+
nested_cls = cls.get_nested_range(column_name)
|
|
101
|
+
|
|
102
|
+
df = ListDictModelTransform.prepare_dataframe(data, column_name, nested_cls)
|
|
103
|
+
|
|
104
|
+
list_transform = ListDictModelTransform(nested_cls.to_schema())
|
|
105
|
+
df = list_transform.explode_unnest_dataframe(df, column_name, data)
|
|
106
|
+
|
|
107
|
+
nested_cls.validate(df)
|
|
108
|
+
return data.lazyframe.select(pl.lit(True))
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
@handle_validation_exceptions
|
|
112
|
+
def _check_nested_struct(cls, data: PolarsData):
|
|
113
|
+
"""Use this in a custom check. Pass the nested model as pandera_model."""
|
|
114
|
+
column_name = data.key
|
|
115
|
+
nested_cls = cls.get_nested_range(column_name)
|
|
116
|
+
|
|
117
|
+
df = NestedStructModelTransform.prepare_dataframe(data, column_name, nested_cls)
|
|
118
|
+
nested_transform = NestedStructModelTransform(nested_cls.to_schema())
|
|
119
|
+
df = nested_transform.explode_unnest_dataframe(df, column_name)
|
|
120
|
+
|
|
121
|
+
nested_cls.validate(df)
|
|
122
|
+
return data.lazyframe.select(pl.lit(True))
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def get_nested_range(cls, column_name):
|
|
126
|
+
"""Resolve a nested class range at runtime.
|
|
127
|
+
|
|
128
|
+
Nested classes are not stored in the pandera schema,
|
|
129
|
+
but rather in the _NESTED_RANGES dictionary as strings.
|
|
130
|
+
"""
|
|
131
|
+
nested_cls_name = cls._NESTED_RANGES[column_name]
|
|
132
|
+
shared_model_module = inspect.getmodule(cls)
|
|
133
|
+
nested_cls = getattr(shared_model_module, nested_cls_name)
|
|
134
|
+
|
|
135
|
+
return nested_cls
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def generate_polars_schema_simple(cls):
|
|
139
|
+
# This is not nesting or list aware, so needs to be aligned with the other method
|
|
140
|
+
return pl.Struct({k: v.dtype.type for k, v in cls.to_schema().columns.items()})
|
|
141
|
+
|
|
142
|
+
@classmethod
|
|
143
|
+
def generate_polars_schema(cls, object_to_validate, parser=False) -> dict:
|
|
144
|
+
"""Creates a nested PolaRS schema suitable for loading the object_to_validate.
|
|
145
|
+
Optional columns that are not present in the data are omitted.
|
|
146
|
+
This approach is only suitable to enable the test fixtures.
|
|
147
|
+
"""
|
|
148
|
+
polars_schema = {}
|
|
149
|
+
|
|
150
|
+
if isinstance(object_to_validate, list):
|
|
151
|
+
object_to_validate = object_to_validate[0]
|
|
152
|
+
|
|
153
|
+
for column_name, column in cls.to_schema().columns.items():
|
|
154
|
+
dtype = column.properties["dtype"]
|
|
155
|
+
required = column.properties["required"]
|
|
156
|
+
|
|
157
|
+
if required or column_name in object_to_validate:
|
|
158
|
+
if dtype.type in [pl.Struct, pl.List]: # maybe use inline form directly here
|
|
159
|
+
inline_form = cls._INLINE_FORM.get(column_name, "not_inline")
|
|
160
|
+
if inline_form == "simple_dict":
|
|
161
|
+
polars_schema[column_name] = pl.Object # make this a struct and make the nested non-
|
|
162
|
+
elif inline_form == "not_inline":
|
|
163
|
+
polars_schema[column_name] = dtype.type
|
|
164
|
+
else:
|
|
165
|
+
nested_cls = cls.get_nested_range(column_name)
|
|
166
|
+
if inline_form == "inlined_dict":
|
|
167
|
+
if parser:
|
|
168
|
+
nested_schema = nested_cls.generate_polars_schema(
|
|
169
|
+
object_to_validate[column_name], parser
|
|
170
|
+
)
|
|
171
|
+
polars_schema[column_name] = pl.Struct(nested_schema)
|
|
172
|
+
else:
|
|
173
|
+
polars_schema[column_name] = pl.Struct
|
|
174
|
+
elif inline_form == "inlined_list_dict":
|
|
175
|
+
if parser:
|
|
176
|
+
nested_schema = nested_cls.generate_polars_schema(
|
|
177
|
+
object_to_validate[column_name], parser
|
|
178
|
+
)
|
|
179
|
+
polars_schema[column_name] = pl.List(pl.Struct(nested_schema))
|
|
180
|
+
else:
|
|
181
|
+
# transformed form
|
|
182
|
+
polars_schema[column_name] = pl.List
|
|
183
|
+
else:
|
|
184
|
+
polars_schema[column_name] = dtype.type
|
|
185
|
+
|
|
186
|
+
return polars_schema
|
|
@@ -14,9 +14,10 @@ from linkml_runtime.utils.formatutils import camelcase
|
|
|
14
14
|
from linkml_runtime.utils.schemaview import SchemaView
|
|
15
15
|
|
|
16
16
|
from linkml._version import __version__
|
|
17
|
-
from linkml.generators.oocodegen import
|
|
17
|
+
from linkml.generators.oocodegen import OOCodeGenerator, OODocument
|
|
18
18
|
|
|
19
19
|
from .class_generator_mixin import ClassGeneratorMixin
|
|
20
|
+
from .dataframe_class import DataframeClass
|
|
20
21
|
from .enum_generator_mixin import EnumGeneratorMixin
|
|
21
22
|
from .slot_generator_mixin import SlotGeneratorMixin
|
|
22
23
|
|
|
@@ -28,6 +29,7 @@ TYPEMAP = {
|
|
|
28
29
|
"panderagen_class_based": {
|
|
29
30
|
"xsd:string": "str",
|
|
30
31
|
"xsd:integer": "int",
|
|
32
|
+
"xsd:int": "int",
|
|
31
33
|
"xsd:float": "float",
|
|
32
34
|
"xsd:double": "float",
|
|
33
35
|
"xsd:boolean": "bool",
|
|
@@ -83,6 +85,8 @@ class PanderaGenerator(OOCodeGenerator, EnumGeneratorMixin, ClassGeneratorMixin,
|
|
|
83
85
|
|
|
84
86
|
@staticmethod
|
|
85
87
|
def make_multivalued(range: str) -> str:
|
|
88
|
+
if range == "Struct":
|
|
89
|
+
return "pl.List"
|
|
86
90
|
return f"List[{range}]"
|
|
87
91
|
|
|
88
92
|
def uri_type_map(self, xsd_uri: str, template: str = None):
|
|
@@ -92,15 +96,22 @@ class PanderaGenerator(OOCodeGenerator, EnumGeneratorMixin, ClassGeneratorMixin,
|
|
|
92
96
|
return TYPEMAP[template].get(xsd_uri)
|
|
93
97
|
|
|
94
98
|
def map_type(self, t: TypeDefinition) -> str:
|
|
99
|
+
logger.info(f"type_map definition: {t}")
|
|
100
|
+
|
|
101
|
+
typ = None
|
|
102
|
+
|
|
95
103
|
if t.uri:
|
|
96
104
|
typ = self.uri_type_map(t.uri)
|
|
97
|
-
|
|
105
|
+
if typ is None:
|
|
106
|
+
typ = self.map_type(self.schemaview.get_type(t.typeof))
|
|
98
107
|
elif t.typeof:
|
|
99
108
|
typ = self.map_type(self.schemaview.get_type(t.typeof))
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
|
|
110
|
+
if typ is None:
|
|
102
111
|
raise ValueError(f"{t} cannot be mapped to a type")
|
|
103
112
|
|
|
113
|
+
return typ
|
|
114
|
+
|
|
104
115
|
def load_template(self, template_filename):
|
|
105
116
|
jinja_env = Environment(loader=PackageLoader("linkml.generators.panderagen", self.template_path))
|
|
106
117
|
return jinja_env.get_template(template_filename)
|
|
@@ -138,6 +149,7 @@ class PanderaGenerator(OOCodeGenerator, EnumGeneratorMixin, ClassGeneratorMixin,
|
|
|
138
149
|
coerce=self.coerce,
|
|
139
150
|
type_map=TYPEMAP,
|
|
140
151
|
template_path=self.template_path,
|
|
152
|
+
pandera_validator_code=None,
|
|
141
153
|
)
|
|
142
154
|
return code
|
|
143
155
|
|
|
@@ -156,12 +168,17 @@ class PanderaGenerator(OOCodeGenerator, EnumGeneratorMixin, ClassGeneratorMixin,
|
|
|
156
168
|
for c in self.ordered_classes():
|
|
157
169
|
cn = c.name
|
|
158
170
|
safe_cn = camelcase(cn)
|
|
159
|
-
|
|
171
|
+
annotations = {}
|
|
172
|
+
identifier_or_key_slot = self.get_identifier_or_key_slot(cn)
|
|
173
|
+
if identifier_or_key_slot:
|
|
174
|
+
annotations["identifier_key_slot"] = identifier_or_key_slot.name
|
|
175
|
+
ooclass = DataframeClass(
|
|
160
176
|
name=safe_cn,
|
|
161
177
|
description=c.description,
|
|
162
178
|
package=self.package,
|
|
163
179
|
fields=[],
|
|
164
180
|
source_class=c,
|
|
181
|
+
annotations=annotations,
|
|
165
182
|
)
|
|
166
183
|
classes.append(ooclass)
|
|
167
184
|
if c.mixin:
|
|
@@ -3,24 +3,81 @@
|
|
|
3
3
|
Details at https://pandera.readthedocs.io/en/stable/dataframe_models.html
|
|
4
4
|
-#}
|
|
5
5
|
{%- import 'slots.jinja2' as slot_macros -%}
|
|
6
|
+
{%- import 'custom_checks.jinja2' as custom_checks -%}
|
|
6
7
|
|
|
7
|
-
{%- macro
|
|
8
|
-
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
{%- macro render_parent_classes(cls) -%}
|
|
9
|
+
{%- if cls.is_a -%}
|
|
10
|
+
{{ cls.is_a }}
|
|
11
|
+
{%- else -%}
|
|
12
|
+
pla.DataFrameModel, _LinkmlPanderaValidator
|
|
13
|
+
{%- endif -%}
|
|
14
|
+
{%- endmacro -%}
|
|
15
|
+
|
|
16
|
+
{%- macro render_nested_ranges(cls) -%}
|
|
17
|
+
{#-
|
|
18
|
+
Helper class member that references another Pandera class that
|
|
19
|
+
handles a nested association.
|
|
20
|
+
-#}
|
|
21
|
+
_NESTED_RANGES = {
|
|
22
|
+
{%- for field in cls.fields -%}
|
|
23
|
+
{%- if field.reference_class() %}
|
|
24
|
+
"{{ field.name }}": "{{ field.reference_class() }}",
|
|
25
|
+
{% endif -%}
|
|
26
|
+
{%- endfor %}
|
|
27
|
+
}
|
|
28
|
+
{%- endmacro -%}
|
|
29
|
+
|
|
30
|
+
{%- macro render_inline_form(cls) -%}
|
|
31
|
+
{#-
|
|
32
|
+
This is a meta object with information used by the various check methods
|
|
33
|
+
-#}
|
|
34
|
+
_INLINE_FORM = {
|
|
35
|
+
{%- for field in cls.fields -%}
|
|
36
|
+
{%- if field.inline_form() %}
|
|
37
|
+
"{{ field.name }}": "{{ field.inline_form() }}",
|
|
38
|
+
{% endif -%}
|
|
39
|
+
{%- endfor %}
|
|
40
|
+
}
|
|
41
|
+
{%- endmacro -%}
|
|
42
|
+
|
|
43
|
+
{%- macro render_inline_details(cls) -%}
|
|
44
|
+
{#-
|
|
45
|
+
This is a meta object with information used by the various check methods
|
|
46
|
+
-#}
|
|
47
|
+
_INLINE_DETAILS = {
|
|
48
|
+
{%- for field in cls.fields -%}
|
|
49
|
+
{%- if field.inline_details() %}
|
|
50
|
+
"{{ field.name }}": {{ field.inline_details() }},
|
|
51
|
+
{% endif -%}
|
|
52
|
+
{%- endfor %}
|
|
53
|
+
}
|
|
54
|
+
{%- endmacro -%}
|
|
55
|
+
|
|
56
|
+
{%- macro render_slots(cls) -%}
|
|
57
|
+
{{ slot_macros.id_slot_name_class_variable(cls) }}
|
|
20
58
|
{%- if (cls.fields | length) == 0 %}
|
|
21
59
|
pass
|
|
22
60
|
{% endif -%}
|
|
23
61
|
{%- for field in cls.fields -%}
|
|
24
62
|
{{ slot_macros.render_slot(field) }}
|
|
25
63
|
{%- endfor -%}
|
|
64
|
+
{%- endmacro -%}
|
|
65
|
+
|
|
66
|
+
{%- macro render_class(cls) %}
|
|
67
|
+
{#-
|
|
68
|
+
Generates the main structure of a Pandera class.
|
|
69
|
+
This includes the slots, custom checks,
|
|
70
|
+
and helper class members with information used by the custom checks.
|
|
71
|
+
-#}
|
|
72
|
+
class {{cls.name}}({{ render_parent_classes(cls) }}):
|
|
73
|
+
{%- if cls.source_class.description %}
|
|
74
|
+
"""
|
|
75
|
+
{{ cls.source_class.description }}
|
|
76
|
+
"""
|
|
77
|
+
{% endif -%}
|
|
78
|
+
{{ render_slots(cls) }}
|
|
79
|
+
{{ custom_checks.render_custom_checks(cls) }}
|
|
80
|
+
{{ render_nested_ranges(cls) }}
|
|
81
|
+
{{ render_inline_form(cls) }}
|
|
82
|
+
{{ render_inline_details(cls) }}
|
|
26
83
|
{% endmacro -%}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
{%- macro nested_checks(field) -%}
|
|
3
|
+
{#-
|
|
4
|
+
field: DataframeField
|
|
5
|
+
Render a class method that follows nested structures
|
|
6
|
+
other than the simple_dict form.
|
|
7
|
+
-#}
|
|
8
|
+
{%- if field.inline_form() %}
|
|
9
|
+
@pla.check("{{ field.name }}")
|
|
10
|
+
def check_nested_struct_{{ field.name }}(cls, data: PolarsData):
|
|
11
|
+
{% if field.inline_form() == 'inline_list_dict' -%}
|
|
12
|
+
return cls._check_nested_struct(data)
|
|
13
|
+
{% elif field.inline_form() == 'simple_dict' -%}
|
|
14
|
+
return cls._check_simple_dict(data)
|
|
15
|
+
{% elif field.inline_form() == 'inline_collection_dict' -%}
|
|
16
|
+
return cls._check_collection_struct(data)
|
|
17
|
+
{% else -%}
|
|
18
|
+
return cls._check_nested_list_struct(data)
|
|
19
|
+
{% endif -%}
|
|
20
|
+
{%- endif -%}
|
|
21
|
+
{%- endmacro -%}
|
|
22
|
+
|
|
23
|
+
{%- macro render_custom_checks(cls) -%}
|
|
24
|
+
{%- for field in cls.fields -%}
|
|
25
|
+
{{ nested_checks(field) }}
|
|
26
|
+
{%- endfor %}
|
|
27
|
+
{%- endmacro -%}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
{%- macro enum_parameter(
|
|
2
|
-
{%- if
|
|
1
|
+
{%- macro enum_parameter(field) -%}
|
|
2
|
+
{%- if field.permissible_values()|length > 0 -%}
|
|
3
3
|
dtype_kwargs={"categories":(
|
|
4
|
-
{%- for pv in
|
|
4
|
+
{%- for pv in field.permissible_values() -%}
|
|
5
5
|
'{{ pv }}',
|
|
6
6
|
{%- endfor -%}
|
|
7
7
|
)}
|
|
@@ -4,10 +4,20 @@
|
|
|
4
4
|
-#}
|
|
5
5
|
{%- import 'header.jinja2' as header -%}
|
|
6
6
|
{%- import 'class.jinja2' as class_macros -%}
|
|
7
|
-
{%- import 'mixins.jinja2' as mixins -%}
|
|
8
7
|
{{ header }}
|
|
9
8
|
|
|
10
|
-
{
|
|
9
|
+
{% if pandera_validator_code %}
|
|
10
|
+
{{ pandera_validator_code }}
|
|
11
|
+
{% else %}
|
|
12
|
+
from linkml.generators.panderagen.linkml_pandera_validator import LinkmlPanderaValidator as _LinkmlPanderaValidator
|
|
13
|
+
{% endif %}
|
|
14
|
+
|
|
15
|
+
# These are all str for now
|
|
16
|
+
ID_TYPES = {
|
|
17
|
+
{%- for cls in doc.classes %}
|
|
18
|
+
"{{ cls.name }}": "str",
|
|
19
|
+
{%- endfor %}
|
|
20
|
+
}
|
|
11
21
|
|
|
12
22
|
{% if metamodel_version %}# metamodel_version: {{metamodel_version}}{% endif %}
|
|
13
23
|
{% if model_version %}# version: {{model_version}}{% endif %}
|
|
@@ -4,33 +4,35 @@
|
|
|
4
4
|
-#}
|
|
5
5
|
{%- import 'enums.jinja2' as enum_macros -%}
|
|
6
6
|
|
|
7
|
-
{%- macro constraint_parameters(field
|
|
7
|
+
{%- macro constraint_parameters(field) -%}
|
|
8
8
|
{%- if coerce is true -%}coerce=True, {% endif -%}
|
|
9
9
|
{%- if field.default_value is not none -%}default={{ field.default_value }}, {% endif -%}
|
|
10
|
-
{%- if
|
|
11
|
-
{%- if
|
|
12
|
-
{%- if
|
|
13
|
-
{%- if (
|
|
10
|
+
{%- if field.minimum_value() is not none -%}ge={{ field.minimum_value() }}, {% endif -%}
|
|
11
|
+
{%- if field.maximum_value() is not none -%}le={{ field.maximum_value() }}, {% endif -%}
|
|
12
|
+
{%- if field.pattern() is not none -%}str_matches=r"{{ field.pattern() }}", {% endif -%}
|
|
13
|
+
{%- if (field.required() is none or field.required() is false) and field.identifier() is not true -%}nullable=True, {% endif -%}
|
|
14
14
|
{%- endmacro -%}
|
|
15
15
|
|
|
16
|
-
{%- macro
|
|
17
|
-
|
|
16
|
+
{%- macro id_slot_name_class_variable(cls) %}
|
|
17
|
+
_id_name : str = {% if cls.identifier_key_slot() %} '{{ cls.identifier_key_slot() }}' {% else %}None{% endif %}
|
|
18
|
+
{%- endmacro -%}
|
|
19
|
+
|
|
20
|
+
{%- macro render_slot(field) %}
|
|
18
21
|
{{ field.name }}:{{ ' ' }}
|
|
19
|
-
{%- if (
|
|
22
|
+
{%- if (field.required() is none or field.required() is false) and (field.identifier() is not true) -%}
|
|
20
23
|
Optional[
|
|
21
24
|
{%- endif -%}
|
|
22
25
|
{{field.range}}
|
|
23
|
-
{%- if (
|
|
24
|
-
{{ ']
|
|
26
|
+
{%- if (field.required() is none or field.required() is false) and (field.identifier() is not true) -%}
|
|
27
|
+
{{ ']' }}
|
|
25
28
|
{%- endif -%}
|
|
26
|
-
= pla.Field(
|
|
27
|
-
{{- constraint_parameters(field
|
|
28
|
-
{{- enum_macros.enum_parameter(
|
|
29
|
+
{{ ' =' }} pla.Field(
|
|
30
|
+
{{- constraint_parameters(field) -}}
|
|
31
|
+
{{- enum_macros.enum_parameter(field) -}}
|
|
29
32
|
)
|
|
30
|
-
{%- if
|
|
33
|
+
{%- if field.description() %}
|
|
31
34
|
"""
|
|
32
|
-
{{
|
|
35
|
+
{{ field.description() }}
|
|
33
36
|
"""
|
|
34
|
-
|
|
35
|
-
{%- endwith -%}
|
|
37
|
+
{% endif -%}
|
|
36
38
|
{%- endmacro %}
|