Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow string parsing of offset units #1349

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ Pint Changelog
- Improved the application registry.
(Issue #1366, thanks keewis)
- Improved testing isolation using pytest fixtures.
- Fix string formatting of numpy array scalars
- Allow string parsing of offset units
(Issue #386)

### Breaking Changes

Expand All @@ -47,6 +50,7 @@ Pint Changelog
0.17 (2021-03-22)
-----------------

- Do not build "universal" wheel as Python 2 is not supported.
- Add the Wh unit for battery capacity measurements
(PR #1260, thanks Maciej Grela)
- Fix issue with reducable dimensionless units when using power (Quantity**ndarray)
Expand Down
14 changes: 12 additions & 2 deletions pint/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
RedefinitionError,
UndefinedUnitError,
)
from .pint_eval import build_eval_tree
from .pint_eval import _BINARY_OPERATOR_MAP, build_eval_tree
from .systems import Group, System
from .util import (
ParserHelper,
Expand Down Expand Up @@ -1232,6 +1232,11 @@ def _eval_token(self, token, case_sensitive=None, use_decimal=False, **values):
else:
raise Exception("unknown token type")

def _eval_implicit_mul(self, left, right):
if isinstance(left, self.Quantity):
return left * right
return self.Quantity(left, right)

def parse_pattern(
self,
input_string: str,
Expand Down Expand Up @@ -1337,8 +1342,13 @@ def parse_expression(
input_string = string_preprocessor(input_string)
gen = tokenizer(input_string)

# replace implicit multiplication with self._eval_implicit_mul
bin_op = dict(_BINARY_OPERATOR_MAP)
bin_op[""] = self._eval_implicit_mul

return build_eval_tree(gen).evaluate(
lambda x: self._eval_token(x, case_sensitive=case_sensitive, **values)
lambda x: self._eval_token(x, case_sensitive=case_sensitive, **values),
bin_op=bin_op,
)

__call__ = parse_expression
Expand Down
10 changes: 10 additions & 0 deletions pint/testsuite/test_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,16 @@ def test_issue354_356_370(self):
assert "{:~}".format(1 * self.ureg.count) == "1 count"
assert "{:~}".format(1 * self.ureg("MiB")) == "1 MiB"

def test_issue_386(self):
x = ureg.Quantity(42, "degC")
y = ureg.Quantity("42 degC")
assert x == y

# make sure normal combined units still work
x = ureg.Quantity(42, ureg.mm * ureg.s)
y = ureg.Quantity("42 mm s")
assert x == y

def test_issue468(self):
@ureg.wraps("kg", "meter")
def f(x):
Expand Down
36 changes: 18 additions & 18 deletions pint/testsuite/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ def test_square_cube(self):
self._test("sq bcd", "bcd**2")
self._test("square bcd", "bcd**2")
self._test("cubic bcd", "bcd**3")
self._test("bcd efg", "bcd*efg")
self._test("bcd efg", "bcd efg")

def test_per(self):
self._test("miles per hour", "miles/hour")
Expand All @@ -244,25 +244,25 @@ def test_numbers(self):
self._test("1E24", "1E24")

def test_space_multiplication(self):
self._test("bcd efg", "bcd*efg")
self._test("bcd efg", "bcd*efg")
self._test("1 hour", "1*hour")
self._test("1. hour", "1.*hour")
self._test("1.1 hour", "1.1*hour")
self._test("1E24 hour", "1E24*hour")
self._test("1E-24 hour", "1E-24*hour")
self._test("1E+24 hour", "1E+24*hour")
self._test("1.2E24 hour", "1.2E24*hour")
self._test("1.2E-24 hour", "1.2E-24*hour")
self._test("1.2E+24 hour", "1.2E+24*hour")
self._test("bcd efg", "bcd efg")
self._test("bcd efg", "bcd efg")
self._test("1 hour", "1 hour")
self._test("1. hour", "1. hour")
self._test("1.1 hour", "1.1 hour")
self._test("1E24 hour", "1E24 hour")
self._test("1E-24 hour", "1E-24 hour")
self._test("1E+24 hour", "1E+24 hour")
self._test("1.2E24 hour", "1.2E24 hour")
self._test("1.2E-24 hour", "1.2E-24 hour")
self._test("1.2E+24 hour", "1.2E+24 hour")

def test_joined_multiplication(self):
self._test("1hour", "1*hour")
self._test("1.hour", "1.*hour")
self._test("1.1hour", "1.1*hour")
self._test("1h", "1*h")
self._test("1.h", "1.*h")
self._test("1.1h", "1.1*h")
self._test("1hour", "1 hour")
self._test("1.hour", "1. hour")
self._test("1.1hour", "1.1 hour")
self._test("1h", "1 h")
self._test("1.h", "1. h")
self._test("1.1h", "1.1 h")

def test_names(self):
self._test("g_0", "g_0")
Expand Down
3 changes: 1 addition & 2 deletions pint/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,9 +761,8 @@ def __rtruediv__(self, other):
(r"sq ({})", r"\1**2"),
(
r"\b([0-9]+\.?[0-9]*)(?=[e|E][a-zA-Z]|[a-df-zA-DF-Z])",
r"\1*",
r"\1 ",
), # Handle numberLetter for multiplication
(r"([\w\.\-])\s+(?=\w)", r"\1*"), # Handle space for multiplication
]

#: Compiles the regex and replace {} by a regex that matches an identifier.
Expand Down
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ test =
[options.package_data]
pint = default_en.txt; constants_en.txt; py.typed

[check-manifest]
ignore =
.travis.yml

[build-system]
requires = ["setuptools", "setuptools_scm", "wheel"]

Expand Down