11"""Implementation of units."""
22import functools
33from importlib .resources import read_text
4+ import os
45from pathlib import Path
56import re
67from tempfile import TemporaryDirectory
1516from pint .errors import UndefinedUnitError , DefinitionSyntaxError # noqa Import
1617
1718# Store directories so they don't get auto-cleaned until exit
18- _TEMP_DIRECTORIES = []
19+ _TEMP_DIRECTORY = TemporaryDirectory ()
1920
2021
2122def _deploy_default_files () -> str :
2223 """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"
2725 units_path .write_text (read_text ("gemd.units" , "citrine_en.txt" ))
2826
29- constants_path = Path (default_dir .name ) / "constants_en.txt"
27+ constants_path = Path (_TEMP_DIRECTORY .name ) / "constants_en.txt"
3028 constants_path .write_text (read_text ("gemd.units" , "constants_en.txt" ))
3129
3230 return str (units_path )
@@ -166,8 +164,8 @@ def _scaling_store_and_mangle(input_string: str, todo: List[Tuple[str, str, str]
166164
167165 if unit_string is not None :
168166 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 ):~} "
171169 long = stripped .replace (stripped_unit , "_" + long_unit )
172170 short = stripped .replace (stripped_unit , " " + short_unit )
173171 else :
@@ -221,41 +219,10 @@ def convert_units(value: float, starting_unit: str, final_unit: str) -> float:
221219 if starting_unit == final_unit :
222220 return value # skip computation
223221 else :
224- resolved_final_unit = _REGISTRY (final_unit ). u # `to` bypasses preparser
222+ resolved_final_unit = _REGISTRY . parse_units (final_unit ) # `to` bypasses preparser
225223 return _REGISTRY .Quantity (value , starting_unit ).to (resolved_final_unit ).magnitude
226224
227225
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-
259226@register_unit_format ("clean" )
260227def _format_clean (unit , registry , ** options ):
261228 """Formatter that turns scaling-factor-units into numbers again."""
@@ -285,36 +252,6 @@ def _format_clean(unit, registry, **options):
285252
286253
287254@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-
318255def parse_units (units : Union [str , Unit , None ],
319256 * ,
320257 return_unit : bool = False
@@ -337,11 +274,11 @@ def parse_units(units: Union[str, Unit, None],
337274 """
338275 if units is None :
339276 if return_unit :
340- return _REGISTRY ("" ). u
277+ return _REGISTRY . parse_units ("" )
341278 else :
342279 return None
343280 elif isinstance (units , str ):
344- parsed = _parse_units (units )
281+ parsed = _REGISTRY . parse_units (units )
345282 if return_unit :
346283 return parsed
347284 else :
@@ -369,7 +306,46 @@ def get_base_units(units: Union[str, Unit]) -> Tuple[Unit, float, float]:
369306
370307 """
371308 if isinstance (units , str ):
372- units = _REGISTRY (units ). u
309+ units = _REGISTRY . parse_units (units )
373310 ratio , base_unit = _REGISTRY .get_base_units (units )
374311 offset = _REGISTRY .Quantity (0 , units ).to (_REGISTRY .Quantity (0 , base_unit )).magnitude
375312 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