1
1
"""Implementation of units."""
2
2
import functools
3
3
from importlib .resources import read_text
4
+ import os
4
5
from pathlib import Path
5
6
import re
6
7
from tempfile import TemporaryDirectory
15
16
from pint .errors import UndefinedUnitError , DefinitionSyntaxError # noqa Import
16
17
17
18
# Store directories so they don't get auto-cleaned until exit
18
- _TEMP_DIRECTORIES = []
19
+ _TEMP_DIRECTORY = TemporaryDirectory ()
19
20
20
21
21
22
def _deploy_default_files () -> str :
22
23
"""Copy the units & constants file into a temporary directory."""
23
- default_dir = TemporaryDirectory ()
24
- _TEMP_DIRECTORIES .append (default_dir )
25
-
26
- units_path = Path (default_dir .name ) / "citrine_en.txt"
24
+ units_path = Path (_TEMP_DIRECTORY .name ) / "citrine_en.txt"
27
25
units_path .write_text (read_text ("gemd.units" , "citrine_en.txt" ))
28
26
29
- constants_path = Path (default_dir .name ) / "constants_en.txt"
27
+ constants_path = Path (_TEMP_DIRECTORY .name ) / "constants_en.txt"
30
28
constants_path .write_text (read_text ("gemd.units" , "constants_en.txt" ))
31
29
32
30
return str (units_path )
@@ -166,8 +164,8 @@ def _scaling_store_and_mangle(input_string: str, todo: List[Tuple[str, str, str]
166
164
167
165
if unit_string is not None :
168
166
stripped_unit = re .sub (r"[+\s]+" , "" , unit_string ).replace ("--" , "" )
169
- long_unit = f"{ _REGISTRY (stripped_unit ). u } "
170
- short_unit = f"{ _REGISTRY (stripped_unit ). u :~} "
167
+ long_unit = f"{ _REGISTRY . parse_units (stripped_unit )} "
168
+ short_unit = f"{ _REGISTRY . parse_units (stripped_unit ):~} "
171
169
long = stripped .replace (stripped_unit , "_" + long_unit )
172
170
short = stripped .replace (stripped_unit , " " + short_unit )
173
171
else :
@@ -221,41 +219,10 @@ def convert_units(value: float, starting_unit: str, final_unit: str) -> float:
221
219
if starting_unit == final_unit :
222
220
return value # skip computation
223
221
else :
224
- resolved_final_unit = _REGISTRY (final_unit ). u # `to` bypasses preparser
222
+ resolved_final_unit = _REGISTRY . parse_units (final_unit ) # `to` bypasses preparser
225
223
return _REGISTRY .Quantity (value , starting_unit ).to (resolved_final_unit ).magnitude
226
224
227
225
228
- def change_definitions_file (filename : str = None ):
229
- """
230
- Change which file is used for units definition.
231
-
232
- Parameters
233
- ----------
234
- filename: str
235
- The file to use
236
-
237
- """
238
- global _REGISTRY
239
- convert_units .cache_clear () # Units will change
240
- if filename is None :
241
- target = DEFAULT_FILE
242
- else :
243
- # TODO: Handle case where user provides a units file but no constants file
244
- target = Path (filename ).expanduser ().resolve (strict = True )
245
-
246
- # TODO: Pint 0.18 doesn't accept paths; must stringify
247
- _REGISTRY = UnitRegistry (filename = str (target ),
248
- preprocessors = [_space_after_minus_preprocessor ,
249
- _scientific_notation_preprocessor ,
250
- _scaling_preprocessor
251
- ],
252
- autoconvert_offset_to_baseunit = True
253
- )
254
-
255
-
256
- change_definitions_file () # initialize to default
257
-
258
-
259
226
@register_unit_format ("clean" )
260
227
def _format_clean (unit , registry , ** options ):
261
228
"""Formatter that turns scaling-factor-units into numbers again."""
@@ -285,36 +252,6 @@ def _format_clean(unit, registry, **options):
285
252
286
253
287
254
@functools .lru_cache (maxsize = 1024 )
288
- def _parse_units (units : str ) -> Unit :
289
- """
290
- Parse a string or Unit into a standard string representation of the unit.
291
-
292
- Parameters
293
- ----------
294
- units: Union[str, Unit, None]
295
- The string or Unit representation of the object we wish to display
296
-
297
- Returns
298
- -------
299
- [Union[str, Unit, None]]
300
- The representation; note that the same type that was passed is returned
301
-
302
- """
303
- # TODO: parse_units has a bug resolved in 0.19, but 3.7 only supports up to 0.18
304
- parsed = _REGISTRY (units )
305
- try :
306
- magnitude = parsed .magnitude
307
- result = parsed .units
308
- except AttributeError : # It was non-dimensional
309
- magnitude = parsed
310
- result = _REGISTRY ("" ).u
311
- if magnitude == 0.0 :
312
- raise ValueError (f"Unit expression had a zero scaling factor. { units } " )
313
- if magnitude != 1 :
314
- raise ValueError (f"Unit expression cannot have a leading scaling factor. { units } " )
315
- return result
316
-
317
-
318
255
def parse_units (units : Union [str , Unit , None ],
319
256
* ,
320
257
return_unit : bool = False
@@ -337,11 +274,11 @@ def parse_units(units: Union[str, Unit, None],
337
274
"""
338
275
if units is None :
339
276
if return_unit :
340
- return _REGISTRY ("" ). u
277
+ return _REGISTRY . parse_units ("" )
341
278
else :
342
279
return None
343
280
elif isinstance (units , str ):
344
- parsed = _parse_units (units )
281
+ parsed = _REGISTRY . parse_units (units )
345
282
if return_unit :
346
283
return parsed
347
284
else :
@@ -369,7 +306,46 @@ def get_base_units(units: Union[str, Unit]) -> Tuple[Unit, float, float]:
369
306
370
307
"""
371
308
if isinstance (units , str ):
372
- units = _REGISTRY (units ). u
309
+ units = _REGISTRY . parse_units (units )
373
310
ratio , base_unit = _REGISTRY .get_base_units (units )
374
311
offset = _REGISTRY .Quantity (0 , units ).to (_REGISTRY .Quantity (0 , base_unit )).magnitude
375
312
return base_unit , float (ratio ), offset
313
+
314
+
315
+ def change_definitions_file (filename : str = None ):
316
+ """
317
+ Change which file is used for units definition.
318
+
319
+ Parameters
320
+ ----------
321
+ filename: str
322
+ The file to use
323
+
324
+ """
325
+ global _REGISTRY
326
+ convert_units .cache_clear () # Units will change
327
+ parse_units .cache_clear ()
328
+ get_base_units .cache_clear ()
329
+ if filename is None :
330
+ target = DEFAULT_FILE
331
+ else :
332
+ # TODO: Handle case where user provides a units file but no constants file
333
+ target = Path (filename ).expanduser ().resolve (strict = True )
334
+
335
+ current_dir = Path .cwd ()
336
+ try :
337
+ path = Path (target )
338
+ os .chdir (path .parent )
339
+ # Need to re-verify path because of some slippiness around tmp on MacOS
340
+ _REGISTRY = UnitRegistry (filename = Path .cwd () / path .name ,
341
+ preprocessors = [_space_after_minus_preprocessor ,
342
+ _scientific_notation_preprocessor ,
343
+ _scaling_preprocessor
344
+ ],
345
+ autoconvert_offset_to_baseunit = True
346
+ )
347
+ finally :
348
+ os .chdir (current_dir )
349
+
350
+
351
+ change_definitions_file () # initialize to default
0 commit comments