Skip to content

Commit fbd57b1

Browse files
Ken KundertKen Kundert
authored andcommitted
Deprecate units argument for scaling functions
Switched the code and the documentation to use scaling functions without units as second argum▶ Loosened UnitConversion so it accepts scaling functions written for Quantity.scale() Switched to using ruff for linting
1 parent a309354 commit fbd57b1

File tree

8 files changed

+203
-64
lines changed

8 files changed

+203
-64
lines changed

doc/examples.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,10 @@ they like, but internally they are all converted to seconds.
322322
>>> _ = UnitConversion('s', 'sec second seconds')
323323
>>> _ = UnitConversion('s', 'm min minute minutes', 60)
324324
>>> _ = UnitConversion('s', 'h hr hour hours', 60*60)
325-
>>> _ = UnitConversion('s', 'd day days', 24*60*60)
326-
>>> _ = UnitConversion('s', 'w week weeks', 7*24*60*60)
325+
>>> _ = UnitConversion('s', 'D d day days', 24*60*60)
326+
>>> _ = UnitConversion('s', 'W w week weeks', 7*24*60*60)
327327
>>> _ = UnitConversion('s', 'M month months', 30*24*60*60)
328-
>>> _ = UnitConversion('s', 'y year years', 365*24*60*60)
328+
>>> _ = UnitConversion('s', 'Y y year years', 365*24*60*60)
329329
>>> Quantity.set_prefs(ignore_sf=True)
330330
331331
>>> configuration = '''

doc/user.rst

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -452,18 +452,15 @@ conversion is :index:`not linear <dB>`:
452452

453453
.. code-block:: python
454454
455-
>>> def from_dB(value, units=''):
455+
>>> def from_dB(value):
456456
... return 10**(value/20), value.units[2:]
457457
458458
>>> Quantity('-100 dBV', scale=from_dB)
459459
Quantity('10 uV')
460460
461-
.. note::
462-
463-
Since version 2.18 the first argument, in this case *value*, is guaranteed to
464-
be a :class:`Quantity` that contains both the units and any parameters needed
465-
during the conversion. As such, the second argument, *units*, is not longer
466-
needed and will eventually be removed.
461+
The argument to the conversion function, in this case *value*, is guaranteed to
462+
be a :class:`Quantity` that contains both the units and any parameters needed
463+
during the conversion.
467464

468465
The conversion can also often occur if you simply state the units you wish the
469466
quantity to have:
@@ -833,8 +830,35 @@ in grams and wanting to present it in either kilograms or in pounds:
833830
>>> print("mass (kg): {}".format(m.render(show_units=False, scale=0.001)))
834831
mass (kg): 2.529
835832
836-
>>> print(m.render(scale=(0.0022046, 'lb'), form='fixed'))
837-
5.5754 lb
833+
Alternatively, you can use either the built-in converters or the converters you
834+
created to do the conversion simply based on the units:
835+
836+
.. code-block:: python
837+
838+
>>> print(m.render(scale='lb'))
839+
5.5755 lb
840+
841+
You can also give the desired scale factor as part of the units:
842+
843+
.. code-block:: python
844+
845+
>>> m = Quantity('2529 g')
846+
>>> print("mass (kg): {}".format(m.render(show_units=False, scale='kg')))
847+
mass (kg): 2.529
848+
849+
This also works with the binary scale factors:
850+
851+
.. code-block:: python
852+
853+
>>> rates = [155.52e6, 622.08e6, 2.48832e9, 9.95328e9, 39.81312e9]
854+
>>> rates = [Quantity(r, 'b') for r in rates]
855+
>>> for r in rates:
856+
... print(f"{r:>14,.2pMiB}")
857+
18.54 MiB
858+
74.16 MiB
859+
296.63 MiB
860+
1,186.52 MiB
861+
4,746.09 MiB
838862
839863
As before, functions can also be used to do the conversion. Here is an example
840864
where that comes in handy: a logarithmic conversion to :index:`dBV <dB>` is
@@ -843,27 +867,16 @@ performed.
843867
.. code-block:: python
844868
845869
>>> import math
846-
>>> def to_dB(value, units=''):
870+
>>> def to_dB(value):
847871
... return 20*math.log10(value), 'dB'+value.units
848872
849873
>>> T = Quantity('100mV')
850874
>>> print(T.render(scale=to_dB))
851875
-20 dBV
852876
853-
.. note::
854-
855-
Since version 2.18 the first argument, in this case *value*, is guaranteed to
856-
be a :class:`Quantity` that contains both the units and any parameters needed
857-
during the conversion. As such, the second argument, *units*, is not longer
858-
needed and will eventually be removed.
859-
860-
Finally, you can also use either the built-in converters or the converters you
861-
created to do the conversion simply based on the units:
862-
863-
.. code-block:: python
864-
865-
>>> print(m.render(scale='lb'))
866-
5.5755 lb
877+
The argument to the conversion function, in this case *value*, is guaranteed to
878+
be a :class:`Quantity` that contains both the units and any parameters needed
879+
during the conversion.
867880

868881
In an earlier example the units of time and temperature data were converted to
869882
normal SI units. Presumably this makes processing easier. Now, when producing
@@ -1208,11 +1221,11 @@ accordingly:
12081221
12091222
>>> for p in range(1, 5):
12101223
... bytes = Quantity(256**p, 'B')
1211-
... print(f"An {8*p} bit bus addresses {bytes:,pkB}.")
1212-
An 8 bit bus addresses 0.256 kB.
1213-
An 16 bit bus addresses 65.536 kB.
1214-
An 24 bit bus addresses 16,777.216 kB.
1215-
An 32 bit bus addresses 4,294,967.296 kB.
1224+
... print(f"An {8*p} bit bus addresses {bytes:,pkB} ({bytes:,pKiB}).")
1225+
An 8 bit bus addresses 0.256 kB (0.25 KiB).
1226+
An 16 bit bus addresses 65.536 kB (64 KiB).
1227+
An 24 bit bus addresses 16,777.216 kB (16,384 KiB).
1228+
An 32 bit bus addresses 4,294,967.296 kB (4,194,304 KiB).
12161229
12171230
Generally you should only specify base units when using a format that renders
12181231
with scale factors as otherwise you could see two scale factors on the same
@@ -1637,13 +1650,14 @@ One way around this is to add another conversion specifically for differences:
16371650
>>> print(Quantity('100 Δ°C', scale='Δ°F'))
16381651
180 Δ°F
16391652
1640-
Notice that the scaling functions used here differ from those described
1641-
previously in that they only take one argument and return one value. The units
1642-
are not included in either then argument list or the return value.
1653+
Notice that the scaling functions used here differ slightly from those described
1654+
previously in that they only return one value. The units are not included in
1655+
the return value. You can use the previously described scaling functions that
1656+
do return units here, but the units will be ignored.
16431657

1644-
Also notice that the return value of *UnitConversion* was not used in the
1645-
examples above. It is enough to simply create the *UnitConversion* for it to be
1646-
available to *Quantity*. So, it is normal to not capture the return value of
1658+
The return value of *UnitConversion* was not used in the examples above. It is
1659+
enough to simply create the *UnitConversion* for it to be available to
1660+
*Quantity*. So, it is normal to not capture the return value of
16471661
*UnitConversion*. However, there are a few things you can do with the return
16481662
value. First you can convert it to a string to get a description of the
16491663
relationship. This is largely used as a sanity check:
@@ -1721,8 +1735,8 @@ conversion. For example:
17211735
17221736
For more information on defining unit converters, see :class:`UnitConversion`.
17231737
For more information on parametrized unit converters, see
1724-
:meth:`UnitConversion.fixture`. For example of real-time dynamic conversions,
1725-
see :ref:`quantiphy bitcoin example`.
1738+
:meth:`UnitConversion.fixture`. For an example of real-time dynamic
1739+
conversions, see :ref:`quantiphy bitcoin example`.
17261740

17271741

17281742
.. index::
@@ -1739,10 +1753,10 @@ doing so the relationship between the units must be known, and
17391753
:class:`UnitConversion` is used to specify the relationship. However, it is
17401754
also possible to perform simple scale factor conversions without changing the
17411755
units. This case is specified in a manner similar to a unit conversion, but in
1742-
this case both the from-units and the to-units are the same, and it is not
1743-
necessary to define a :class:`UnitConversion`. For example, imagine printing
1744-
a table of bit-rates where the rates are held in bps but are expected to be
1745-
displayed in Mbps:
1756+
this case both the from-units and the to-units are the same or are equivalent,
1757+
and it is not necessary to define a :class:`UnitConversion`. For example,
1758+
imagine printing a table of bit-rates where the rates are held in bps but are
1759+
expected to be displayed in Mbps:
17461760

17471761
.. code-block:: python
17481762

examples/bitcoin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def get_btc_price():
1515
resp = requests.get(url=url, params=params)
1616
prices = resp.json()
1717
return prices['bitcoin']['usd']
18-
except Exception as e:
18+
except Exception:
1919
print('error: cannot connect to coingecko.com.')
2020

2121
# use UnitConversion from QuantiPhy to perform the conversion

pyproject.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,13 @@ changelog = "https://github.com/KenKundert/quantiphy/blob/master/doc/releases.rs
3838
[build-system]
3939
requires = ["flit_core >=2,<4"]
4040
build-backend = "flit_core.buildapi"
41+
42+
[tool.ruff]
43+
exclude = [".tox", "doc", "tests", "Diffs"]
44+
45+
[tool.ruff.lint]
46+
select = ["F"]
47+
ignore = []
48+
49+
[tool.ruff.lint.per-file-ignores]
50+
"quantiphy/__init__.py" = ["F401"] # imported but unused

quantiphy/quantiphy.py

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,29 @@ def _scale(scale, unscaled):
7373
if isinstance(scale, str):
7474
scaled = UnitConversion._convert_units(scale, unscaled.units, unscaled)
7575
to_units = scale
76-
else:
77-
# otherwise, it might be a function
76+
return scaled, to_units
77+
78+
if callable(scale):
7879
try:
79-
scaled, to_units = scale(unscaled, unscaled.units)
80-
# passing units as second argument is redundant, deprecated
81-
except TypeError:
82-
# otherwise, assume it is a scale factor
80+
scaled, to_units = scale(unscaled)
81+
# do not pass units as second argument, this is the new style
82+
except TypeError as e:
8383
try:
84-
# might be a tuple containing scale factor and units
85-
multiplier, to_units = scale
86-
except TypeError:
87-
# otherwise, assume it is just a scale factor
88-
multiplier = scale
89-
to_units = unscaled.units
90-
scaled = multiplier * unscaled
84+
scaled, to_units = scale(unscaled, unscaled.units)
85+
# passing units as second argument is redundant, deprecated
86+
except TypeError: # pragma: no cover
87+
raise e
88+
return scaled, to_units
89+
90+
# otherwise, assume it is a scale factor
91+
try:
92+
# might be a tuple containing scale factor and units
93+
multiplier, to_units = scale
94+
except TypeError:
95+
# otherwise, assume it is just a scale factor
96+
multiplier = scale
97+
to_units = unscaled.units
98+
scaled = multiplier * unscaled
9199
return scaled, to_units
92100

93101

@@ -3469,7 +3477,7 @@ def clear_all(cls):
34693477
cls._known_units = set()
34703478

34713479
@classmethod
3472-
def enable_scaling(cls, si_scaling=None, bin_scaling=None):
3480+
def enable_sf_scaling(cls, si_scaling=None, bin_scaling=None):
34733481
"""By default the given or desired units in a unit conversion or scaling
34743482
may include scale factors. This is true for both SI and binary scale
34753483
factors. The scale factor is provided as a prefix on the units. In
@@ -3660,7 +3668,25 @@ def extract_sf(units):
36603668
converter = cls._unit_conversions[(to_units, from_units)]
36613669
except KeyError:
36623670
raise UnknownConversion(to_units=orig_to_units, from_units=orig_from_units)
3663-
return converter(value.scale(from_sf)) / to_sf
3671+
scaled = value.scale(from_sf)
3672+
try:
3673+
value = converter(scaled)
3674+
except TypeError as e:
3675+
# assume that this scale function is the deprecated form that
3676+
# expects units as the second argument
3677+
try:
3678+
value = converter(scaled, scaled.units)
3679+
except TypeError: # pragma: no cover
3680+
raise e
3681+
try:
3682+
value, units = value
3683+
# the Quantity.scale() method returns the units along with the
3684+
# scaled value. This differs from UnitConversion scale
3685+
# functions, which do not return the units. This code allows
3686+
# UnitConversion to work with Quantity.scale() scale functions.
3687+
except TypeError:
3688+
pass
3689+
return value / to_sf
36643690

36653691

36663692
# __str__ {{{3

tests/test_doctests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_manual():
3333
Quantity.reset_prefs()
3434
expected_test_count = {
3535
'../doc/index.rst': 31,
36-
'../doc/user.rst': 450,
36+
'../doc/user.rst': 454,
3737
'../doc/api.rst': 0,
3838
'../doc/examples.rst': 42,
3939
'../doc/accessories.rst': 12,

0 commit comments

Comments
 (0)