22
33
44# standard library
5- from dataclasses import Field , InitVar , dataclass , field , is_dataclass
6- from typing import Any , List , Type , Union , cast
5+ from dataclasses import Field , dataclass , field , is_dataclass
6+ from typing import Any , List , Optional , Type , Union , cast
77
88
99# dependencies
2222 FieldType ,
2323 get_dims ,
2424 get_dtype ,
25- get_first ,
26- get_repr ,
25+ get_inner ,
26+ unannotate ,
2727)
2828
2929
3030# type hints
31+ DataType = TypedDict ("DataType" , dims = Dims , dtype = Dtype )
3132Reference = Union [xr .DataArray , xr .Dataset , None ]
32- DataTypes = TypedDict ("DataTypes" , dims = Dims , dtype = Dtype )
3333
3434
3535# field models
36- @dataclass
36+ @dataclass ( frozen = True )
3737class Data :
38- """Model for the coord or data fields."""
38+ """Field model for data-related fields."""
3939
4040 name : str
41- type : DataTypes
42- value : Any
41+ """Name of the field."""
4342
44- def __call__ (self , reference : Reference = None ) -> xr .DataArray :
45- """Create a DataArray object from the value and a reference."""
46- return typedarray (
47- self .value ,
48- self .type ["dims" ],
49- self .type ["dtype" ],
50- reference ,
51- )
52-
53- @classmethod
54- def from_field (cls , field : Field [Any ], value : Any ) -> "Data" :
55- """Create a field model from a dataclass field and a value."""
56- return cls (
57- field .name ,
58- {
59- "dims" : get_dims (field .type ),
60- "dtype" : get_dtype (field .type ),
61- },
62- value ,
63- )
64-
65-
66- @dataclass
67- class Dataof :
68- """Model for the coordof or dataof fields."""
69-
70- name : str
71- type : str
7243 value : Any
73- dataclass : InitVar [Type [DataClass ]]
44+ """Value assigned to the field."""
45+
46+ type : DataType
47+ """Type (dims and dtype) of the field."""
7448
75- def __post_init__ ( self , dataclass : Type [DataClass ]) -> None :
76- self . _dataclass = dataclass
49+ factory : Optional [ Type [DataClass ]] = None
50+ """Factory dataclass to create a DataArray object."""
7751
7852 def __call__ (self , reference : Reference = None ) -> xr .DataArray :
7953 """Create a DataArray object from the value and a reference."""
8054 from .dataarray import asdataarray
8155
56+ if self .factory is None :
57+ return typedarray (
58+ self .value ,
59+ self .type ["dims" ],
60+ self .type ["dtype" ],
61+ reference ,
62+ )
63+
8264 if is_dataclass (self .value ):
8365 return asdataarray (self .value , reference )
8466 else :
85- return asdataarray (self ._dataclass (self .value ), reference )
67+ return asdataarray (self .factory (self .value ), reference )
8668
8769 @classmethod
88- def from_field (cls , field : Field [Any ], value : Any ) -> "Dataof " :
70+ def from_field (cls , field : Field [Any ], value : Any , of : bool ) -> "Data " :
8971 """Create a field model from a dataclass field and a value."""
90- dataclass = get_first (field .type )
91- return cls (field .name , get_repr (dataclass ), value , dataclass )
72+ hint = unannotate (field .type )
9273
74+ if of :
75+ dataclass = get_inner (hint , 0 )
76+ data = DataModel .from_dataclass (dataclass ).data [0 ]
77+ return cls (field .name , value , data .type , dataclass )
78+ else :
79+ return cls (
80+ field .name ,
81+ value ,
82+ {"dims" : get_dims (hint ), "dtype" : get_dtype (hint )},
83+ )
9384
94- @dataclass
85+
86+ @dataclass (frozen = True )
9587class General :
96- """Model for the attribute or name fields."""
88+ """Field model for general fields."""
9789
9890 name : str
99- type : str
91+ """Name of the field."""
92+
10093 value : Any
94+ """Value assigned to the field."""
95+
96+ type : str
97+ """Type of the field."""
98+
99+ factory : Optional [Type [Any ]] = None
100+ """Factory function to create an object."""
101101
102102 def __call__ (self ) -> Any :
103- """Just return the value."""
104- return self .value
103+ """Create an object from the value."""
104+ if self .factory is None :
105+ return self .value
106+ else :
107+ return self .factory (self .value )
105108
106109 @classmethod
107110 def from_field (cls , field : Field [Any ], value : Any ) -> "General" :
108111 """Create a field model from a dataclass field and a value."""
109- return cls (field .name , get_repr (field .type ), value )
112+ hint = unannotate (field .type )
113+
114+ try :
115+ return cls (field .name , value , f"{ hint .__module__ } .{ hint .__qualname__ } " )
116+ except AttributeError :
117+ return cls (field .name , value , repr (hint ))
110118
111119
112120# data models
@@ -117,10 +125,10 @@ class DataModel:
117125 attr : List [General ] = field (default_factory = list )
118126 """Model of the attribute fields."""
119127
120- coord : List [Union [ Data , Dataof ] ] = field (default_factory = list )
128+ coord : List [Data ] = field (default_factory = list )
121129 """Model of the coordinate fields."""
122130
123- data : List [Union [ Data , Dataof ] ] = field (default_factory = list )
131+ data : List [Data ] = field (default_factory = list )
124132 """Model of the data fields."""
125133
126134 name : List [General ] = field (default_factory = list )
@@ -138,13 +146,13 @@ def from_dataclass(cls, dataclass: DataClass) -> "DataModel":
138146 if FieldType .ATTR .annotates (field_ .type ):
139147 model .attr .append (General .from_field (field_ , value ))
140148 elif FieldType .COORD .annotates (field_ .type ):
141- model .coord .append (Data .from_field (field_ , value ))
149+ model .coord .append (Data .from_field (field_ , value , False ))
142150 elif FieldType .COORDOF .annotates (field_ .type ):
143- model .coord .append (Dataof .from_field (field_ , value ))
151+ model .coord .append (Data .from_field (field_ , value , True ))
144152 elif FieldType .DATA .annotates (field_ .type ):
145- model .data .append (Data .from_field (field_ , value ))
153+ model .data .append (Data .from_field (field_ , value , False ))
146154 elif FieldType .DATAOF .annotates (field_ .type ):
147- model .data .append (Dataof .from_field (field_ , value ))
155+ model .data .append (Data .from_field (field_ , value , True ))
148156 elif FieldType .NAME .annotates (field_ .type ):
149157 model .name .append (General .from_field (field_ , value ))
150158
0 commit comments