linkml 1.8.2__tar.gz → 1.8.3__tar.gz

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 (161) hide show
  1. {linkml-1.8.2 → linkml-1.8.3}/PKG-INFO +1 -1
  2. {linkml-1.8.2 → linkml-1.8.3}/linkml/cli/main.py +4 -0
  3. linkml-1.8.3/linkml/generators/common/ifabsent_processor.py +286 -0
  4. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/jsonldcontextgen.py +0 -1
  5. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/pydanticgen.py +11 -8
  6. linkml-1.8.3/linkml/generators/python/__init__.py +1 -0
  7. linkml-1.8.3/linkml/generators/python/python_ifabsent_processor.py +92 -0
  8. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pythongen.py +7 -21
  9. linkml-1.8.3/linkml/generators/shacl/__init__.py +1 -0
  10. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/shacl/shacl_data_type.py +1 -1
  11. linkml-1.8.3/linkml/generators/shacl/shacl_ifabsent_processor.py +89 -0
  12. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/shaclgen.py +5 -3
  13. {linkml-1.8.2 → linkml-1.8.3}/pyproject.toml +2 -1
  14. {linkml-1.8.2 → linkml-1.8.3}/setup.py +2 -1
  15. linkml-1.8.2/linkml/generators/shacl/__init__.py +0 -3
  16. linkml-1.8.2/linkml/generators/shacl/ifabsent_processor.py +0 -59
  17. linkml-1.8.2/linkml/utils/ifabsent_functions.py +0 -138
  18. {linkml-1.8.2 → linkml-1.8.3}/LICENSE +0 -0
  19. {linkml-1.8.2 → linkml-1.8.3}/README.md +0 -0
  20. {linkml-1.8.2 → linkml-1.8.3}/linkml/__init__.py +0 -0
  21. {linkml-1.8.2 → linkml-1.8.3}/linkml/_version.py +0 -0
  22. {linkml-1.8.2 → linkml-1.8.3}/linkml/cli/__init__.py +0 -0
  23. {linkml-1.8.2 → linkml-1.8.3}/linkml/cli/__main__.py +0 -0
  24. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/PythonGenNotes.md +0 -0
  25. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/README.md +0 -0
  26. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/__init__.py +0 -0
  27. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/common/__init__.py +0 -0
  28. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/common/build.py +0 -0
  29. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/common/lifecycle.py +0 -0
  30. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/common/template.py +0 -0
  31. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/common/type_designators.py +0 -0
  32. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/csvgen.py +0 -0
  33. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/class.md.jinja2 +0 -0
  34. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/class_diagram.md.jinja2 +0 -0
  35. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/common_metadata.md.jinja2 +0 -0
  36. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/enum.md.jinja2 +0 -0
  37. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/index.md.jinja2 +0 -0
  38. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/index.tex.jinja2 +0 -0
  39. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/schema.md.jinja2 +0 -0
  40. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/slot.md.jinja2 +0 -0
  41. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/subset.md.jinja2 +0 -0
  42. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen/type.md.jinja2 +0 -0
  43. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/docgen.py +0 -0
  44. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/dotgen.py +0 -0
  45. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/erdiagramgen.py +0 -0
  46. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/excelgen.py +0 -0
  47. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/golanggen.py +0 -0
  48. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/golrgen.py +0 -0
  49. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/graphqlgen.py +0 -0
  50. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/javagen/example_template.java.jinja2 +0 -0
  51. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/javagen/java_record_template.jinja2 +0 -0
  52. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/javagen.py +0 -0
  53. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/jsonldgen.py +0 -0
  54. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/jsonschemagen.py +0 -0
  55. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/legacy/__init__.py +0 -0
  56. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/linkmlgen.py +0 -0
  57. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/markdowngen.py +0 -0
  58. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/namespacegen.py +0 -0
  59. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/oocodegen.py +0 -0
  60. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/owlgen.py +0 -0
  61. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/plantumlgen.py +0 -0
  62. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/prefixmapgen.py +0 -0
  63. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/projectgen.py +0 -0
  64. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/protogen.py +0 -0
  65. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/__init__.py +0 -0
  66. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/array.py +0 -0
  67. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/black.py +0 -0
  68. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/build.py +0 -0
  69. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/includes.py +0 -0
  70. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/template.py +0 -0
  71. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/templates/attribute.py.jinja +0 -0
  72. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/templates/base_model.py.jinja +0 -0
  73. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/templates/class.py.jinja +0 -0
  74. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/templates/conditional_import.py.jinja +0 -0
  75. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/templates/enum.py.jinja +0 -0
  76. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/templates/footer.py.jinja +0 -0
  77. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/templates/imports.py.jinja +0 -0
  78. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/templates/module.py.jinja +0 -0
  79. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/pydanticgen/templates/validator.py.jinja +0 -0
  80. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/rdfgen.py +0 -0
  81. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/shexgen.py +0 -0
  82. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/sparqlgen.py +0 -0
  83. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/sqlalchemy/__init__.py +0 -0
  84. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/sqlalchemy/sqlalchemy_declarative_template.py +0 -0
  85. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/sqlalchemy/sqlalchemy_imperative_template.py +0 -0
  86. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/sqlalchemygen.py +0 -0
  87. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/sqltablegen.py +0 -0
  88. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/sssomgen.py +0 -0
  89. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/string_template.md +0 -0
  90. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/summarygen.py +0 -0
  91. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/terminusdbgen.py +0 -0
  92. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/typescriptgen.py +0 -0
  93. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/yamlgen.py +0 -0
  94. {linkml-1.8.2 → linkml-1.8.3}/linkml/generators/yumlgen.py +0 -0
  95. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/__init__.py +0 -0
  96. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/cli.py +0 -0
  97. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/config/datamodel/.linkmllint.yaml +0 -0
  98. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/config/datamodel/__init__.py +0 -0
  99. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/config/datamodel/config.py +0 -0
  100. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/config/datamodel/config.yaml +0 -0
  101. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/config/default.yaml +0 -0
  102. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/config/recommended.yaml +0 -0
  103. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/formatters/__init__.py +0 -0
  104. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/formatters/formatter.py +0 -0
  105. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/formatters/json_formatter.py +0 -0
  106. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/formatters/markdown_formatter.py +0 -0
  107. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/formatters/terminal_formatter.py +0 -0
  108. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/formatters/tsv_formatter.py +0 -0
  109. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/linter.py +0 -0
  110. {linkml-1.8.2 → linkml-1.8.3}/linkml/linter/rules.py +0 -0
  111. {linkml-1.8.2 → linkml-1.8.3}/linkml/reporting/__init__.py +0 -0
  112. {linkml-1.8.2 → linkml-1.8.3}/linkml/reporting/model.py +0 -0
  113. {linkml-1.8.2 → linkml-1.8.3}/linkml/transformers/__init__.py +0 -0
  114. {linkml-1.8.2 → linkml-1.8.3}/linkml/transformers/logical_model_transformer.py +0 -0
  115. {linkml-1.8.2 → linkml-1.8.3}/linkml/transformers/model_transformer.py +0 -0
  116. {linkml-1.8.2 → linkml-1.8.3}/linkml/transformers/relmodel_transformer.py +0 -0
  117. {linkml-1.8.2 → linkml-1.8.3}/linkml/transformers/schema_renamer.py +0 -0
  118. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/__init__.py +0 -0
  119. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/cli_utils.py +0 -0
  120. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/converter.py +0 -0
  121. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/datautils.py +0 -0
  122. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/datavalidator.py +0 -0
  123. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/deprecation.py +0 -0
  124. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/execute_tutorial.py +0 -0
  125. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/generator.py +0 -0
  126. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/helpers.py +0 -0
  127. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/logictools.py +0 -0
  128. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/mergeutils.py +0 -0
  129. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/rawloader.py +0 -0
  130. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/schema_builder.py +0 -0
  131. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/schema_fixer.py +0 -0
  132. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/schemaloader.py +0 -0
  133. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/schemasynopsis.py +0 -0
  134. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/sqlutils.py +0 -0
  135. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/typereferences.py +0 -0
  136. {linkml-1.8.2 → linkml-1.8.3}/linkml/utils/validation.py +0 -0
  137. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/__init__.py +0 -0
  138. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/cli.py +0 -0
  139. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/loaders/__init__.py +0 -0
  140. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/loaders/delimited_file_loader.py +0 -0
  141. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/loaders/json_loader.py +0 -0
  142. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/loaders/loader.py +0 -0
  143. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/loaders/passthrough_loader.py +0 -0
  144. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/loaders/yaml_loader.py +0 -0
  145. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/plugins/__init__.py +0 -0
  146. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/plugins/jsonschema_validation_plugin.py +0 -0
  147. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/plugins/pydantic_validation_plugin.py +0 -0
  148. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/plugins/recommended_slots_plugin.py +0 -0
  149. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/plugins/shacl_validation_plugin.py +0 -0
  150. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/plugins/validation_plugin.py +0 -0
  151. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/report.py +0 -0
  152. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/validation_context.py +0 -0
  153. {linkml-1.8.2 → linkml-1.8.3}/linkml/validator/validator.py +0 -0
  154. {linkml-1.8.2 → linkml-1.8.3}/linkml/validators/__init__.py +0 -0
  155. {linkml-1.8.2 → linkml-1.8.3}/linkml/validators/jsonschemavalidator.py +0 -0
  156. {linkml-1.8.2 → linkml-1.8.3}/linkml/validators/sparqlvalidator.py +0 -0
  157. {linkml-1.8.2 → linkml-1.8.3}/linkml/workspaces/__init__.py +0 -0
  158. {linkml-1.8.2 → linkml-1.8.3}/linkml/workspaces/datamodel/__init__.py +0 -0
  159. {linkml-1.8.2 → linkml-1.8.3}/linkml/workspaces/datamodel/workspaces.py +0 -0
  160. {linkml-1.8.2 → linkml-1.8.3}/linkml/workspaces/datamodel/workspaces.yaml +0 -0
  161. {linkml-1.8.2 → linkml-1.8.3}/linkml/workspaces/example_runner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: linkml
