linkml 1.9.4rc1__py3-none-any.whl → 1.9.5rc1__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 +4 -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 +4 -1
- 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 +94 -0
- linkml/generators/rustgen/cli.py +65 -0
- linkml/generators/rustgen/rustgen.py +1038 -0
- linkml/generators/rustgen/template.py +865 -0
- linkml/generators/rustgen/templates/Cargo.toml.jinja +42 -0
- linkml/generators/rustgen/templates/anything.rs.jinja +142 -0
- linkml/generators/rustgen/templates/as_key_value.rs.jinja +56 -0
- linkml/generators/rustgen/templates/class_module.rs.jinja +8 -0
- linkml/generators/rustgen/templates/enum.rs.jinja +54 -0
- linkml/generators/rustgen/templates/file.rs.jinja +62 -0
- linkml/generators/rustgen/templates/import.rs.jinja +4 -0
- linkml/generators/rustgen/templates/imports.rs.jinja +8 -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 +132 -0
- linkml/generators/rustgen/templates/poly_trait_property_match.rs.jinja +10 -0
- linkml/generators/rustgen/templates/property.rs.jinja +19 -0
- linkml/generators/rustgen/templates/pyproject.toml.jinja +10 -0
- linkml/generators/rustgen/templates/serde_utils.rs.jinja +310 -0
- linkml/generators/rustgen/templates/slot_range_as_union.rs.jinja +61 -0
- linkml/generators/rustgen/templates/struct.rs.jinja +75 -0
- linkml/generators/rustgen/templates/struct_or_subtype_enum.rs.jinja +108 -0
- linkml/generators/rustgen/templates/typealias.rs.jinja +13 -0
- linkml/generators/sqltablegen.py +18 -16
- linkml/generators/yarrrmlgen.py +157 -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/schemaloader.py +55 -3
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/METADATA +2 -2
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/RECORD +76 -38
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/entry_points.txt +1 -0
- linkml/generators/panderagen/panderagen_class_based/mixins.jinja2 +0 -26
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/WHEEL +0 -0
- {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#[cfg(feature = "serde")]
|
|
2
|
+
use serde::{de::{DeserializeOwned, IntoDeserializer}, Deserialize, Deserializer};
|
|
3
|
+
#[cfg(feature = "serde")]
|
|
4
|
+
use serde::de::Error;
|
|
5
|
+
#[cfg(feature = "serde")]
|
|
6
|
+
use serde_value::{Value, ValueDeserializer};
|
|
7
|
+
#[cfg(feature = "serde")]
|
|
8
|
+
use std::collections::{BTreeMap, HashMap};
|
|
9
|
+
#[cfg(all(feature = "serde", feature = "pyo3"))]
|
|
10
|
+
use pyo3::prelude::*;
|
|
11
|
+
#[cfg(all(feature = "serde", feature = "pyo3"))]
|
|
12
|
+
use pyo3::types::{PyAny, PyDict, PyList, PyTuple};
|
|
13
|
+
|
|
14
|
+
#[cfg(feature = "serde")]
|
|
15
|
+
pub trait InlinedPair: Sized {
|
|
16
|
+
type Key: std::hash::Hash + Eq + serde::de::DeserializeOwned + Clone + Ord;
|
|
17
|
+
type Value: serde::de::DeserializeOwned;
|
|
18
|
+
type Error: std::fmt::Display;
|
|
19
|
+
|
|
20
|
+
fn from_pair_mapping(k: Self::Key, v: Value) -> Result<Self,Self::Error>;
|
|
21
|
+
fn from_pair_simple(k: Self::Key, v: Value) -> Result<Self,Self::Error>;
|
|
22
|
+
fn extract_key(&self) -> &Self::Key;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[cfg(feature = "serde")]
|
|
26
|
+
impl<T> InlinedPair for Box<T>
|
|
27
|
+
where
|
|
28
|
+
T: InlinedPair + ?Sized, // allow unsized boxes too
|
|
29
|
+
{
|
|
30
|
+
type Key = T::Key;
|
|
31
|
+
type Value = T::Value;
|
|
32
|
+
type Error = T::Error;
|
|
33
|
+
|
|
34
|
+
#[inline]
|
|
35
|
+
fn from_pair_mapping(k: Self::Key, v: Value) -> Result<Self, Self::Error> {
|
|
36
|
+
T::from_pair_mapping(k, v).map(|x| Box::new(x))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#[inline]
|
|
40
|
+
fn from_pair_simple(k: Self::Key, v: Value) -> Result<Self, Self::Error> {
|
|
41
|
+
T::from_pair_simple(k, v).map(|x| Box::new(x))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#[inline]
|
|
45
|
+
fn extract_key(&self) -> &Self::Key {
|
|
46
|
+
T::extract_key(self)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#[cfg(feature = "serde")]
|
|
52
|
+
#[allow(dead_code)]
|
|
53
|
+
pub fn deserialize_inlined_dict_list<'de, D, T>(de: D) -> Result<Vec<T>, D::Error>
|
|
54
|
+
where
|
|
55
|
+
D: Deserializer<'de>,
|
|
56
|
+
T: InlinedPair,
|
|
57
|
+
{
|
|
58
|
+
let raw: BTreeMap<T::Key, Value> = BTreeMap::deserialize(de)?;
|
|
59
|
+
raw.into_iter().map(|(k, v)| {
|
|
60
|
+
let obj = T::from_pair_mapping(k.clone(), v).map_err(D::Error::custom)?;
|
|
61
|
+
Ok(obj)
|
|
62
|
+
}).collect()
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#[cfg(feature = "serde")]
|
|
67
|
+
pub fn deserialize_inlined_dict_map<'de, D, T>(
|
|
68
|
+
de: D,
|
|
69
|
+
) -> Result<HashMap<T::Key, T>, D::Error>
|
|
70
|
+
where
|
|
71
|
+
D: Deserializer<'de>,
|
|
72
|
+
T: InlinedPair + Deserialize<'de>,
|
|
73
|
+
{
|
|
74
|
+
|
|
75
|
+
// Parse into a generic AST once
|
|
76
|
+
let ast: Value = Value::deserialize(de)?;
|
|
77
|
+
|
|
78
|
+
match ast {
|
|
79
|
+
// ---------- { key : value } form ----------
|
|
80
|
+
Value::Map(m) => {
|
|
81
|
+
let mut out = HashMap::with_capacity(m.len());
|
|
82
|
+
for (k_ast, v_ast) in m {
|
|
83
|
+
// 1) convert key and value separately
|
|
84
|
+
let key: T::Key = Deserialize::deserialize(
|
|
85
|
+
ValueDeserializer::<D::Error>::new(k_ast)
|
|
86
|
+
).map_err(D::Error::custom)?;
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
// ----------------- decide by the *value* shape
|
|
90
|
+
let obj = match v_ast {
|
|
91
|
+
// (1) full object (mapping) -> deserialize directly
|
|
92
|
+
Value::Map(_) => {
|
|
93
|
+
let m: Value = Deserialize::deserialize(
|
|
94
|
+
ValueDeserializer::<D::Error>::new(v_ast)
|
|
95
|
+
).map_err(D::Error::custom)?;
|
|
96
|
+
T::from_pair_mapping(key.clone(), m).map_err(D::Error::custom)?
|
|
97
|
+
}
|
|
98
|
+
other => {
|
|
99
|
+
T::from_pair_simple(key.clone(), other).map_err(D::Error::custom)?
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
out.insert(key, obj);
|
|
105
|
+
}
|
|
106
|
+
Ok(out)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------- [ value, ... ] form -------------
|
|
110
|
+
Value::Seq(seq) => {
|
|
111
|
+
let mut out = HashMap::with_capacity(seq.len());
|
|
112
|
+
for v_ast in seq {
|
|
113
|
+
let val: T = Deserialize::deserialize(
|
|
114
|
+
ValueDeserializer::<D::Error>::new(v_ast)
|
|
115
|
+
).map_err(D::Error::custom)?;
|
|
116
|
+
|
|
117
|
+
let key = val.extract_key().clone();
|
|
118
|
+
if out.insert(key, val).is_some() {
|
|
119
|
+
return Err(D::Error::custom("duplicate key"));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
Ok(out)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
_ => Err(D::Error::custom("expected mapping or sequence")),
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#[cfg(feature = "serde")]
|
|
130
|
+
pub fn deserialize_inlined_dict_map_optional<'de, D, T>(
|
|
131
|
+
de: D,
|
|
132
|
+
) -> Result<Option<HashMap<T::Key, T>>, D::Error>
|
|
133
|
+
where
|
|
134
|
+
D: Deserializer<'de>,
|
|
135
|
+
T: InlinedPair + Deserialize<'de>,
|
|
136
|
+
{
|
|
137
|
+
let ast: Value = Value::deserialize(de)?;
|
|
138
|
+
match ast {
|
|
139
|
+
Value::Unit => Ok(None),
|
|
140
|
+
Value::Map(_) | Value::Seq(_) => {
|
|
141
|
+
let map = deserialize_inlined_dict_map(ValueDeserializer::<D::Error>::new(ast))?;
|
|
142
|
+
Ok(Some(map))
|
|
143
|
+
}
|
|
144
|
+
_ => Err(D::Error::custom("expected mapping, sequence, or unit")),
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
#[cfg(feature = "serde")]
|
|
150
|
+
#[allow(dead_code)]
|
|
151
|
+
pub fn deserialize_inlined_dict_list_optional<'de, D, T>(
|
|
152
|
+
de: D,
|
|
153
|
+
) -> Result<Option<Vec<T>>, D::Error>
|
|
154
|
+
where
|
|
155
|
+
D: Deserializer<'de>,
|
|
156
|
+
T: InlinedPair + Deserialize<'de>,
|
|
157
|
+
{
|
|
158
|
+
let ast: Value = Value::deserialize(de)?;
|
|
159
|
+
match ast {
|
|
160
|
+
Value::Unit => Ok(None),
|
|
161
|
+
Value::Map(_) => {
|
|
162
|
+
let list = deserialize_inlined_dict_list(ValueDeserializer::<D::Error>::new(ast))?;
|
|
163
|
+
Ok(Some(list))
|
|
164
|
+
}
|
|
165
|
+
Value::Seq(seq) => {
|
|
166
|
+
let mut out = Vec::with_capacity(seq.len());
|
|
167
|
+
for v_ast in seq {
|
|
168
|
+
let val: T = Deserialize::deserialize(ValueDeserializer::<D::Error>::new(v_ast))
|
|
169
|
+
.map_err(D::Error::custom)?;
|
|
170
|
+
out.push(val);
|
|
171
|
+
}
|
|
172
|
+
Ok(Some(out))
|
|
173
|
+
}
|
|
174
|
+
_ => Err(D::Error::custom("expected mapping, sequence, or unit")),
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
pub fn deserialize_primitive_list_or_single_value<'de, D, T>(
|
|
179
|
+
deserializer: D
|
|
180
|
+
) -> Result<Vec<T>, D::Error> where D: Deserializer<'de>, T: Deserialize<'de> {
|
|
181
|
+
let ast: Value = Value::deserialize(deserializer)?;
|
|
182
|
+
match ast {
|
|
183
|
+
Value::Seq(seq) => {
|
|
184
|
+
seq.into_iter()
|
|
185
|
+
.map(|v| T::deserialize(ValueDeserializer::<D::Error>::new(v)))
|
|
186
|
+
.collect()
|
|
187
|
+
}
|
|
188
|
+
Value::Unit => Ok(vec![]),
|
|
189
|
+
other => {
|
|
190
|
+
let single_value: T = Deserialize::deserialize(
|
|
191
|
+
ValueDeserializer::<D::Error>::new(other)
|
|
192
|
+
).map_err(D::Error::custom)?;
|
|
193
|
+
Ok(vec![single_value])
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
pub fn deserialize_primitive_list_or_single_value_optional<'de, D, T>(
|
|
200
|
+
deserializer: D
|
|
201
|
+
) -> Result<Option<Vec<T>>, D::Error> where D: Deserializer<'de>, T: Deserialize<'de> {
|
|
202
|
+
let ast: Value = Value::deserialize(deserializer)?;
|
|
203
|
+
match ast {
|
|
204
|
+
Value::Unit => Ok(None),
|
|
205
|
+
_ => {
|
|
206
|
+
let d = deserialize_primitive_list_or_single_value(ValueDeserializer::<D::Error>::new(ast))?;
|
|
207
|
+
Ok(Some(d))
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#[cfg(all(feature = "serde", feature = "pyo3"))]
|
|
213
|
+
fn py_any_to_value(bound: &Bound<'_, PyAny>) -> PyResult<Value> {
|
|
214
|
+
if bound.is_none() {
|
|
215
|
+
return Ok(Value::Unit);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if let Ok(b) = bound.extract::<bool>() {
|
|
219
|
+
return Ok(Value::Bool(b));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if let Ok(i) = bound.extract::<i64>() {
|
|
223
|
+
return Ok(Value::I64(i));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if let Ok(f) = bound.extract::<f64>() {
|
|
227
|
+
return Ok(Value::F64(f));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if let Ok(s) = bound.extract::<String>() {
|
|
231
|
+
return Ok(Value::String(s));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if let Ok(dict_obj) = bound.getattr("__dict__") {
|
|
235
|
+
if let Ok(dict) = dict_obj.downcast::<PyDict>() {
|
|
236
|
+
let mut map: BTreeMap<Value, Value> = BTreeMap::new();
|
|
237
|
+
for (k, v) in dict.iter() {
|
|
238
|
+
let key_str: String = k.extract()?;
|
|
239
|
+
let val = py_any_to_value(&v)?;
|
|
240
|
+
map.insert(Value::String(key_str), val);
|
|
241
|
+
}
|
|
242
|
+
return Ok(Value::Map(map));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if let Ok(dict) = bound.downcast::<PyDict>() {
|
|
247
|
+
let mut map: BTreeMap<Value, Value> = BTreeMap::new();
|
|
248
|
+
for (k, v) in dict.iter() {
|
|
249
|
+
let key_str: String = k.extract()?;
|
|
250
|
+
let val = py_any_to_value(&v)?;
|
|
251
|
+
map.insert(Value::String(key_str), val);
|
|
252
|
+
}
|
|
253
|
+
return Ok(Value::Map(map));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if let Ok(list) = bound.downcast::<PyList>() {
|
|
257
|
+
let mut items = Vec::with_capacity(list.len());
|
|
258
|
+
for item in list.iter() {
|
|
259
|
+
items.push(py_any_to_value(&item)?);
|
|
260
|
+
}
|
|
261
|
+
return Ok(Value::Seq(items));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if let Ok(tuple) = bound.downcast::<PyTuple>() {
|
|
265
|
+
let mut items = Vec::with_capacity(tuple.len());
|
|
266
|
+
for item in tuple.iter() {
|
|
267
|
+
items.push(py_any_to_value(&item)?);
|
|
268
|
+
}
|
|
269
|
+
return Ok(Value::Seq(items));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
|
273
|
+
"unsupported value type for conversion",
|
|
274
|
+
))
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
#[cfg(all(feature = "serde", feature = "pyo3"))]
|
|
278
|
+
pub fn deserialize_py_any<'py, T>(bound: &Bound<'py, PyAny>) -> PyResult<T>
|
|
279
|
+
where
|
|
280
|
+
T: DeserializeOwned,
|
|
281
|
+
{
|
|
282
|
+
let value = py_any_to_value(bound)?;
|
|
283
|
+
let de = value.into_deserializer();
|
|
284
|
+
match serde_path_to_error::deserialize(de) {
|
|
285
|
+
Ok(ok) => Ok(ok),
|
|
286
|
+
Err(err) => Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
|
|
287
|
+
format!("at `{}`: {}", err.path(), err.inner()),
|
|
288
|
+
)),
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
#[cfg(feature = "pyo3")]
|
|
293
|
+
pub struct PyValue<T>(pub T);
|
|
294
|
+
|
|
295
|
+
#[cfg(feature = "pyo3")]
|
|
296
|
+
impl<T> PyValue<T> {
|
|
297
|
+
pub fn into_inner(self) -> T {
|
|
298
|
+
self.0
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
#[cfg(all(feature = "pyo3", feature = "serde"))]
|
|
303
|
+
impl<'py, T> FromPyObject<'py> for PyValue<T>
|
|
304
|
+
where
|
|
305
|
+
T: DeserializeOwned,
|
|
306
|
+
{
|
|
307
|
+
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
|
308
|
+
deserialize_py_any(ob).map(PyValue)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
2
|
+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
3
|
+
pub enum {{ slot_name }}_range {
|
|
4
|
+
{% for r in ranges %}
|
|
5
|
+
{{ r }}({{ r }}){% if not loop.last %},
|
|
6
|
+
{% endif %}
|
|
7
|
+
{% endfor %}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
#[cfg(feature = "pyo3")]
|
|
11
|
+
impl<'py> FromPyObject<'py> for {{ slot_name }}_range {
|
|
12
|
+
fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
|
|
13
|
+
{% for t in ranges %}
|
|
14
|
+
if let Ok(val) = ob.extract::<{{ t }}>() {
|
|
15
|
+
return Ok({{ slot_name }}_range::{{ t }}(val));
|
|
16
|
+
}
|
|
17
|
+
{%- endfor -%}
|
|
18
|
+
Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
|
19
|
+
"invalid {{ slot_name }}",
|
|
20
|
+
))
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#[cfg(feature = "pyo3")]
|
|
25
|
+
impl<'py> IntoPyObject<'py> for {{ slot_name }}_range {
|
|
26
|
+
type Target = PyAny;
|
|
27
|
+
type Output = Bound<'py, Self::Target>;
|
|
28
|
+
type Error = PyErr;
|
|
29
|
+
|
|
30
|
+
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
31
|
+
match self {
|
|
32
|
+
{% for t in ranges %}
|
|
33
|
+
{{ slot_name }}_range::{{ t }}(val) => Ok(val.into_pyobject(py).map(move |b| <pyo3::Bound<'_, _> as Clone>::clone(&b).into_any())?),
|
|
34
|
+
{% endfor %}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
#[cfg(feature = "pyo3")]
|
|
41
|
+
impl<'py> IntoPyObject<'py> for Box<{{ slot_name }}_range>
|
|
42
|
+
{
|
|
43
|
+
type Target = PyAny;
|
|
44
|
+
type Output = Bound<'py, Self::Target>;
|
|
45
|
+
type Error = PyErr;
|
|
46
|
+
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
47
|
+
(*self).into_pyobject(py).map(move |x| x.into_any())
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#[cfg(feature = "pyo3")]
|
|
52
|
+
impl<'py> FromPyObject<'py> for Box<{{ slot_name }}_range> {
|
|
53
|
+
fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
|
|
54
|
+
if let Ok(val) = ob.extract::<{{ slot_name }}_range>() {
|
|
55
|
+
return Ok(Box::new(val));
|
|
56
|
+
}
|
|
57
|
+
Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
|
58
|
+
"invalid {{ slot_name }}",
|
|
59
|
+
))
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{% if special_case_enabled and name == 'Anything' %}
|
|
2
|
+
{% include 'anything.rs.jinja' %}
|
|
3
|
+
{% elif special_case_enabled and name == 'AnyValue' %}
|
|
4
|
+
pub type AnyValue = Anything;
|
|
5
|
+
{% else %}
|
|
6
|
+
{% if class_module %}
|
|
7
|
+
{{ class_module }}
|
|
8
|
+
{% endif %}
|
|
9
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
10
|
+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
11
|
+
#[cfg_attr(feature = "pyo3", pyclass(subclass, get_all, set_all))]
|
|
12
|
+
{% if generate_merge %}
|
|
13
|
+
#[derive(Merge)]
|
|
14
|
+
{% endif %}
|
|
15
|
+
pub struct {{ name }} {
|
|
16
|
+
{% for property in properties %}
|
|
17
|
+
{% filter indent(width=4, first=True, blank=False) %}
|
|
18
|
+
{{ property }}
|
|
19
|
+
{%- if not loop.last -%},{{ '\n' }}{% else %}{{ '\n' }}{%- endif -%}
|
|
20
|
+
{% endfilter %}
|
|
21
|
+
{% endfor %}
|
|
22
|
+
}
|
|
23
|
+
{% if properties | length > 0 %}
|
|
24
|
+
#[cfg(feature = "pyo3")]
|
|
25
|
+
#[pymethods]
|
|
26
|
+
impl {{ name }} {
|
|
27
|
+
#[new]
|
|
28
|
+
#[pyo3(signature = ({{ constructor_signature }}))]
|
|
29
|
+
pub fn new({% for (n, t) in property_names_and_types %}{{ n }}: {{ t }}{% if not loop.last %}, {% endif %}{% endfor %}) -> Self {
|
|
30
|
+
{% for name, expr in constructor_conversions %}
|
|
31
|
+
let {{ name }} = {{ expr }};
|
|
32
|
+
{% endfor %}
|
|
33
|
+
{{ name }}{{ "{" }}{% for (p, _) in property_names_and_types %}{{ p }}{% if not loop.last %}, {% endif %}{% endfor %}{{ "}" }}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#[cfg(feature = "pyo3")]
|
|
38
|
+
impl<'py> IntoPyObject<'py> for Box<{{ name }}>
|
|
39
|
+
{
|
|
40
|
+
type Target = PyAny;
|
|
41
|
+
type Output = Bound<'py, Self::Target>;
|
|
42
|
+
type Error = PyErr;
|
|
43
|
+
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
44
|
+
(*self).into_pyobject(py).map(move |x| x.into_any())
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#[cfg(feature = "pyo3")]
|
|
49
|
+
impl<'py> FromPyObject<'py> for Box<{{ name }}> {
|
|
50
|
+
fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
|
|
51
|
+
if let Ok(val) = ob.extract::<{{ name }}>() {
|
|
52
|
+
return Ok(Box::new(val));
|
|
53
|
+
}
|
|
54
|
+
Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
|
55
|
+
"invalid {{ name}}",
|
|
56
|
+
))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
{% if generate_merge %}
|
|
61
|
+
impl {{ name }} {
|
|
62
|
+
pub fn merge_with(&mut self, other: &{{ name }}) {
|
|
63
|
+
self.merge(other.clone())
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
{% endif %}
|
|
67
|
+
|
|
68
|
+
{% endif %}
|
|
69
|
+
{% if as_key_value %}
|
|
70
|
+
{{ as_key_value }}
|
|
71
|
+
{% endif %}
|
|
72
|
+
{% if struct_or_subtype_enum %}
|
|
73
|
+
{{ struct_or_subtype_enum }}
|
|
74
|
+
{% endif %}
|
|
75
|
+
{% endif %}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
2
|
+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
3
|
+
{% if type_designator_field %}
|
|
4
|
+
#[cfg_attr(feature="serde", serde(tag = "{{ type_designator_field }}"))]
|
|
5
|
+
{% else %}
|
|
6
|
+
#[cfg_attr(feature="serde", serde(untagged))]
|
|
7
|
+
{% endif %}
|
|
8
|
+
pub enum {{ enum_name }} {
|
|
9
|
+
{%- for t in struct_names -%}
|
|
10
|
+
{% if type_designator_field %}
|
|
11
|
+
{% set tds = type_designators[t] %}
|
|
12
|
+
#[serde(rename = "{{ tds[0] }}", {% for t in tds[1:] %} alias="{{ t }}", {% endfor %} )]
|
|
13
|
+
{% endif %}
|
|
14
|
+
{{ t }}({{ t }}){% if not loop.last %}, {% endif %}
|
|
15
|
+
{%- endfor -%}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
{% for t in struct_names %}
|
|
19
|
+
impl From<{{ t }}> for {{ enum_name }} { fn from(x: {{ t }}) -> Self { Self::{{ t }}(x) } }
|
|
20
|
+
{% endfor %}
|
|
21
|
+
|
|
22
|
+
#[cfg(feature = "pyo3")]
|
|
23
|
+
impl<'py> FromPyObject<'py> for {{ enum_name }} {
|
|
24
|
+
fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
|
|
25
|
+
{% for t in struct_names %}
|
|
26
|
+
if let Ok(val) = ob.extract::<{{ t }}>() {
|
|
27
|
+
return Ok({{ enum_name }}::{{ t }}(val));
|
|
28
|
+
}
|
|
29
|
+
{%- endfor -%}
|
|
30
|
+
Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
|
31
|
+
"invalid {{ enum_name }}",
|
|
32
|
+
))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#[cfg(feature = "pyo3")]
|
|
37
|
+
impl<'py> IntoPyObject<'py> for {{ enum_name }} {
|
|
38
|
+
type Target = PyAny;
|
|
39
|
+
type Output = Bound<'py, Self::Target>;
|
|
40
|
+
type Error = PyErr;
|
|
41
|
+
|
|
42
|
+
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
43
|
+
match self {
|
|
44
|
+
{% for t in struct_names %}
|
|
45
|
+
{{ enum_name }}::{{ t }}(val) => val.into_pyobject(py).map(move |b| b.into_any()),
|
|
46
|
+
{% endfor %}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
#[cfg(feature = "pyo3")]
|
|
53
|
+
impl<'py> IntoPyObject<'py> for Box<{{ enum_name }}>
|
|
54
|
+
{
|
|
55
|
+
type Target = PyAny;
|
|
56
|
+
type Output = Bound<'py, Self::Target>;
|
|
57
|
+
type Error = PyErr;
|
|
58
|
+
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
59
|
+
(*self).into_pyobject(py).map(move |x| x.into_any())
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
#[cfg(feature = "pyo3")]
|
|
64
|
+
impl<'py> FromPyObject<'py> for Box<{{ enum_name }}> {
|
|
65
|
+
fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
|
|
66
|
+
if let Ok(val) = ob.extract::<{{ enum_name }}>() {
|
|
67
|
+
return Ok(Box::new(val));
|
|
68
|
+
}
|
|
69
|
+
Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
|
70
|
+
"invalid {{ enum_name }}",
|
|
71
|
+
))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
{% if as_key_value %}
|
|
76
|
+
#[cfg(feature = "serde")]
|
|
77
|
+
impl serde_utils::InlinedPair for {{enum_name}} {
|
|
78
|
+
type Key = String;
|
|
79
|
+
type Value = serde_value::Value;
|
|
80
|
+
type Error = String;
|
|
81
|
+
|
|
82
|
+
fn from_pair_mapping(k: Self::Key, v: Self::Value) -> Result<Self, Self::Error> {
|
|
83
|
+
{% for t in struct_names %}
|
|
84
|
+
if let Ok(x) = {{t}}::from_pair_mapping(k.clone(), v.clone()) {
|
|
85
|
+
return Ok({{enum_name}}::{{t}}(x));
|
|
86
|
+
}
|
|
87
|
+
{% endfor %}
|
|
88
|
+
Err("none of the variants matched the mapping form".into())
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
fn from_pair_simple(k: Self::Key, v: Self::Value) -> Result<Self, Self::Error> {
|
|
92
|
+
{% for t in struct_names %}
|
|
93
|
+
if let Ok(x) = {{t}}::from_pair_simple(k.clone(), v.clone()) {
|
|
94
|
+
return Ok({{enum_name}}::{{t}}(x));
|
|
95
|
+
}
|
|
96
|
+
{% endfor %}
|
|
97
|
+
Err("none of the variants support the primitive form".into())
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fn extract_key(&self) -> &Self::Key {
|
|
101
|
+
match self {
|
|
102
|
+
{% for t in struct_names %}
|
|
103
|
+
{{enum_name}}::{{t}}(inner) => inner.extract_key(),
|
|
104
|
+
{% endfor %}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
{% endif %}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% if attributes or description %}
|
|
2
|
+
#[
|
|
3
|
+
{%- if description -%}doc = r" {{ description | escape }}", {%- endif -%}
|
|
4
|
+
{%- for key, val in attributes.items() -%}
|
|
5
|
+
{{ key }} = "{{ val }}"{% if not loop.last %}, {% endif %}
|
|
6
|
+
{%- endfor -%}
|
|
7
|
+
]
|
|
8
|
+
{%- endif -%}
|
|
9
|
+
pub type {{ name }} = {%- if multivalued %} Vec<{{ type_ }}>
|
|
10
|
+
{%- else %} {{ type_ }}{%- endif -%};
|
|
11
|
+
{%- if slot_range_as_union %}
|
|
12
|
+
{{ slot_range_as_union }}
|
|
13
|
+
{% endif -%}
|
linkml/generators/sqltablegen.py
CHANGED
|
@@ -149,8 +149,7 @@ class SQLTableGenerator(Generator):
|
|
|
149
149
|
relative_slot_num: bool = False
|
|
150
150
|
default_length_oracle: int = ORACLE_MAX_VARCHAR_LENGTH
|
|
151
151
|
generate_abstract_class_ddl: bool = True
|
|
152
|
-
|
|
153
|
-
autogenerate_fk_index: bool = True
|
|
152
|
+
autogenerate_index: bool = True
|
|
154
153
|
|
|
155
154
|
def serialize(self, **kwargs: dict[str, Any]) -> str:
|
|
156
155
|
return self.generate_ddl(**kwargs)
|
|
@@ -235,8 +234,8 @@ class SQLTableGenerator(Generator):
|
|
|
235
234
|
fk = sql_name(self.get_id_or_key(s.range, sv))
|
|
236
235
|
args = [ForeignKey(fk)]
|
|
237
236
|
field_type = self.get_sql_range(s, schema)
|
|
238
|
-
fk_index_cond = (s.key or s.identifier) and self.
|
|
239
|
-
pk_index_cond = is_pk and self.
|
|
237
|
+
fk_index_cond = (s.key or s.identifier) and self.autogenerate_index
|
|
238
|
+
pk_index_cond = is_pk and self.autogenerate_index
|
|
240
239
|
is_index = fk_index_cond or pk_index_cond
|
|
241
240
|
col = Column(
|
|
242
241
|
sql_name(sn),
|
|
@@ -266,18 +265,21 @@ class SQLTableGenerator(Generator):
|
|
|
266
265
|
sql_uc = UniqueConstraint(*sql_names)
|
|
267
266
|
cols.append(sql_uc)
|
|
268
267
|
# Anything that has a unique constraint should have an associated index with it
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
268
|
+
if self.autogenerate_index:
|
|
269
|
+
uc_index_name = sql_name(cn)
|
|
270
|
+
for name in sql_names:
|
|
271
|
+
uc_index_name = uc_index_name + "_" + name
|
|
272
|
+
uc_index_name = uc_index_name + "_idx"
|
|
273
|
+
is_duplicate = self.check_duplicate_entry_names(
|
|
274
|
+
autogenerated_item_names, sql_name(uc_index_name)
|
|
275
|
+
)
|
|
276
|
+
if not is_duplicate:
|
|
277
|
+
sql_names = [sql_name(uc_index_name)] + sql_names
|
|
278
|
+
uc_index = Index(*sql_names)
|
|
279
|
+
cols.append(uc_index)
|
|
280
|
+
autogenerated_item_names.append(sql_name(uc_index_name))
|
|
279
281
|
if not c.abstract or (c.abstract and self.generate_abstract_class_ddl):
|
|
280
|
-
for tag, annotation in c.annotations.items():
|
|
282
|
+
for tag, annotation in sorted(c.annotations.items()):
|
|
281
283
|
if tag == "index":
|
|
282
284
|
value_dict = {k: annotation for k, annotation in annotation.value._items()}
|
|
283
285
|
for key, value in value_dict.items():
|
|
@@ -424,7 +426,7 @@ class SQLTableGenerator(Generator):
|
|
|
424
426
|
)
|
|
425
427
|
@click.option(
|
|
426
428
|
"--autogenerate_index",
|
|
427
|
-
default=
|
|
429
|
+
default=True,
|
|
428
430
|
show_default=True,
|
|
429
431
|
help="Enable the creation of indexes on all columns generated",
|
|
430
432
|
)
|