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 MetadataMode for 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]
black: bool = False

If black is present in the environment, format the serialized code with it

inlined: bool = True
emit_metadata: bool = True
gen_classvars: bool = True
gen_slots: bool = True
skip_meta: ClassVar[Tuple[str]] = ('domain_of', 'alias')
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 named annotation, wrap it in Named

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 VectorData or VectorIndex, 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

wrap_preserving_optional(annotation: str, wrap: str) str

Add a wrapping type to a type annotation string, preserving any Optional[] annotation, bumping it to the outside

Examples

>>> wrap_preserving_optional('Optional[list[str]]', 'NewType')
'Optional[NewType[list[str]]]'