3
- Version: 1.8.2
3
+ Version: 1.8.3
4
4
  Summary: Linked Open Data Modeling Language
5
5
  Home-page: https://linkml.io/linkml/
6
6
  Keywords: schema,linked data,data modeling,rdf,owl,biolink
@@ -6,6 +6,7 @@ Gathers all the other linkml click entrypoints and puts them under ``linkml`` :)
6
6
 
7
7
  import click
8
8
 
9
+ from linkml._version import __version__
9
10
  from linkml.generators.csvgen import cli as gen_csv
10
11
  from linkml.generators.docgen import cli as gen_doc
11
12
  from linkml.generators.dotgen import cli as gen_graphviz
@@ -54,6 +55,7 @@ from linkml.workspaces.example_runner import cli as linkml_run_examples
54
55
 
55
56
 
56
57
  @click.group()
58
+ @click.version_option(__version__, "-V", "--version")
57
59
  def linkml():
58
60
  """
59
61
  LinkML: A flexible linked data modeling language
@@ -61,6 +63,7 @@ def linkml():
61
63
 
62
64
 
63
65
  @linkml.group()
66
+ @click.version_option(__version__, "-V", "--version")
64
67
  def generate():
65
68
  """
66
69
  Generate formats from a LinkML schema
@@ -68,6 +71,7 @@ def generate():
68
71
 
