20
20
from fractions import Fraction
21
21
from numbers import Number
22
22
from token import NAME , NUMBER
23
+ from tokenize import TokenInfo
24
+
23
25
from typing import (
24
26
TYPE_CHECKING ,
25
27
Any ,
33
35
from ..context import Context
34
36
from ..._typing import Quantity , Unit
35
37
36
- from ..._typing import QuantityOrUnitLike , UnitLike
38
+ from ..._typing import QuantityOrUnitLike , UnitLike , QuantityArgument
37
39
from ..._vendor import appdirs
38
40
from ...compat import HAS_BABEL , babel_parse , tokenizer
39
41
from ...errors import DimensionalityError , RedefinitionError , UndefinedUnitError
75
77
76
78
77
79
@functools .lru_cache
78
- def pattern_to_regex (pattern ):
79
- if hasattr (pattern , "finditer" ):
80
+ def pattern_to_regex (pattern : str | re .Pattern [str ]) -> re .Pattern [str ]:
81
+ # TODO: This has been changed during typing improvements.
82
+ # if hasattr(pattern, "finditer"):
83
+ if not isinstance (pattern , str ):
80
84
pattern = pattern .pattern
81
85
82
86
# Replace "{unit_name}" match string with float regex with unit_name as group
@@ -197,7 +201,15 @@ def __init__(
197
201
mpl_formatter : str = "{:P}" ,
198
202
):
199
203
#: Map a definition class to a adder methods.
200
- self ._adders = {}
204
+ self ._adders : dict [
205
+ type [T ],
206
+ Callable [
207
+ [
208
+ T ,
209
+ ],
210
+ None ,
211
+ ],
212
+ ] = {}
201
213
self ._register_definition_adders ()
202
214
self ._init_dynamic_classes ()
203
215
@@ -297,7 +309,16 @@ def _after_init(self) -> None:
297
309
self ._build_cache (loaded_files )
298
310
self ._initialized = True
299
311
300
- def _register_adder (self , definition_class , adder_func ):
312
+ def _register_adder (
313
+ self ,
314
+ definition_class : type [T ],
315
+ adder_func : Callable [
316
+ [
317
+ T ,
318
+ ],
319
+ None ,
320
+ ],
321
+ ) -> None :
301
322
"""Register a block definition."""
302
323
self ._adders [definition_class ] = adder_func
303
324
@@ -316,18 +337,18 @@ def __deepcopy__(self, memo) -> PlainRegistry:
316
337
new ._init_dynamic_classes ()
317
338
return new
318
339
319
- def __getattr__ (self , item ) :
340
+ def __getattr__ (self , item : str ) -> Unit :
320
341
getattr_maybe_raise (self , item )
321
342
return self .Unit (item )
322
343
323
- def __getitem__ (self , item ):
344
+ def __getitem__ (self , item : str ):
324
345
logger .warning (
325
346
"Calling the getitem method from a UnitRegistry is deprecated. "
326
347
"use `parse_expression` method or use the registry as a callable."
327
348
)
328
349
return self .parse_expression (item )
329
350
330
- def __contains__ (self , item ) -> bool :
351
+ def __contains__ (self , item : str ) -> bool :
331
352
"""Support checking prefixed units with the `in` operator"""
332
353
try :
333
354
self .__getattr__ (item )
@@ -390,7 +411,7 @@ def cache_folder(self) -> pathlib.Path | None:
390
411
def non_int_type (self ):
391
412
return self ._non_int_type
392
413
393
- def define (self , definition ) :
414
+ def define (self , definition : str | type ) -> None :
394
415
"""Add unit to the registry.
395
416
396
417
Parameters
@@ -413,7 +434,7 @@ def define(self, definition):
413
434
# - then we define specific adder for each definition class. :-D
414
435
############
415
436
416
- def _helper_dispatch_adder (self , definition ) :
437
+ def _helper_dispatch_adder (self , definition : Any ) -> None :
417
438
"""Helper function to add a single definition,
418
439
choosing the appropiate method by class.
419
440
"""
@@ -474,19 +495,19 @@ def _add_alias(self, definition: AliasDefinition):
474
495
for alias in definition .aliases :
475
496
self ._helper_single_adder (alias , unit , self ._units , self ._units_casei )
476
497
477
- def _add_dimension (self , definition : DimensionDefinition ):
498
+ def _add_dimension (self , definition : DimensionDefinition ) -> None :
478
499
self ._helper_adder (definition , self ._dimensions , None )
479
500
480
- def _add_derived_dimension (self , definition : DerivedDimensionDefinition ):
501
+ def _add_derived_dimension (self , definition : DerivedDimensionDefinition ) -> None :
481
502
for dim_name in definition .reference .keys ():
482
503
if dim_name not in self ._dimensions :
483
504
self ._add_dimension (DimensionDefinition (dim_name ))
484
505
self ._helper_adder (definition , self ._dimensions , None )
485
506
486
- def _add_prefix (self , definition : PrefixDefinition ):
507
+ def _add_prefix (self , definition : PrefixDefinition ) -> None :
487
508
self ._helper_adder (definition , self ._prefixes , None )
488
509
489
- def _add_unit (self , definition : UnitDefinition ):
510
+ def _add_unit (self , definition : UnitDefinition ) -> None :
490
511
if definition .is_base :
491
512
self ._base_units .append (definition .name )
492
513
for dim_name in definition .reference .keys ():
@@ -673,7 +694,7 @@ def _get_dimensionality_recurse(self, ref, exp, accumulator):
673
694
if reg .reference is not None :
674
695
self ._get_dimensionality_recurse (reg .reference , exp2 , accumulator )
675
696
676
- def _get_dimensionality_ratio (self , unit1 , unit2 ):
697
+ def _get_dimensionality_ratio (self , unit1 : UnitLike , unit2 : UnitLike ):
677
698
"""Get the exponential ratio between two units, i.e. solve unit2 = unit1**x for x.
678
699
679
700
Parameters
@@ -780,7 +801,9 @@ def _get_root_units(self, input_units, check_nonmult=True):
780
801
cache [input_units ] = factor , units
781
802
return factor , units
782
803
783
- def get_base_units (self , input_units , check_nonmult = True , system = None ):
804
+ def get_base_units (
805
+ self , input_units : UnitsContainer | str , check_nonmult : bool = True , system = None
806
+ ):
784
807
"""Convert unit or dict of units to the plain units.
785
808
786
809
If any unit is non multiplicative and check_converter is True,
@@ -1104,7 +1127,32 @@ def _parse_units(
1104
1127
1105
1128
return ret
1106
1129
1107
- def _eval_token (self , token , case_sensitive = None , ** values ):
1130
+ def _eval_token (
1131
+ self ,
1132
+ token : TokenInfo ,
1133
+ case_sensitive : bool | None = None ,
1134
+ ** values : QuantityArgument ,
1135
+ ):
1136
+ """Evaluate a single token using the following rules:
1137
+
1138
+ 1. numerical values as strings are replaced by their numeric counterparts
1139
+ - integers are parsed as integers
1140
+ - other numeric values are parses of non_int_type
1141
+ 2. strings in (inf, infinity, nan, dimensionless) with their numerical value.
1142
+ 3. strings in values.keys() are replaced by Quantity(values[key])
1143
+ 4. in other cases, the values are parsed as units and replaced by their canonical name.
1144
+
1145
+ Parameters
1146
+ ----------
1147
+ token
1148
+ Token to evaluate.
1149
+ case_sensitive, optional
1150
+ If true, a case sensitive matching of the unit name will be done in the registry.
1151
+ If false, a case INsensitive matching of the unit name will be done in the registry.
1152
+ (Default value = None, which uses registry setting)
1153
+ **values
1154
+ Other string that will be parsed using the Quantity constructor on their corresponding value.
1155
+ """
1108
1156
token_type = token [0 ]
1109
1157
token_text = token [1 ]
1110
1158
if token_type == NAME :
@@ -1139,28 +1187,25 @@ def parse_pattern(
1139
1187
1140
1188
Parameters
1141
1189
----------
1142
- input_string :
1190
+ input_string
1143
1191
1144
1192
pattern_string:
1145
- The regex parse string
1146
- case_sensitive :
1147
- (Default value = None, which uses registry setting)
1148
- many :
1193
+ The regex parse string
1194
+ case_sensitive, optional
1195
+ If true, a case sensitive matching of the unit name will be done in the registry.
1196
+ If false, a case INsensitive matching of the unit name will be done in the registry.
1197
+ (Default value = None, which uses registry setting)
1198
+ many, optional
1149
1199
Match many results
1150
1200
(Default value = False)
1151
-
1152
-
1153
- Returns
1154
- -------
1155
-
1156
1201
"""
1157
1202
1158
1203
if not input_string :
1159
1204
return [] if many else None
1160
1205
1161
1206
# Parse string
1162
- pattern = pattern_to_regex (pattern )
1163
- matched = re .finditer (pattern , input_string )
1207
+ regex = pattern_to_regex (pattern )
1208
+ matched = re .finditer (regex , input_string )
1164
1209
1165
1210
# Extract result(s)
1166
1211
results = []
@@ -1196,16 +1241,14 @@ def parse_expression(
1196
1241
1197
1242
Parameters
1198
1243
----------
1199
- input_string :
1200
-
1201
- case_sensitive :
1202
- (Default value = None, which uses registry setting)
1203
- **values :
1204
-
1205
-
1206
- Returns
1207
- -------
1208
-
1244
+ input_string
1245
+
1246
+ case_sensitive, optional
1247
+ If true, a case sensitive matching of the unit name will be done in the registry.
1248
+ If false, a case INsensitive matching of the unit name will be done in the registry.
1249
+ (Default value = None, which uses registry setting)
1250
+ **values
1251
+ Other string that will be parsed using the Quantity constructor on their corresponding value.
1209
1252
"""
1210
1253
if not input_string :
1211
1254
return self .Quantity (1 )
@@ -1215,8 +1258,9 @@ def parse_expression(
1215
1258
input_string = string_preprocessor (input_string )
1216
1259
gen = tokenizer (input_string )
1217
1260
1218
- return build_eval_tree (gen ).evaluate (
1219
- lambda x : self ._eval_token (x , case_sensitive = case_sensitive , ** values )
1220
- )
1261
+ def _define_op (s : str ):
1262
+ return self ._eval_token (s , case_sensitive = case_sensitive , ** values )
1263
+
1264
+ return build_eval_tree (gen ).evaluate (_define_op )
1221
1265
1222
1266
__call__ = parse_expression
0 commit comments