88from ._typing import (
99 Data ,
1010 DataLike ,
11+ File ,
12+ FileLike ,
1113 KeywordEntryLike ,
1214 StandaloneData ,
1315 StandaloneDataLike ,
2022
2123@overload
2224def normalize (
23- data : DataLike , * , keywords : tuple [str , ...] | None = None , bool_ok : bool = True
25+ data : FileLike ,
26+ * ,
27+ keywords : tuple [()],
28+ bool_ok : bool = True ,
29+ tuple_is_keyword_entry : bool = False ,
30+ ) -> File : ...
31+
32+
33+ @overload
34+ def normalize (
35+ data : DataLike ,
36+ * ,
37+ keywords : tuple [str , ...] | None = None ,
38+ bool_ok : bool = True ,
39+ tuple_is_keyword_entry : bool = False ,
2440) -> Data : ...
2541
2642
@@ -30,28 +46,50 @@ def normalize(
3046 * ,
3147 keywords : tuple [str , ...] | None = None ,
3248 bool_ok : bool = True ,
49+ tuple_is_keyword_entry : bool = False ,
3350) -> StandaloneData : ...
3451
3552
3653@overload
3754def normalize (
38- data : SubDictLike , * , keywords : tuple [str , ...] | None = None , bool_ok : bool = True
55+ data : SubDictLike ,
56+ * ,
57+ keywords : tuple [str , ...] | None = None ,
58+ bool_ok : bool = True ,
59+ tuple_is_keyword_entry : bool = False ,
3960) -> SubDict : ...
4061
4162
63+ @overload
4264def normalize (
43- data : DataLike | StandaloneDataLike | SubDictLike ,
65+ data : None ,
66+ * ,
67+ keywords : tuple [()],
68+ bool_ok : bool = True ,
69+ tuple_is_keyword_entry : bool = False ,
70+ ) -> None : ...
71+
72+
73+ def normalize (
74+ data : FileLike | DataLike | StandaloneDataLike | SubDictLike | None ,
4475 * ,
4576 keywords : tuple [str , ...] | None = None ,
4677 bool_ok : bool = True ,
47- ) -> Data | StandaloneData | SubDict :
78+ tuple_is_keyword_entry : bool = False ,
79+ ) -> File | Data | StandaloneData | SubDict | None :
4880 if isinstance (data , Mapping ):
49- items = ((normalize (k , bool_ok = False ), normalize (v )) for k , v in data .items ()) # ty: ignore[no-matching-overload]
81+ items = (
82+ (
83+ normalize (k , keywords = keywords , bool_ok = False ), # ty: ignore[no-matching-overload]
84+ normalize (v , keywords = (* keywords , k ) if keywords is not None else None ), # ty: ignore[no-matching-overload]
85+ )
86+ for k , v in data .items ()
87+ )
5088 if keywords is None :
5189 return as_dict_check_unique (items )
5290 ret1 : SubDict = {}
5391 for k , v in items :
54- if k .startswith ("#" ):
92+ if k is not None and k .startswith ("#" ):
5593 if isinstance (v , Mapping ):
5694 msg = f"Directive { k } cannot have a dictionary as value"
5795 raise ValueError (msg )
@@ -103,9 +141,11 @@ def normalize(
103141 if isinstance (data , int ):
104142 return float (data )
105143
106- return normalize (data )
144+ return normalize (data ) # ty: ignore[no-matching-overload]
107145
108146 if isinstance (data , np .ndarray ):
147+ if keywords == (None ,):
148+ return data # ty: ignore[invalid-return-type]
109149 ret2 = data .tolist ()
110150 assert isinstance (ret2 , (int , float , list ))
111151 return ret2
@@ -120,14 +160,18 @@ def normalize(
120160 ):
121161 return DimensionSet (* data ) # ty: ignore[invalid-argument-type]
122162
123- if keywords is None and isinstance (data , tuple ) and len (data ) == 2 :
124- k2 , v2 = data
125- if isinstance (k2 , Mapping ):
126- msg = "Keyword in keyword entry cannot be a dictionary"
127- raise ValueError (msg )
128- k2 = normalize (k2 , bool_ok = False ) # ty: ignore[no-matching-overload]
129- v2 = normalize (v2 ) # ty: ignore[no-matching-overload]
130- return (k2 , v2 )
163+ if isinstance (data , tuple ) and not isinstance (data , DimensionSet ):
164+ if tuple_is_keyword_entry :
165+ k2 , v2 = data
166+ if isinstance (k2 , Mapping ):
167+ msg = "Keyword in keyword entry cannot be a dictionary"
168+ raise ValueError (msg )
169+ k2 = normalize (k2 , keywords = keywords , bool_ok = False ) # ty: ignore[no-matching-overload]
170+ v2 = normalize (
171+ v2 , keywords = (* keywords , k2 ) if keywords is not None else keywords
172+ ) # ty: ignore[no-matching-overload]
173+ return (k2 , v2 )
174+ return tuple (normalize (d , keywords = keywords ) for d in data ) # ty: ignore[no-matching-overload]
131175
132176 if (
133177 is_sequence (data )
@@ -136,9 +180,6 @@ def normalize(
136180 ):
137181 return [normalize (d ) for d in data ] # ty: ignore[not-iterable]
138182
139- if isinstance (data , tuple ) and not isinstance (data , DimensionSet ):
140- return tuple (normalize (d , keywords = keywords ) for d in data ) # ty: ignore[no-matching-overload]
141-
142183 if isinstance (data , str ):
143184 with contextlib .suppress (ValueError , KeyError ):
144185 s = Parsed (data )[()]
@@ -153,34 +194,43 @@ def normalize(
153194 ):
154195 return data
155196
197+ if data is None :
198+ if keywords != ():
199+ msg = "Unexpected None key in subdictionary"
200+ raise TypeError (msg )
201+ return data
202+
156203 msg = f"Unsupported data type: { type (data )} "
157204 raise TypeError (msg )
158205
159206
160207def dumps (
161- data : DataLike | StandaloneDataLike | KeywordEntryLike | SubDictLike ,
208+ data : FileLike | DataLike | StandaloneDataLike | KeywordEntryLike | SubDictLike ,
162209 * ,
163210 keywords : tuple [str , ...] | None = None ,
164211 header : SubDictLike | None = None ,
165212 tuple_is_keyword_entry : bool = False ,
166213) -> bytes :
167- data = normalize (data , keywords = keywords )
214+ data = normalize ( # type: ignore[no-matching-overload]
215+ data , keywords = keywords , tuple_is_keyword_entry = tuple_is_keyword_entry
216+ )
168217
169218 if isinstance (data , Mapping ):
170219 return (
171- b"{"
220+ ( b"{" if keywords != () else b"" )
172221 + b" " .join (
173222 dumps (
174223 (k , v ),
175224 keywords = keywords ,
225+ header = header ,
176226 tuple_is_keyword_entry = True ,
177227 )
178228 for k , v in data .items ()
179229 )
180- + b"}"
230+ + ( b"}" if keywords != () else b"" )
181231 )
182232
183- if keywords == () and isinstance (data , np .ndarray ):
233+ if keywords == (None , ) and isinstance (data , np .ndarray ):
184234 if (header .get ("format" , "" ) if header else "" ) == "binary" :
185235 return dumps (len (data )) + b"(" + data .tobytes () + b")"
186236
@@ -254,18 +304,19 @@ def dumps(
254304 if tuple_is_keyword_entry :
255305 k , v = data
256306 ret = b"\n " if isinstance (k , str ) and k [0 ] == "#" else b""
257- ret += dumps (k )
307+ if k is not None :
308+ ret += dumps (k , keywords = keywords )
258309 val = dumps (
259310 v ,
260- keywords = (* keywords , k )
261- if keywords is not None and isinstance (k , str )
262- else None ,
311+ keywords = (* keywords , k ) if keywords is not None else None ,
312+ header = header ,
263313 )
264- if val :
265- ret += b" " + val
314+ if k is not None and val :
315+ ret += b" "
316+ ret += val
266317 if isinstance (k , str ) and k [0 ] == "#" :
267318 ret += b"\n "
268- elif not isinstance (v , Mapping ):
319+ elif k is not None and not isinstance (v , Mapping ):
269320 ret += b";"
270321 return ret
271322
0 commit comments