From bffc5ea929323feccd8667750a71e11b9ff6c85f Mon Sep 17 00:00:00 2001 From: Saelyos Date: Fri, 20 Oct 2023 10:27:14 +0200 Subject: [PATCH 1/3] Add wrapper benchmark --- pint/testsuite/benchmarks/test_20_quantity.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/pint/testsuite/benchmarks/test_20_quantity.py b/pint/testsuite/benchmarks/test_20_quantity.py index 36c0f92ba..1ec7cbb60 100644 --- a/pint/testsuite/benchmarks/test_20_quantity.py +++ b/pint/testsuite/benchmarks/test_20_quantity.py @@ -53,3 +53,39 @@ def test_op2(benchmark, setup, keys, op): _, data = setup key1, key2 = keys benchmark(op, data[key1], data[key2]) + + +@pytest.mark.parametrize("key", ALL_VALUES_Q) +def test_wrapper(benchmark, setup, key): + ureg, data = setup + value, unit = key.split("_") + + @ureg.wraps(None, (unit,)) + def f(a): + pass + + benchmark(f, data[key]) + + +@pytest.mark.parametrize("key", ALL_VALUES_Q) +def test_wrapper_nonstrict(benchmark, setup, key): + ureg, data = setup + value, unit = key.split("_") + + @ureg.wraps(None, (unit,), strict=False) + def f(a): + pass + + benchmark(f, data[value]) + + +@pytest.mark.parametrize("key", ALL_VALUES_Q) +def test_wrapper_ret(benchmark, setup, key): + ureg, data = setup + value, unit = key.split("_") + + @ureg.wraps(unit, (unit,)) + def f(a): + return a + + benchmark(f, data[key]) From 221ecc33ff38aef3a5afee45cd69a09deadc4f81 Mon Sep 17 00:00:00 2001 From: Saelyos Date: Fri, 20 Oct 2023 10:31:57 +0200 Subject: [PATCH 2/3] Improve wrapper performance --- pint/registry_helpers.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pint/registry_helpers.py b/pint/registry_helpers.py index 6b2f0e0b6..ec5c7f1f8 100644 --- a/pint/registry_helpers.py +++ b/pint/registry_helpers.py @@ -134,7 +134,7 @@ def _converter(ureg, values, strict): for ndx in dependent_args_ndx: value = values[ndx] assert _replace_units(args_as_uc[ndx][0], values_by_name) is not None - new_values[ndx] = ureg._convert( + new_values[ndx] = ureg.convert( getattr(value, "_magnitude", value), getattr(value, "_units", UnitsContainer({})), _replace_units(args_as_uc[ndx][0], values_by_name), @@ -143,7 +143,7 @@ def _converter(ureg, values, strict): # third pass: convert other arguments for ndx in unit_args_ndx: if isinstance(values[ndx], ureg.Quantity): - new_values[ndx] = ureg._convert( + new_values[ndx] = ureg.convert( values[ndx]._magnitude, values[ndx]._units, args_as_uc[ndx][0] ) else: @@ -151,7 +151,7 @@ def _converter(ureg, values, strict): if isinstance(values[ndx], str): # if the value is a string, we try to parse it tmp_value = ureg.parse_expression(values[ndx]) - new_values[ndx] = ureg._convert( + new_values[ndx] = ureg.convert( tmp_value._magnitude, tmp_value._units, args_as_uc[ndx][0] ) else: @@ -168,14 +168,13 @@ def _converter(ureg, values, strict): return _converter -def _apply_defaults(func, args, kwargs): +def _apply_defaults(sig, args, kwargs): """Apply default keyword arguments. Named keywords may have been left blank. This function applies the default values so that every argument is defined. """ - sig = signature(func) bound_arguments = sig.bind(*args, **kwargs) for param in sig.parameters.values(): if param.name not in bound_arguments.arguments: @@ -254,7 +253,8 @@ def wraps( ret = _to_units_container(ret, ureg) def decorator(func: Callable[..., Any]) -> Callable[..., Quantity]: - count_params = len(signature(func).parameters) + sig = signature(func) + count_params = len(sig.parameters) if len(args) != count_params: raise TypeError( "%s takes %i parameters, but %i units were passed" @@ -270,7 +270,7 @@ def decorator(func: Callable[..., Any]) -> Callable[..., Quantity]: @functools.wraps(func, assigned=assigned, updated=updated) def wrapper(*values, **kw) -> Quantity: - values, kw = _apply_defaults(func, values, kw) + values, kw = _apply_defaults(sig, values, kw) # In principle, the values are used as is # When then extract the magnitudes when needed. @@ -335,7 +335,8 @@ def check( ] def decorator(func): - count_params = len(signature(func).parameters) + sig = signature(func) + count_params = len(sig.parameters) if len(dimensions) != count_params: raise TypeError( "%s takes %i parameters, but %i dimensions were passed" @@ -351,7 +352,7 @@ def decorator(func): @functools.wraps(func, assigned=assigned, updated=updated) def wrapper(*args, **kwargs): - list_args, empty = _apply_defaults(func, args, kwargs) + list_args, empty = _apply_defaults(sig, args, kwargs) for dim, value in zip(dimensions, list_args): if dim is None: From a2bebe050817894921331e10e789a5a78afdb6f5 Mon Sep 17 00:00:00 2001 From: Saelyos Date: Fri, 20 Oct 2023 10:41:22 +0200 Subject: [PATCH 3/3] Fix unintentional changes --- pint/registry_helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pint/registry_helpers.py b/pint/registry_helpers.py index ec5c7f1f8..a31836ea6 100644 --- a/pint/registry_helpers.py +++ b/pint/registry_helpers.py @@ -134,7 +134,7 @@ def _converter(ureg, values, strict): for ndx in dependent_args_ndx: value = values[ndx] assert _replace_units(args_as_uc[ndx][0], values_by_name) is not None - new_values[ndx] = ureg.convert( + new_values[ndx] = ureg._convert( getattr(value, "_magnitude", value), getattr(value, "_units", UnitsContainer({})), _replace_units(args_as_uc[ndx][0], values_by_name), @@ -143,7 +143,7 @@ def _converter(ureg, values, strict): # third pass: convert other arguments for ndx in unit_args_ndx: if isinstance(values[ndx], ureg.Quantity): - new_values[ndx] = ureg.convert( + new_values[ndx] = ureg._convert( values[ndx]._magnitude, values[ndx]._units, args_as_uc[ndx][0] ) else: @@ -151,7 +151,7 @@ def _converter(ureg, values, strict): if isinstance(values[ndx], str): # if the value is a string, we try to parse it tmp_value = ureg.parse_expression(values[ndx]) - new_values[ndx] = ureg.convert( + new_values[ndx] = ureg._convert( tmp_value._magnitude, tmp_value._units, args_as_uc[ndx][0] ) else: