linkml 1.9.4rc2__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.
Files changed (77) hide show
  1. linkml/cli/main.py +4 -0
  2. linkml/generators/__init__.py +2 -0
  3. linkml/generators/common/build.py +5 -20
  4. linkml/generators/common/template.py +289 -3
  5. linkml/generators/docgen.py +55 -10
  6. linkml/generators/erdiagramgen.py +9 -5
  7. linkml/generators/graphqlgen.py +32 -6
  8. linkml/generators/jsonldcontextgen.py +78 -12
  9. linkml/generators/jsonschemagen.py +29 -12
  10. linkml/generators/mermaidclassdiagramgen.py +21 -3
  11. linkml/generators/owlgen.py +4 -1
  12. linkml/generators/panderagen/dataframe_class.py +13 -0
  13. linkml/generators/panderagen/dataframe_field.py +50 -0
  14. linkml/generators/panderagen/linkml_pandera_validator.py +186 -0
  15. linkml/generators/panderagen/panderagen.py +22 -5
  16. linkml/generators/panderagen/panderagen_class_based/class.jinja2 +70 -13
  17. linkml/generators/panderagen/panderagen_class_based/custom_checks.jinja2 +27 -0
  18. linkml/generators/panderagen/panderagen_class_based/enums.jinja2 +3 -3
  19. linkml/generators/panderagen/panderagen_class_based/pandera.jinja2 +12 -2
  20. linkml/generators/panderagen/panderagen_class_based/slots.jinja2 +19 -17
  21. linkml/generators/panderagen/slot_generator_mixin.py +143 -16
  22. linkml/generators/panderagen/transforms/__init__.py +19 -0
  23. linkml/generators/panderagen/transforms/collection_dict_model_transform.py +62 -0
  24. linkml/generators/panderagen/transforms/list_dict_model_transform.py +66 -0
  25. linkml/generators/panderagen/transforms/model_transform.py +8 -0
  26. linkml/generators/panderagen/transforms/nested_struct_model_transform.py +27 -0
  27. linkml/generators/panderagen/transforms/simple_dict_model_transform.py +86 -0
  28. linkml/generators/plantumlgen.py +17 -11
  29. linkml/generators/pydanticgen/pydanticgen.py +53 -2
  30. linkml/generators/pydanticgen/template.py +45 -233
  31. linkml/generators/pydanticgen/templates/attribute.py.jinja +1 -0
  32. linkml/generators/pydanticgen/templates/base_model.py.jinja +16 -2
  33. linkml/generators/pydanticgen/templates/imports.py.jinja +1 -1
  34. linkml/generators/rdfgen.py +11 -2
  35. linkml/generators/rustgen/__init__.py +3 -0
  36. linkml/generators/rustgen/build.py +94 -0
  37. linkml/generators/rustgen/cli.py +65 -0
  38. linkml/generators/rustgen/rustgen.py +1038 -0
  39. linkml/generators/rustgen/template.py +865 -0
  40. linkml/generators/rustgen/templates/Cargo.toml.jinja +42 -0
  41. linkml/generators/rustgen/templates/anything.rs.jinja +142 -0
  42. linkml/generators/rustgen/templates/as_key_value.rs.jinja +56 -0
  43. linkml/generators/rustgen/templates/class_module.rs.jinja +8 -0
  44. linkml/generators/rustgen/templates/enum.rs.jinja +54 -0
  45. linkml/generators/rustgen/templates/file.rs.jinja +62 -0
  46. linkml/generators/rustgen/templates/import.rs.jinja +4 -0
  47. linkml/generators/rustgen/templates/imports.rs.jinja +8 -0
  48. linkml/generators/rustgen/templates/poly.rs.jinja +9 -0
  49. linkml/generators/rustgen/templates/poly_containers.rs.jinja +439 -0
  50. linkml/generators/rustgen/templates/poly_trait.rs.jinja +15 -0
  51. linkml/generators/rustgen/templates/poly_trait_impl.rs.jinja +5 -0
  52. linkml/generators/rustgen/templates/poly_trait_impl_orsubtype.rs.jinja +5 -0
  53. linkml/generators/rustgen/templates/poly_trait_property.rs.jinja +8 -0
  54. linkml/generators/rustgen/templates/poly_trait_property_impl.rs.jinja +132 -0
  55. linkml/generators/rustgen/templates/poly_trait_property_match.rs.jinja +10 -0
  56. linkml/generators/rustgen/templates/property.rs.jinja +19 -0
  57. linkml/generators/rustgen/templates/pyproject.toml.jinja +10 -0
  58. linkml/generators/rustgen/templates/serde_utils.rs.jinja +310 -0
  59. linkml/generators/rustgen/templates/slot_range_as_union.rs.jinja +61 -0
  60. linkml/generators/rustgen/templates/struct.rs.jinja +75 -0
  61. linkml/generators/rustgen/templates/struct_or_subtype_enum.rs.jinja +108 -0
  62. linkml/generators/rustgen/templates/typealias.rs.jinja +13 -0
  63. linkml/generators/sqltablegen.py +18 -16
  64. linkml/generators/yarrrmlgen.py +157 -0
  65. linkml/linter/config/datamodel/config.py +160 -293
  66. linkml/linter/config/datamodel/config.yaml +34 -26
  67. linkml/linter/config/default.yaml +4 -0
  68. linkml/linter/config/recommended.yaml +4 -0
  69. linkml/linter/linter.py +1 -2
  70. linkml/linter/rules.py +37 -0
  71. linkml/utils/schemaloader.py +55 -3
  72. {linkml-1.9.4rc2.dist-info → linkml-1.9.5rc1.dist-info}/METADATA +1 -1
  73. {linkml-1.9.4rc2.dist-info → linkml-1.9.5rc1.dist-info}/RECORD +76 -38
  74. {linkml-1.9.4rc2.dist-info → linkml-1.9.5rc1.dist-info}/entry_points.txt +1 -0
  75. linkml/generators/panderagen/panderagen_class_based/mixins.jinja2 +0 -26
  76. {linkml-1.9.4rc2.dist-info → linkml-1.9.5rc1.dist-info}/WHEEL +0 -0
  77. {linkml-1.9.4rc2.dist-info → linkml-1.9.5rc1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,42 @@
1
+ [package]
2
+ name = "{{ name }}"
3
+ version = "{{ version }}"
4
+ edition = "{{ edition }}"
5
+
6
+ [lib]
7
+ name = "{{ name }}"
8
+ {% if pyo3 %}
9
+ crate-type = ["cdylib"]
10
+ {% else %}
11
+ crate-type = ["lib"]
12
+ {% endif %}
13
+
14
+ [dependencies]
15
+ {% for i in imports %}
16
+ {% if i.version %}
17
+ {% if i.features %}
18
+ {{ i.module }} = { version = "{{ i.version }}", features = [{% for feature in i.features %}"{{ feature }}"{% if not loop.last %},{% endif %}{% endfor %}] {% if i.feature_flag %} , optional = true {% endif %} }
19
+ {% else %}
20
+ {% if i.feature_flag %}
21
+ {{ i.module }} = { version = "{{ i.version }}", optional = true }
22
+ {% else %}
23
+ {{ i.module }} = "{{ i.version }}"
24
+ {% endif %}
25
+ {% endif %}
26
+ {% endif %}
27
+ {% endfor %}
28
+
29
+
30
+ [features]
31
+ {% if pyo3 and serde %}
32
+ default = ["pyo3", "serde"]
33
+ {% elif pyo3 %}
34
+ default = ["pyo3"]
35
+ {% elif serde %}
36
+ default = ["serde"]
37
+ {% else %}
38
+ default = []
39
+ {% endif %}
40
+ {% for k,v in cratefeatures.items() %}
41
+ {{ k }} = [{% for i in v %}"dep:{{ i }}"{% if not loop.last %}, {% endif %}{% endfor %}]
42
+ {% endfor %}
@@ -0,0 +1,142 @@
1
+ #[derive(Clone, PartialEq)]
2
+ pub struct Anything(
3
+ #[cfg(feature = "serde")] pub serde_value::Value,
4
+ #[cfg(not(feature = "serde"))] pub (),
5
+ );
6
+
7
+
8
+ #[cfg(feature = "serde")]
9
+ impl Serialize for Anything {
10
+ fn serialize<S>(&self, to_ser: S) -> Result<S::Ok, S::Error>
11
+ where S: serde::Serializer {
12
+ self.0.serialize(to_ser)
13
+ }
14
+ }
15
+
16
+ #[cfg(feature = "serde")]
17
+ impl<'de> Deserialize<'de> for Anything {
18
+ fn deserialize<D>(de: D) -> Result<Self, D::Error>
19
+ where D: serde::Deserializer<'de> {
20
+ <serde_value::Value as Deserialize>::deserialize(de).map(Anything)
21
+ }
22
+ }
23
+
24
+ #[cfg(all(feature = "pyo3", feature = "serde"))]
25
+ impl<'py> FromPyObject<'py> for Anything {
26
+ fn extract_bound(obj: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
27
+ use pyo3::types::{PyAny, PyDict, PyList, PyTuple};
28
+ use serde_value::Value;
29
+
30
+ fn py_to_value<'py>(o: &pyo3::Bound<'py, PyAny>) -> pyo3::PyResult<Value> {
31
+ // None -> Unit
32
+ if o.is_none() {
33
+ return Ok(Value::Unit);
34
+ }
35
+
36
+ // Try simple primitives first
37
+ if let Ok(s) = o.extract::<&str>() {
38
+ return Ok(Value::String(s.to_string()));
39
+ }
40
+ if let Ok(b) = o.extract::<bool>() {
41
+ return Ok(Value::Bool(b));
42
+ }
43
+
44
+ // Sequences (list/tuple)
45
+ if let Ok(list) = o.downcast::<PyList>() {
46
+ let mut out = Vec::with_capacity(list.len());
47
+ for item in list.iter() {
48
+ out.push(py_to_value(&item)?);
49
+ }
50
+ return Ok(Value::Seq(out));
51
+ }
52
+ if let Ok(t) = o.downcast::<PyTuple>() {
53
+ let mut out = Vec::with_capacity(t.len());
54
+ for item in t.iter() {
55
+ out.push(py_to_value(&item)?);
56
+ }
57
+ return Ok(Value::Seq(out));
58
+ }
59
+
60
+ // Mappings (dict with string-like keys)
61
+ if let Ok(d) = o.downcast::<PyDict>() {
62
+ let mut map = std::collections::BTreeMap::<Value, Value>::new();
63
+ for (k, v) in d.iter() {
64
+ // Only accept string-like keys for deterministic ordering
65
+ if let Ok(ks) = k.extract::<&str>() {
66
+ map.insert(Value::String(ks.to_string()), py_to_value(&v)?);
67
+ } else {
68
+ return Err(pyo3::exceptions::PyTypeError::new_err(
69
+ "dict keys for Anything must be str",
70
+ ));
71
+ }
72
+ }
73
+ return Ok(Value::Map(map));
74
+ }
75
+
76
+ // Fallback: stringify unknown types
77
+ let s = format!("{}", o.str()?);
78
+ Ok(Value::String(s))
79
+ }
80
+
81
+ Ok(Anything(py_to_value(obj)?))
82
+ }
83
+ }
84
+
85
+ /* ---------- getter side ---------- */
86
+ #[cfg(all(feature = "pyo3", feature = "serde"))]
87
+ impl<'py> IntoPyObject<'py> for Anything {
88
+ type Target = PyAny;
89
+ type Output = Bound<'py, Self::Target>;
90
+ type Error = PyErr;
91
+
92
+ fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
93
+ use pyo3::types::{PyAny, PyDict, PyList, PyString};
94
+ use pyo3::PyObject;
95
+ use serde_value::Value;
96
+
97
+ fn value_to_py<'py>(py: Python<'py>, v: &Value) -> pyo3::PyResult<Bound<'py, PyAny>> {
98
+ match v {
99
+ Value::Unit => Ok(py.None().into_bound(py)),
100
+ Value::Bool(b) => Ok(pyo3::types::PyBool::new(py, *b).to_owned().into_any()),
101
+ Value::String(s) => Ok(PyString::new(py, s).into_any()),
102
+ Value::Seq(seq) => {
103
+ let list = PyList::empty(py);
104
+ for item in seq.iter() {
105
+ let ob = value_to_py(py, item)?;
106
+ list.append(ob)?;
107
+ }
108
+ Ok(list.into_any())
109
+ }
110
+ Value::Map(map) => {
111
+ let dict = PyDict::new(py);
112
+ for (k, v) in map.iter() {
113
+ let pk = value_to_py(py, k)?;
114
+ let pv = value_to_py(py, v)?;
115
+ dict.set_item(pk, pv)?;
116
+ }
117
+ Ok(dict.into_any())
118
+ }
119
+ // Best-effort for other serde_value variants
120
+ // (numbers, bytes, chars, etc.)
121
+ other => {
122
+ // Try common cases without bringing extra deps
123
+ // Numbers are converted via string if not covered above
124
+ let s = format!("{:?}", other);
125
+ Ok(PyString::new(py, &s).into_any())
126
+ }
127
+ }
128
+ }
129
+
130
+ value_to_py(py, &self.0)
131
+ }
132
+ }
133
+
134
+ impl std::fmt::Debug for Anything {
135
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136
+ #[cfg(feature = "serde")]
137
+ return write!(f, "Anything({:?})", self.0);
138
+
139
+ #[cfg(not(feature = "serde"))]
140
+ return f.write_str("Anything(<opaque>)");
141
+ }
142
+ }
@@ -0,0 +1,56 @@
1
+ #[cfg(feature = "serde")]
2
+ impl serde_utils::InlinedPair for {{ name }} {
3
+ type Key = {{ key_property_type }};
4
+ {% if can_convert_from_primitive %}
5
+ type Value = {{ value_property_type }};
6
+ {% else %}
7
+ type Value = Value;
8
+ {% endif %}
9
+ type Error = String;
10
+
11
+ fn extract_key(&self) -> &Self::Key {
12
+ return &self.{{ key_property_name }};
13
+ }
14
+
15
+ fn from_pair_mapping(k: Self::Key, v: Value) -> Result<Self,Self::Error> {
16
+ let mut map = match v {
17
+ Value::Map(m) => m,
18
+ _ => return Err("ClassDefinition must be a mapping".into()),
19
+ };
20
+ map.insert(Value::String("{{key_property_name}}".into()), Value::String(k));
21
+ let de = Value::Map(map).into_deserializer();
22
+ match serde_path_to_error::deserialize(de) {
23
+ Ok(ok) => Ok(ok),
24
+ Err(e) => Err(format!("at `{}`: {}", e.path(), e.inner())),
25
+ }
26
+ }
27
+
28
+
29
+ {% if can_convert_from_primitive %}
30
+ fn from_pair_simple(k: Self::Key, v: Value) -> Result<Self,Self::Error> {
31
+ let mut map: BTreeMap<Value, Value> = BTreeMap::new();
32
+ map.insert(Value::String("{{ key_property_name }}".into()), Value::String(k));
33
+ map.insert(Value::String("{{ value_property_name }}".into()), v);
34
+ let de = Value::Map(map).into_deserializer();
35
+ match serde_path_to_error::deserialize(de) {
36
+ Ok(ok) => Ok(ok),
37
+ Err(e) => Err(format!("at `{}`: {}", e.path(), e.inner())),
38
+ }
39
+
40
+ {% elif can_convert_from_empty %}
41
+ fn from_pair_simple(k: Self::Key, _v: Value) -> Result<Self,Self::Error> {
42
+ let mut map: BTreeMap<Value, Value> = BTreeMap::new();
43
+ map.insert(Value::String("{{ key_property_name }}".into()), Value::String(k));
44
+ let de = Value::Map(map).into_deserializer();
45
+ match serde_path_to_error::deserialize(de) {
46
+ Ok(ok) => Ok(ok),
47
+ Err(e) => Err(format!("at `{}`: {}", e.path(), e.inner())),
48
+ }
49
+
50
+
51
+ {% else %}
52
+ fn from_pair_simple(_k: Self::Key, _v: Value) -> Result<Self,Self::Error> {
53
+ Err("Cannot create a {{name}} from a primitive value!".into())
54
+ {% endif %}
55
+ }
56
+ }
@@ -0,0 +1,8 @@
1
+ {% if slot_ranges | length > 0%}
2
+ pub mod {{ class_name_snakecase }}_utl {
3
+ use super::*;
4
+ {% for u in slot_ranges %}
5
+ {{ u}}
6
+ {% endfor %}
7
+ }
8
+ {% endif %}
@@ -0,0 +1,54 @@
1
+ #[derive(Debug, Clone, PartialEq)]
2
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3
+ pub enum {{ name }} {
4
+ {% for item in items %}
5
+ {% if serde %}#[cfg_attr(feature = "serde", serde(rename = "{{ item.text_literal }}"))]
6
+ {% endif %}
7
+ {{ item.variant }},
8
+ {% endfor %}
9
+ }
10
+
11
+ impl core::fmt::Display for {{ name }} {
12
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
13
+ match self {
14
+ {% for item in items %}
15
+ {{ name }}::{{ item.variant }} => f.write_str("{{ item.text_literal }}"),
16
+ {% endfor %}
17
+ }
18
+ }
19
+ }
20
+
21
+ #[cfg(feature = "pyo3")]
22
+ impl<'py> IntoPyObject<'py> for {{ name }} {
23
+ type Target = PyAny;
24
+ type Output = Bound<'py, Self::Target>;
25
+ type Error = PyErr;
26
+ fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
27
+ let s: &str = match self {
28
+ {% for item in items %}
29
+ {{ name }}::{{ item.variant }} => "{{ item.text_literal }}",
30
+ {% endfor %}
31
+ };
32
+ Ok(pyo3::types::PyString::new(py, s).into_any())
33
+ }
34
+ }
35
+
36
+ #[cfg(feature = "pyo3")]
37
+ impl<'py> FromPyObject<'py> for {{ name }} {
38
+ fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
39
+ if let Ok(s) = ob.extract::<&str>() {
40
+ match s {
41
+ {% for item in items %}
42
+ {{ item.python_match_pattern }} => Ok({{ name }}::{{ item.variant }}),
43
+ {% endfor %}
44
+ _ => Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
45
+ format!("invalid value for {{ name }}: {}", s),
46
+ )),
47
+ }
48
+ } else {
49
+ Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
50
+ concat!("expected str for ", stringify!({{ name }})),
51
+ ))
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,62 @@
1
+ #![allow(non_camel_case_types)]
2
+
3
+ {% if inline_serde_utils %}
4
+ #[cfg(feature = "serde")]
5
+ mod serde_utils { {{ serde_utils }} }
6
+ {% else %}
7
+ #[cfg(feature = "serde")]
8
+ mod serde_utils;
9
+ {% endif %}
10
+ {% if emit_poly %}
11
+ pub mod poly;
12
+ pub mod poly_containers;
13
+ {% endif %}
14
+
15
+ {{ imports }}
16
+
17
+ // Types
18
+
19
+ {% for type in types %}
20
+ {{ type }}
21
+ {% endfor %}
22
+
23
+ // Slots
24
+
25
+ {% for slot in slots %}
26
+ {{ slot }}
27
+ {% endfor %}
28
+
29
+ // Enums
30
+
31
+ {% for enum in enums %}
32
+ {{ enum }}
33
+ {% endfor %}
34
+
35
+ // Classes
36
+
37
+ {% for struct in structs %}
38
+ {{ struct }}
39
+ {% endfor %}
40
+
41
+
42
+ {% if needs_overwrite_except_none %}
43
+ /// Overwrite `left` with `right` unless `right` is `None`.
44
+ fn overwrite_except_none<T>(left: &mut Option<T>, right: Option<T>) {
45
+ if right.is_some() {
46
+ *left = right;
47
+ }
48
+ }
49
+ {% endif %}
50
+
51
+
52
+ {% if pyo3 %}
53
+ #[cfg(feature = "pyo3")]
54
+ #[pymodule]
55
+ #[pyo3(name="{{ name }}")]
56
+ fn {{ name }}(m: &Bound<'_, PyModule>) -> PyResult<()> {
57
+ {% for s in pyclass_struct_names %}
58
+ m.add_class::<{{ s }}>()?;
59
+ {% endfor %}
60
+ Ok(())
61
+ }
62
+ {% endif %}
@@ -0,0 +1,4 @@
1
+ {% if feature_flag %}
2
+ #[cfg(feature = "{{ feature_flag }}")]
3
+ {% endif %}
4
+ use {{ module.replace('-','_') }}{% if objects %}::{{ "{" if objects|length > 1 else "" }}{% for obj in objects %}{{ obj.name }}{% if obj.alias %} as {{ obj.alias}}{% endif %}{% if not loop.last %},{% endif %}{% endfor %}{{ "}" if objects|length > 1 else "" }}{% else %}{% if alias %} as {{ alias }} {% endif %}{% endif %};
@@ -0,0 +1,8 @@
1
+ {% if imports is string %}
2
+ {{ imports }}
3
+ {% else %}
4
+ {% for import in imports %}
5
+ {{ import }}
6
+ {% endfor %}
7
+ {% endif %}
8
+ use std::collections::BTreeMap;
@@ -0,0 +1,9 @@
1
+ #![allow(non_camel_case_types)]
2
+
3
+ use crate::*;
4
+ use crate::poly_containers::*;
5
+
6
+
7
+ {% for t in traits %}
8
+ {{ t }}
9
+ {% endfor %}