Pydantic¶
Subclass of linkml.generators.PydanticGenerator
customized to support NWB models.
See class and module docstrings for details :)
- class NWBPydanticGenerator(schema: str | ~typing.TextIO | ~linkml_runtime.linkml_model.meta.SchemaDefinition | Generator | ~pathlib.Path, schemaview: ~linkml_runtime.utils.schemaview.SchemaView = None, format: str | None = None, metadata: bool = True, useuris: bool | None = None, log_level: int | None = 30, mergeimports: bool | None = True, source_file_date: str | None = None, source_file_size: int | None = None, logger: ~logging.Logger | None = None, verbose: bool | None = None, output: str | None = None, namespaces: ~linkml_runtime.utils.namespaces.Namespaces | None = None, directory_output: bool = False, base_dir: str = None, metamodel_name_map: ~typing.Dict[str, str] = None, importmap: str | ~typing.Mapping[str, str] | None = None, emit_prefixes: ~typing.Set[str] = <factory>, metamodel: ~linkml.utils.schemaloader.SchemaLoader = None, stacktrace: bool = False, include: str | ~pathlib.Path | ~linkml_runtime.linkml_model.meta.SchemaDefinition | None = None, template_file: str = None, package: str = 'example', array_representations: ~typing.List[~linkml.generators.pydanticgen.array.ArrayRepresentation] = <factory>, black: bool = False, template_dir: str | ~pathlib.Path | None = None, extra_fields: ~typing.Literal['allow', 'forbid', 'ignore'] = 'forbid', gen_mixin_inheritance: bool = True, injected_classes: ~typing.List[str | ~typing.Type] | None = None, injected_fields: ~typing.List[str] = ('hdf5_path: Optional[str] = Field(None, description="The absolute path that this object is stored in an NWB file")', 'object_id: Optional[str] = Field(None, description="Unique UUID for each object")', '\n def __getitem__(self, val: Union[int, slice, str]) -> Any:\n """Try and get a value from value or "data" if we have it"""\n if hasattr(self, "value") and self.value is not None:\n return self.value[val]\n elif hasattr(self, "data") and self.data is not None:\n return self.data[val]\n else:\n raise KeyError("No value or data field to index from")\n', '\n @field_validator("*", mode="wrap")\n @classmethod\n def coerce_value(cls, v: Any, handler, info) -> Any:\n """Try to rescue instantiation by using the value field"""\n try:\n return handler(v)\n except Exception as e1:\n try:\n return handler(v.value)\n except AttributeError:\n try:\n return handler(v["value"])\n except (IndexError, KeyError, TypeError):\n raise e1\n', '\n @field_validator("*", mode="wrap")\n @classmethod\n def cast_with_value(cls, v: Any, handler, info) -> Any:\n """Try to rescue instantiation by casting into the model\'s value field"""\n try:\n return handler(v)\n except Exception as e1:\n try:\n return handler({"value": v})\n except Exception:\n raise e1\n', '\n @field_validator("*", mode="before")\n @classmethod\n def coerce_subclass(cls, v: Any, info) -> Any:\n """Recast parent classes into child classes"""\n if isinstance(v, BaseModel):\n annotation = cls.model_fields[info.field_name].annotation\n while hasattr(annotation, "__args__"):\n annotation = annotation.__args__[0]\n try:\n if issubclass(annotation, type(v)) and annotation is not type(v):\n if v.__pydantic_extra__:\n v = annotation(**{**v.__dict__, **v.__pydantic_extra__})\n else:\n v = annotation(**v.__dict__) \n except TypeError:\n # fine, annotation is a non-class type like a TypeVar\n pass\n return v\n', '\n @model_validator(mode="before")\n @classmethod\n def gather_extra_to_value(cls, v: Any) -> Any:\n """\n For classes that don\'t allow extra fields and have a value slot, \n pack those extra kwargs into ``value``\n """\n if (\n cls.model_config["extra"] == "forbid" \n and "value" in cls.model_fields \n and isinstance(v, dict)\n ):\n extras = {key:val for key, val in v.items() if key not in cls.model_fields}\n if extras:\n for k in extras:\n del v[k]\n if "value" in v:\n v["value"].update(extras)\n else:\n v["value"] = extras\n return v\n'), imports: list[~linkml.generators.pydanticgen.template.Import] = <factory>, sort_imports: bool = True, metadata_mode: ~linkml.generators.pydanticgen.pydanticgen.MetadataMode | str | None = MetadataMode.AUTO, split: bool = True, split_pattern: str = '.{{ schema.name }}', split_context: dict | None = None, split_mode: ~linkml.generators.pydanticgen.pydanticgen.SplitMode = SplitMode.AUTO, gen_classvars: bool = True, gen_slots: bool = True, genmeta: bool = False, emit_metadata: bool = True, _predefined_slot_values: ~typing.Dict[str, ~typing.Dict[str, str]] | None = None, _class_bases: ~typing.Dict[str, ~typing.List[str]] | None = None, schema_map: ~typing.Dict[str, ~linkml_runtime.linkml_model.meta.SchemaDefinition] | None = None, inlined: bool = True, **_kwargs)¶
Subclass of pydantic generator, custom behavior is in overridden lifecycle methods :)
- metadata_mode: MetadataMode | str | None = None¶
How to include schema metadata in generated pydantic models.
See
MetadataModefor mode documentation
- injected_fields: List[str] = ('hdf5_path: Optional[str] = Field(None, description="The absolute path that this object is stored in an NWB file")', 'object_id: Optional[str] = Field(None, description="Unique UUID for each object")', '\n def __getitem__(self, val: Union[int, slice, str]) -> Any:\n """Try and get a value from value or "data" if we have it"""\n if hasattr(self, "value") and self.value is not None:\n return self.value[val]\n elif hasattr(self, "data") and self.data is not None:\n return self.data[val]\n else:\n raise KeyError("No value or data field to index from")\n', '\n @field_validator("*", mode="wrap")\n @classmethod\n def coerce_value(cls, v: Any, handler, info) -> Any:\n """Try to rescue instantiation by using the value field"""\n try:\n return handler(v)\n except Exception as e1:\n try:\n return handler(v.value)\n except AttributeError:\n try:\n return handler(v["value"])\n except (IndexError, KeyError, TypeError):\n raise e1\n', '\n @field_validator("*", mode="wrap")\n @classmethod\n def cast_with_value(cls, v: Any, handler, info) -> Any:\n """Try to rescue instantiation by casting into the model\'s value field"""\n try:\n return handler(v)\n except Exception as e1:\n try:\n return handler({"value": v})\n except Exception:\n raise e1\n', '\n @field_validator("*", mode="before")\n @classmethod\n def coerce_subclass(cls, v: Any, info) -> Any:\n """Recast parent classes into child classes"""\n if isinstance(v, BaseModel):\n annotation = cls.model_fields[info.field_name].annotation\n while hasattr(annotation, "__args__"):\n annotation = annotation.__args__[0]\n try:\n if issubclass(annotation, type(v)) and annotation is not type(v):\n if v.__pydantic_extra__:\n v = annotation(**{**v.__dict__, **v.__pydantic_extra__})\n else:\n v = annotation(**v.__dict__) \n except TypeError:\n # fine, annotation is a non-class type like a TypeVar\n pass\n return v\n', '\n @model_validator(mode="before")\n @classmethod\n def gather_extra_to_value(cls, v: Any) -> Any:\n """\n For classes that don\'t allow extra fields and have a value slot,\n pack those extra kwargs into ``value``\n """\n if (\n cls.model_config["extra"] == "forbid" \n and "value" in cls.model_fields \n and isinstance(v, dict)\n ):\n extras = {key:val for key,val in v.items() if key not in cls.model_fields}\n if extras:\n for k in extras:\n del v[k]\n if "value" in v:\n v["value"].update(extras)\n else:\n v["value"] = extras\n return v\n')¶
A list/tuple of field strings to inject into the base class.
Examples:
injected_fields = ( 'object_id: Optional[str] = Field(None, description="Unique UUID for each object")', )
- split: bool = True¶
Generate schema that import other schema as separate python modules that import from one another, rather than rolling all into a single module (default,
False).
- schema_map: Dict[str, SchemaDefinition] | None = None¶
See
LinkMLProvider.build()for usage - a list of specific versions to import from
- array_representations: List[ArrayRepresentation]¶
- render() PydanticModule¶
Override of super’s render method to switch the split_mode before generation depending on whether it’s a namespace schema or not
- before_generate_slot(slot: SlotDefinition, sv: SchemaView) SlotDefinition¶
Force some properties to be optional
- after_generate_slot(slot: SlotResult, sv: SchemaView) SlotResult¶
strip unwanted metadata
generate range with any_of
- after_generate_class(cls: ClassResult, sv: SchemaView) ClassResult¶
Customize dynamictable behavior
- before_render_template(template: PydanticModule, sv: SchemaView) PydanticModule¶
Remove source file from metadata
put typing_extensions imports at the end
- class AfterGenerateSlot¶
Container class for slot-modification methods
- static skip_meta(slot: SlotResult, skip_meta: tuple[str]) SlotResult¶
Skip additional metadata slots
- static make_array_anyofs(slot: SlotResult) SlotResult¶
Make a Union of array ranges if multiple array types specified in
any_of
- static make_named_class_range(slot: SlotResult) SlotResult¶
When a slot has a
namedannotation, wrap it inNamed
- class AfterGenerateClass¶
Container class for class-modification methods
- static inject_dynamictable(cls: ClassResult) ClassResult¶
Modify dynamictable class bases and inject needed objects :) :param cls:
Returns:
- static wrap_dynamictable_columns(cls: ClassResult, sv: SchemaView) ClassResult¶
Wrap NDArray columns inside of dynamictables with
VectorDataorVectorIndex, which are generic classes whose value slot is parameterized by the NDArray
- static inject_dynamictable_imports(cls: ClassResult, sv: SchemaView, import_method: Callable[[str], Import]) ClassResult¶
Ensure that schema that contain dynamictables have all the imports needed to use them