"""Extension of nptyping NDArray for pydantic that allows for JSON-Schema serialization.. note:: This module should *only* have the :class:`.NDArray` class in it, because the type stub ``ndarray.pyi`` is only created for :class:`.NDArray` . Otherwise, type checkers will complain about using any helper functions elsewhere - those all belong in :mod:`numpydantic.schema` . Keeping with nptyping's style, NDArrayMeta is in this module even if it's excluded from the type stub."""fromtypingimportAny,Tupleimportnumpyasnpfromnptyping.errorimportInvalidArgumentsErrorfromnptyping.ndarrayimportNDArrayMetaas_NDArrayMetafromnptyping.nptyping_typeimportNPTypingTypefromnptyping.structureimportStructurefromnptyping.structure_expressionimportcheck_type_namesfromnptyping.typing_import(dtype_per_name,)frompydanticimportGetJsonSchemaHandlerfrompydantic_coreimportcore_schemafromnumpydantic.dtypeimportDTypefromnumpydantic.mapsimportpython_to_nptypingfromnumpydantic.schemaimport(_handler_type,_jsonize_array,get_validate_interface,make_json_schema,)fromnumpydantic.typesimportDtypeType,ShapeType
[docs]classNDArrayMeta(_NDArrayMeta,implementation="NDArray"):""" Hooking into nptyping's array metaclass to override methods pending completion of the transition away from nptyping """def_get_dtype(cls,dtype_candidate:Any)->DType:""" Override of base _get_dtype method to allow for compound tuple types """ifdtype_candidateinpython_to_nptyping:dtype_candidate=python_to_nptyping[dtype_candidate]is_dtype=isinstance(dtype_candidate,type)andissubclass(dtype_candidate,np.generic)ifdtype_candidateisAny:dtype=Anyelifis_dtype:dtype=dtype_candidateelifissubclass(dtype_candidate,Structure):# pragma: no coverdtype=dtype_candidatecheck_type_names(dtype,dtype_per_name)elifcls._is_literal_like(dtype_candidate):# pragma: no coverstructure_expression=dtype_candidate.__args__[0]dtype=Structure[structure_expression]check_type_names(dtype,dtype_per_name)elifisinstance(dtype_candidate,tuple):# pragma: no coverdtype=tuple([cls._get_dtype(dt)fordtindtype_candidate])else:# pragma: no coverraiseInvalidArgumentsError(f"Unexpected argument '{dtype_candidate}', expecting"" Structure[<StructureExpression>]"" or Literal[<StructureExpression>]"" or a dtype"" or typing.Any.")returndtype
[docs]classNDArray(NPTypingType,metaclass=NDArrayMeta):""" Constrained array type allowing npytyping syntax for dtype and shape validation and serialization. This class is not intended to be instantiated or used for type checking, it implements the ``__get_pydantic_core_schema__` method to invoke the relevant :ref:`interface <Interfaces>` for validation and serialization. References: - https://docs.pydantic.dev/latest/usage/types/custom/#handling-third-party-types """__args__:Tuple[ShapeType,DtypeType]=(Any,Any)@classmethoddef__get_pydantic_core_schema__(cls,_source_type:"NDArray",_handler:_handler_type,)->core_schema.CoreSchema:shape,dtype=_source_type.__args__shape:ShapeTypedtype:DtypeType# get pydantic core schema as a list of lists for JSON schemalist_schema=make_json_schema(shape,dtype,_handler)returncore_schema.json_or_python_schema(json_schema=list_schema,python_schema=core_schema.with_info_plain_validator_function(get_validate_interface(shape,dtype)),serialization=core_schema.plain_serializer_function_ser_schema(_jsonize_array,when_used="json",info_arg=True),)@classmethoddef__get_pydantic_json_schema__(cls,schema:core_schema.CoreSchema,handler:GetJsonSchemaHandler)->core_schema.JsonSchema:json_schema=handler(schema)json_schema=handler.resolve_ref_schema(json_schema)dtype=cls.__args__[1]ifnotisinstance(dtype,tuple)anddtype.__module__notin("builtins","typing",):json_schema["dtype"]=".".join([dtype.__module__,dtype.__name__])returnjson_schema