69
72
 
70
73
  @linkml.group()
74
+ @click.version_option(__version__, "-V", "--version")
71
75
  def dev():
72
76
  """
73
77
  Helper tools for linkml development
@@ -0,0 +1,286 @@
1
+ import abc
2
+ import re
3
+ from abc import ABC
4
+ from typing import Any, Optional
5
+
6
+ from linkml_runtime import SchemaView
7
+ from linkml_runtime.linkml_model import (
8
+ Boolean,
9
+ ClassDefinition,
10
+ Date,
11
+ Datetime,
12
+ Decimal,
13
+ Double,
14
+ EnumDefinitionName,
15
+ Float,
16
+ Integer,
17
+ SlotDefinition,
18
+ String,
19
+ Time,
20
+ Uri,
21
+ )
22
+ from linkml_runtime.linkml_model.types import (
23
+ Curie,
24
+ DateOrDatetime,
25
+ Jsonpath,
26
+ Jsonpointer,
27
+ Ncname,
28
+ Nodeidentifier,
29
+ Objectidentifier,
30
+ Sparqlpath,
31
+ Uriorcurie,
32
+ )
33
+
34
+
35
+ class IfAbsentProcessor(ABC):
36
+ """
37
+ Processes value of ifabsent slot.
38
+
39
+ See `<https://w3id.org/linkml/ifabsent>`_.
40
+ """
41
+
42
+ ifabsent_regex = re.compile("""(?:(?P<type>\w+)\()?[\"\']?(?P<default_value>[^\(\)\"\')]*)[\"\']?\)?""")
43
+
44
+ def __init__(self, schema_view: SchemaView):
45
+ self.schema_view = schema_view
46
+
47
+ def process_slot(self, slot: SlotDefinition, cls: ClassDefinition) -> Optional[str]:
48
+ if slot.ifabsent:
49
+ ifabsent_match = self.ifabsent_regex.search(slot.ifabsent)
50
+ ifabsent_default_value = ifabsent_match.group("default_value")
51
+
52
+ return self._map_to_default_value(slot, ifabsent_default_value, cls)
53
+
54
+ return None
55
+
56
+ def _map_to_default_value(
57
+ self, slot: SlotDefinition, ifabsent_default_value: Any, cls: ClassDefinition
58
+ ) -> Optional[str]:
59
+ # Used to manage specific cases that aren't generic
60
+ mapped, custom_default_value = self.map_custom_default_values(ifabsent_default_value, slot, cls)
61
+ if mapped:
62
+ return custom_default_value
63
+
64
+ if slot.range == String.type_name:
65
+ return self.map_string_default_value(ifabsent_default_value, slot, cls)
66
+
67
+ if slot.range == Boolean.type_name:
68
+ if re.match(r"^[Tt]rue$", ifabsent_default_value):
69
+ return self.map_boolean_true_default_value(slot, cls)
70
+ elif re.match(r"^[Ff]alse$", ifabsent_default_value):
71
+ return self.map_boolean_false_default_value(slot, cls)
72
+ else:
73
+ raise ValueError(
74
+ f"The ifabsent value `{slot.ifabsent}` of the `{slot.name}` slot does not match a valid boolean "
75
+ f"value"
76
+ )
77
+
78
+ if slot.range == Integer.type_name:
79
+ return self.map_integer_default_value(ifabsent_default_value, slot, cls)
80
+
81
+ if slot.range == Float.type_name:
82
+ return self.map_float_default_value(ifabsent_default_value, slot, cls)
83
+
84
+ if slot.range == Double.type_name:
85
+ return self.map_double_default_value(ifabsent_default_value, slot, cls)
86
+
87
+ if slot.range == Decimal.type_name:
88
+ return self.map_decimal_default_value(ifabsent_default_value, slot, cls)
89
+
90
+ if slot.range == Time.type_name:
91
+ match = re.match(r"^(\d{2}):(\d{2}):(\d{2}).*$", ifabsent_default_value)
92
+ if match:
93
+ return self.map_time_default_value(match[1], match[2], match[3], slot, cls)
94
+ else:
95
+ raise ValueError(
96
+ f"The ifabsent value `{slot.ifabsent}` of the `{slot.name}` slot does not match a valid time value"
97
+ )
98
+
99
+ # TODO manage timezones and offsets
100
+ if slot.range == Date.type_name:
101
+ match = re.match(r"^(\d{4})-(\d{2})-(\d{2})$", ifabsent_default_value)
102
+ if match:
103
+ return self.map_date_default_value(match[1], match[2], match[3], slot, cls)
104
+ else:
105
+ raise ValueError(
106
+ f"The ifabsent value `{slot.ifabsent}` of the `{slot.name}` slot does not match a valid date value"
107
+ )
108
+
109
+ # TODO manage timezones and offsets
110
+ if slot.range == Datetime.type_name:
111
+ match = re.match(r"^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).*$", ifabsent_default_value)
112
+ if match:
113
+ return self.map_datetime_default_value(
114
+ match[1], match[2], match[3], match[4], match[5], match[6], slot, cls
115
+ )
116
+ else:
117
+ raise ValueError(
118
+ f"The ifabsent value `{slot.ifabsent}` of the `{slot.name}` slot does not match a valid datetime "
119
+ f"value"
120
+ )
121
+
122
+ # TODO manage timezones and offsets
123
+ if slot.range == DateOrDatetime.type_name:
124
+ match = re.match(r"^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2}))?.*$", ifabsent_default_value)
125
+ if match and (match[4] is None or match[5] is None or match[6] is None):
126
+ return self.map_date_default_value(match[1], match[2], match[3], slot, cls)
127
+ elif match:
128
+ return self.map_datetime_default_value(
129
+ match[1], match[2], match[3], match[4], match[5], match[6], slot, cls
130
+ )
131
+ else:
132
+ raise ValueError(
133
+ f"The ifabsent value `{slot.ifabsent}` of the `{slot.name}` slot does not match a valid date or "
134
+ f"datetime value"
135
+ )
136
+
137
+ if slot.range == Uri.type_name:
138
+ return self.map_uri_default_value(ifabsent_default_value, slot, cls)
139
+
140
+ if slot.range == Curie.type_name:
141
+ return self.map_curie_default_value(ifabsent_default_value, slot, cls)
142
+
143
+ if slot.range == Uriorcurie.type_name:
144
+ return self.map_uri_or_curie_default_value(ifabsent_default_value, slot, cls)
145
+
146
+ if slot.range == Ncname.type_name:
147
+ return self.map_nc_name_default_value(ifabsent_default_value, slot, cls)
148
+
149
+ if slot.range == Objectidentifier.type_name:
150
+ return self.map_object_identifier_default_value(ifabsent_default_value, slot, cls)
151
+
152
+ if slot.range == Nodeidentifier.type_name:
153
+ return self.map_node_identifier_default_value(ifabsent_default_value, slot, cls)
154
+
155
+ if slot.range == Jsonpointer.type_name:
156
+ return self.map_json_pointer_default_value(ifabsent_default_value, slot, cls)
157
+
158
+ if slot.range == Jsonpath.type_name:
159
+ return self.map_json_path_default_value(ifabsent_default_value, slot, cls)
160
+
161
+ if slot.range == Sparqlpath.type_name:
162
+ return self.map_sparql_path_default_value(ifabsent_default_value, slot, cls)
163
+
164
+ # -----------------------
165
+ # Enum slot ranges
166
+ # -----------------------
167
+
168
+ for enum_name, enum in self.schema_view.all_enums().items():
169
+ if enum_name == slot.range:
170
+ for permissible_value_name, permissible_value in enum.permissible_values.items():
171
+ if permissible_value_name == ifabsent_default_value:
172
+ return self.map_enum_default_value(enum_name, permissible_value_name, slot, cls)
173
+
174
+ raise ValueError(f"The ifabsent value `{slot.ifabsent}` of the `{slot.name}` slot could not be processed")
175
+
176
+ @abc.abstractmethod
177
+ def map_custom_default_values(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition) -> (bool, str):
178
+ """
179
+ Maps custom default values that aren't generic behaviours.
180
+
181
+ @param default_value: the default value extracted from the ifabsent attribute
182
+ @param slot: the definition of the slot
183
+ @param cls: the definition of the class
184
+ @return: a boolean that indicates if the value has been mapped followed by the mapped value
185
+ """
186
+ return False, None
187
+
188
+ @abc.abstractmethod
189
+ def map_string_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
190
+ raise NotImplementedError()
191
+
192
+ @abc.abstractmethod
193
+ def map_integer_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
194
+ raise NotImplementedError()
195
+
196
+ @abc.abstractmethod
197
+ def map_boolean_true_default_value(self, slot: SlotDefinition, cls: ClassDefinition):
198
+ raise NotImplementedError()
199
+
200
+ @abc.abstractmethod
201
+ def map_boolean_false_default_value(self, slot: SlotDefinition, cls: ClassDefinition):
202
+ raise NotImplementedError()
203
+
204
+ @abc.abstractmethod
205
+ def map_float_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
206
+ raise NotImplementedError()
207
+
208
+ @abc.abstractmethod
209
+ def map_double_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
210
+ raise NotImplementedError()
211
+
212
+ @abc.abstractmethod
213
+ def map_decimal_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
214
+ raise NotImplementedError()
215
+
216
+ @abc.abstractmethod
217
+ def map_time_default_value(self, hour: str, minutes: str, seconds: str, slot: SlotDefinition, cls: ClassDefinition):
218
+ raise NotImplementedError()
219
+
220
+ @abc.abstractmethod
221
+ def map_date_default_value(self, year: str, month: str, day: str, slot: SlotDefinition, cls: ClassDefinition):
222
+ raise NotImplementedError()
223
+
224
+ @abc.abstractmethod
225
+ def map_datetime_default_value(
226
+ self,
227
+ year: str,
228
+ month: str,
229
+ day: str,
230
+ hour: str,
231
+ minutes: str,
232
+ seconds: str,
233
+ slot: SlotDefinition,
234
+ cls: ClassDefinition,
235
+ ):
236
+ raise NotImplementedError()
237
+
238
+ @abc.abstractmethod
239
+ def map_uri_or_curie_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
240
+ raise NotImplementedError()
241
+
242
+ @abc.abstractmethod
243
+ def map_curie_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
244
+ raise NotImplementedError()
245
+
246
+ @abc.abstractmethod
247
+ def map_uri_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
248
+ raise NotImplementedError()
249
+
250
+ @abc.abstractmethod
251
+ def map_nc_name_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
252
+ raise NotImplementedError()
253
+
254
+ @abc.abstractmethod
255
+ def map_object_identifier_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
256
+ raise NotImplementedError()
257
+
258
+ @abc.abstractmethod
259
+ def map_node_identifier_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
260
+ raise NotImplementedError()
261
+
262
+ @abc.abstractmethod
263
+ def map_json_pointer_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
264
+ raise NotImplementedError()
265
+
266
+ @abc.abstractmethod
267
+ def map_json_path_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
268
+ raise NotImplementedError()
269
+
270
+ @abc.abstractmethod
271
+ def map_sparql_path_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
272
+ raise NotImplementedError()
273
+
274
+ @abc.abstractmethod
275
+ def map_enum_default_value(
276
+ self, enum_name: EnumDefinitionName, permissible_value_name: str, slot: SlotDefinition, cls: ClassDefinition
277
+ ):
278
+ raise NotImplementedError()
279
+
280
+ def _uri_for(self, s: str) -> str:
281
+ uri = str(self.schema_view.namespaces().uri_for(s))
282
+ return self.schema_view.namespaces().curie_for(uri, True, True) or self._strval(uri)
283
+
284
+ def _strval(self, txt: str) -> str:
285
+ txt = str(txt).replace('"', '\\"')
286
+ return f'"{txt}"'
@@ -21,7 +21,6 @@ from linkml.utils.generator import Generator, shared_arguments
21
21
  URI_RANGES = (SHEX.nonliteral, SHEX.bnode, SHEX.iri)
22
22
 
23
23
  ENUM_CONTEXT = {
24
- "@vocab": "@null",
25
24
  "text": "skos:notation",
26
25
  "description": "skos:prefLabel",
27
26
  "meaning": "@id",
@@ -41,9 +41,9 @@ from linkml.generators.pydanticgen.template import (
41
41
  PydanticModule,
42
42
  PydanticTemplateModel,
43
43
  )
44
+ from linkml.generators.python.python_ifabsent_processor import PythonIfAbsentProcessor
44
45
  from linkml.utils import deprecation_warning
45
46
  from linkml.utils.generator import shared_arguments
46
- from linkml.utils.ifabsent_functions import ifabsent_value_declaration
47
47
 
48
48
  if int(PYDANTIC_VERSION[0]) == 1:
49
49
  deprecation_warning("pydantic-v1")
@@ -67,7 +67,9 @@ def _get_pyrange(t: TypeDefinition, sv: SchemaView) -> str:
67
67
  DEFAULT_IMPORTS = (
68
68
  Imports()
69
69
  + Import(module="__future__", objects=[ObjectImport(name="annotations")])
70
- + Import(module="datetime", objects=[ObjectImport(name="datetime"), ObjectImport(name="date")])
70
+ + Import(
71
+ module="datetime", objects=[ObjectImport(name="datetime"), ObjectImport(name="date"), ObjectImport(name="time")]
72
+ )
71
73
  + Import(module="decimal", objects=[ObjectImport(name="Decimal")])
72
74
  + Import(module="enum", objects=[ObjectImport(name="Enum")])
73
75
  + Import(module="re")
@@ -526,6 +528,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
526
528
  """
527
529
  if self._predefined_slot_values is None:
528
530
  sv = self.schemaview
531
+ ifabsent_processor = PythonIfAbsentProcessor(sv)
529
532
  slot_values = defaultdict(dict)
530
533
  for class_def in sv.all_classes().values():
531
534
  for slot_name in sv.class_slots(class_def.name):
@@ -541,10 +544,10 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
541
544
  slot.name
542
545
  ]
543
546
  elif slot.ifabsent is not None:
544
- value = ifabsent_value_declaration(slot.ifabsent, sv, class_def, slot)
547
+ value = ifabsent_processor.process_slot(slot, class_def)
545
548
  slot_values[camelcase(class_def.name)][slot.name] = value
546
549
 
547
- self._predefined_slot_values = slot_values
550
+ self._predefined_slot_values = slot_values
548
551
 
549
552
  return self._predefined_slot_values
550
553
 
@@ -1061,7 +1064,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
1061
1064
  # interpret all imported schema paths as relative to that
1062
1065
  output_path.parent.mkdir(parents=True, exist_ok=True)
1063
1066
  serialized = generator.serialize(rendered_module=rendered)
1064
- with open(output_path, "w") as ofile:
1067
+ with open(output_path, "w", encoding="utf-8") as ofile:
1065
1068
  ofile.write(serialized)
1066
1069
 
1067
1070
  results.append(
@@ -1080,7 +1083,7 @@ class PydanticGenerator(OOCodeGenerator, LifecycleMixin):
1080
1083
  rel_path = _import_to_path(generated_import.module)
1081
1084
  abs_path = (output_path.parent / rel_path).resolve()
1082
1085
  abs_path.parent.mkdir(parents=True, exist_ok=True)
1083
- with open(abs_path, "w") as ofile:
1086
+ with open(abs_path, "w", encoding="utf-8") as ofile:
1084
1087
  ofile.write(serialized)
1085
1088
 
1086
1089
  results.append(
@@ -1123,7 +1126,7 @@ def _ensure_inits(paths: List[Path]):
1123
1126
  common_path = Path(os.path.commonpath(paths))
1124
1127
 
1125
1128
  if not (ipath := (common_path / "__init__.py")).exists():
1126
- with open(ipath, "w") as ifile:
1129
+ with open(ipath, "w", encoding="utf-8") as ifile:
1127
1130
  ifile.write(" \n")
1128
1131
 
1129
1132
  for path in paths:
@@ -1131,7 +1134,7 @@ def _ensure_inits(paths: List[Path]):
1131
1134
  path = path.parent
1132
1135
  while path != common_path:
1133
1136
  if not (ipath := (path / "__init__.py")).exists():
1134
- with open(ipath, "w") as ifile:
1137
+ with open(ipath, "w", encoding="utf-8") as ifile:
1135
1138
  ifile.write(" \n")
1136
1139
  path = path.parent
1137
1140
 
@@ -0,0 +1 @@
1
+ __all__ = ["python_ifabsent_processor"]
@@ -0,0 +1,92 @@
1
+ from linkml_runtime.linkml_model import (
2
+ ClassDefinition,
3
+ EnumDefinitionName,
4
+ SlotDefinition,
5
+ )
6
+
7
+ from linkml.generators.common.ifabsent_processor import IfAbsentProcessor
8
+
9
+
10
+ class PythonIfAbsentProcessor(IfAbsentProcessor):
11
+ UNIMPLEMENTED_DEFAULT_VALUES = ["class_curie", "class_uri", "slot_uri", "slot_curie", "default_range", "default_ns"]
12
+
13
+ def map_custom_default_values(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition) -> (bool, str):
14
+ if default_value in self.UNIMPLEMENTED_DEFAULT_VALUES:
15
+ return True, None
16
+
17
+ if default_value == "bnode":
18
+ return True, "bnode()"
19
+
20
+ return False, None
21
+
22
+ def map_string_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
23
+ return self._strval(default_value)
24
+
25
+ def map_boolean_true_default_value(self, slot: SlotDefinition, cls: ClassDefinition):
26
+ return "True"
27
+
28
+ def map_boolean_false_default_value(self, slot: SlotDefinition, cls: ClassDefinition):
29
+ return "False"
30
+
31
+ def map_integer_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
32
+ return default_value
33
+
34
+ def map_float_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
35
+ return default_value
36
+
37
+ def map_double_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
38
+ return default_value
39
+
40
+ def map_decimal_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
41
+ return default_value
42
+
43
+ def map_time_default_value(self, hour: str, minutes: str, seconds: str, slot: SlotDefinition, cls: ClassDefinition):
44
+ return f"time({int(hour)}, {int(minutes)}, {int(seconds)})"
45
+
46
+ def map_date_default_value(self, year: str, month: str, day: str, slot: SlotDefinition, cls: ClassDefinition):
47
+ return f"date({int(year)}, {int(month)}, {int(day)})"
48
+
49
+ def map_datetime_default_value(
50
+ self,
51
+ year: str,
52
+ month: str,
53
+ day: str,
54
+ hour: str,
55
+ minutes: str,
56
+ seconds: str,
57
+ slot: SlotDefinition,
58
+ cls: ClassDefinition,
59
+ ):
60
+ return f"datetime({int(year)}, {int(month)}, {int(day)}, " f"{int(hour)}, {int(minutes)}, {int(seconds)})"
61
+
62
+ def map_uri_or_curie_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
63
+ return self._uri_for(default_value)
64
+
65
+ def map_curie_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
66
+ return self._uri_for(default_value)
67
+
68
+ def map_uri_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
69
+ return self._uri_for(default_value)
70
+
71
+ def map_enum_default_value(
72
+ self, enum_name: EnumDefinitionName, permissible_value_name: str, slot: SlotDefinition, cls: ClassDefinition
73
+ ):
74
+ return f"{enum_name}.{permissible_value_name}"
75
+
76
+ def map_nc_name_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
77
+ raise NotImplementedError()
78
+
79
+ def map_object_identifier_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
80
+ raise NotImplementedError()
81
+
82
+ def map_node_identifier_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
83
+ raise NotImplementedError()
84
+
85
+ def map_json_pointer_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
86
+ raise NotImplementedError()
87
+
88
+ def map_json_path_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
89
+ raise NotImplementedError()
90
+
91
+ def map_sparql_path_default_value(self, default_value: str, slot: SlotDefinition, cls: ClassDefinition):
92
+ raise NotImplementedError()
@@ -29,8 +29,8 @@ from rdflib import URIRef
29
29
 
30
30
  import linkml
31
31
  from linkml._version import __version__
32
+ from linkml.generators.python.python_ifabsent_processor import PythonIfAbsentProcessor
32
33
  from linkml.utils.generator import Generator, shared_arguments
33
- from linkml.utils.ifabsent_functions import ifabsent_postinit_declaration, ifabsent_value_declaration
34
34
 
35
35
 
36
36
  @dataclass
@@ -70,6 +70,7 @@ class PythonGenerator(Generator):
70
70
  self.schema = str(self.schema)
71
71
  self.sourcefile = self.schema
72
72
  self.schemaview = SchemaView(self.schema, base_dir=self.base_dir)
73
+ self.ifabsent_processor = PythonIfAbsentProcessor(self.schemaview)
73
74
  super().__post_init__()
74
75
  if self.format is None:
75
76
  self.format = self.valid_formats[0]
@@ -153,7 +154,7 @@ import re
153
154
  from jsonasobj2 import JsonObj, as_dict
154
155
  from typing import Optional, List, Union, Dict, ClassVar, Any
155
156
  from dataclasses import dataclass
156
- from datetime import date, datetime
157
+ from datetime import date, datetime, time
157
158
  {enumimports}
158
159
  from linkml_runtime.utils.slot import Slot
159
160
  from linkml_runtime.utils.metamodelcore import empty_list, empty_dict, bnode
@@ -515,7 +516,7 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
515
516
 
516
517
  return "\n\t".join(initializers)
517
518
 
518
- def gen_class_variable(self, cls: ClassDefinition, slot: SlotDefinition, can_be_positional: bool) -> str:
519
+ def gen_class_variable(self, cls: ClassDefinition, slot: SlotDefinition, can_be_positional: bool = False) -> str:
519
520
  """
520
521
  Generate a class variable declaration for the supplied slot. Note: the can_be_positional attribute works,
521
522
  but it makes tag/value lists unduly complex, as you can't load them with tag=..., value=... -- you HAVE
@@ -527,12 +528,9 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
527
528
  :param can_be_positional: True means that positional parameters are allowed.
528
529
  :return: Initializer string
529
530
  """
530
- can_be_positional = False # Force everything to be tag values
531
531
  slotname = self.slot_name(slot.name)
532
532
  slot_range, default_val = self.range_cardinality(slot, cls, can_be_positional)
533
- ifabsent_text = (
534
- ifabsent_value_declaration(slot.ifabsent, self, cls, slot) if slot.ifabsent is not None else None
535
- )
533
+ ifabsent_text = self.ifabsent_processor.process_slot(slot, cls) if slot.ifabsent is not None else None
536
534
  if ifabsent_text is not None:
537
535
  default = f"= {ifabsent_text}"
538
536
  else:
@@ -637,15 +635,6 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
637
635
 
638
636
  def gen_postinits(self, cls: ClassDefinition) -> str:
639
637
  """Generate all the typing and existence checks post initialize"""
640
- post_inits_pre_super = []
641
- for slot in self.domain_slots(cls):
642
- if slot.ifabsent:
643
- dflt = ifabsent_postinit_declaration(slot.ifabsent, self, cls, slot)
644
-
645
- if dflt and dflt != "None":
646
- post_inits_pre_super.append(f"if self.{self.slot_name(slot.name)} is None:")
647
- post_inits_pre_super.append(f"\tself.{self.slot_name(slot.name)} = {dflt}")
648
-
649
638
  post_inits = []
650
639
  if not (cls.mixin or cls.abstract):
651
640
  pkeys = self.primary_keys_for(cls)
@@ -676,20 +665,17 @@ dataclasses._init_fn = dataclasses_init_fn_with_kwargs
676
665
  if slot.name not in domain_slot_names and slot.designates_type:
677
666
  post_inits_designators.append(self.gen_postinit(cls, slot))
678
667
 
679
- post_inits_pre_super_line = "\n\t\t".join([p for p in post_inits_pre_super if p]) + (
680
- "\n\t\t" if post_inits_pre_super else ""
681
- )
682
668
  post_inits_post_super_line = "\n\t\t".join(post_inits_designators)
683
669
  post_inits_line = "\n\t\t".join([p for p in post_inits if p])
684
670
  return (
685
671
  (
686
672
  f"""
687
673
  def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
688
- {post_inits_pre_super_line}{post_inits_line}
674
+ {post_inits_line}
689
675
  super().__post_init__(**kwargs)
690
676
  {post_inits_post_super_line}"""
691
677
  )
692
- if post_inits_line or post_inits_pre_super_line or post_inits_post_super_line
678
+ if post_inits_line or post_inits_post_super_line
693
679
  else ""
694
680
  )
695
681
 
@@ -0,0 +1 @@
1
+ __all__ = ["shacl_data_type", "shacl_ifabsent_processor"]
@@ -20,7 +20,7 @@ class ShaclDataType(DataType, Enum):
20
20
  FLOAT = ("float", URIRef("http://www.w3.org/2001/XMLSchema#float"))
21
21
  DOUBLE = ("double", URIRef("http://www.w3.org/2001/XMLSchema#double"))
22
22
  URI = ("uri", URIRef("http://www.w3.org/2001/XMLSchema#anyURI"))
23
- CURI = ("curi", URIRef("http://www.w3.org/2001/XMLSchema#string"))
23
+ CURIE = ("curi", URIRef("http://www.w3.org/2001/XMLSchema#string"))
24
24
  NCNAME = ("ncname", URIRef("http://www.w3.org/2001/XMLSchema#string"))
25
25
  OBJECT_IDENTIFIER = ("objectidentifier", URIRef("http://www.w3.org/ns/shex#iri"))
26
26
  NODE_IDENTIFIER = ("nodeidentifier", URIRef("http://www.w3.org/ns/shex#nonLiteral"))