Skip to content
Merged
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
20 changes: 10 additions & 10 deletions .github/workflows/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ jobs:
build:
runs-on: ubuntu-latest
name: Test python API
strategy:
matrix:
python-version: ['3.10', '3.11'] # Test on multiple Python versions
fail-fast: false # Continue testing other versions even if one fails
defaults:
run:
working-directory: .
steps:
- uses: actions/checkout@v2 # Use latest version of checkout action
- uses: actions/setup-python@v2
- uses: actions/checkout@v4 # Use latest version of checkout action
- uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: ${{ matrix.python-version }}
- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
Expand All @@ -28,14 +32,10 @@ jobs:
- name: Run tests and collect coverage
run: pytest --cov .
env:
COVERAGE_FILE: coverage.$PYTHON_VERSION.xml
COVERAGE_FILE: coverage.${{ matrix.python-version }}.xml
shell: bash
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
files: coverage.*.xml # Upload coverage reports for all Python versions
if: always() # Ensure the action is executed even if the tests fail
strategy:
matrix:
python-version: ['3.10', '3.11'] # Test on multiple Python versions
fail-fast: false # Continue testing other versions even if one fails
4 changes: 2 additions & 2 deletions .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ jobs:
matrix:
python-version: ["3.10"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
31 changes: 20 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ and this project adheres (at least as of 0.3.0!) to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).


1.1.0
1.3.0
-----

- Added CHANGELOG.md
[#100](https://github.com/juhasch/PhysicalQuantities/pull/100)
[@juhasch](https://github.com/juhasch)
- Move from regular expression to tokenize in IPython extension
[#98](https://github.com/juhasch/PhysicalQuantities/pull/98)
[@juhasch](https://github.com/juhasch)
- Introduce fractdict to handle fractions in units.
- Refactor arithmetic operations (`*`, `/`, `//`) in `PhysicalUnit` and `PhysicalQuantity` for type consistency and Python 3 compatibility.
- Resolve circular imports between `unit.py` and `quantity.py` via local imports.
- Correct logic in `PhysicalUnit.is_power` and `dBQuantity` comparison methods for proper dimensional handling.
- Fix `PhysicalUnit.__hash__` to enable unit/quantity hashing and restore `@lru_cache` on `findunit`.
- Improve error handling and type checks in `findunit` and `add_composite_unit`.
- Correct unit handling and type checking in `numpywrapper.linspace`.
- Update tests to match corrected behavior and expected exceptions.
- Improve code readability (f-strings, type checks).



1.1.1
-----
Expand All @@ -30,8 +35,12 @@ and this project adheres (at least as of 0.3.0!) to
[@juhasch](https://github.com/juhasch)
- Fix ReadTheDocs build

Unreleased (aka. GitHub master)
-------------------------------
1.1.0
-----

- Introduce fractdict to handle fractions in units.
- Type annotation checking using mypy. Allows compiling the whole package using 'mypyc'
- Added CHANGELOG.md
[#100](https://github.com/juhasch/PhysicalQuantities/pull/100)
[@juhasch](https://github.com/juhasch)
- Move from regular expression to tokenize in IPython extension
[#98](https://github.com/juhasch/PhysicalQuantities/pull/98)
[@juhasch](https://github.com/juhasch)
4 changes: 2 additions & 2 deletions PhysicalQuantities/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from .dBQuantity import dBQuantity, dB_unit_table
from .quantityarray import PhysicalQuantityArray

__version__: str = '1.1.1'
__version__: str = '1.3.0'

Q = PhysicalQuantity
U = PhysicalUnit
Expand Down Expand Up @@ -84,7 +84,7 @@ def __getattr__(self, attr):
try:
_Q = self.table[attr]
except KeyError as exc:
raise KeyError(f'Unit {attr} not found') from exc
raise AttributeError(f'Unit {attr} not found') from exc
return _Q

def _ipython_key_completions_(self):
Expand Down
63 changes: 44 additions & 19 deletions PhysicalQuantities/dBQuantity.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,14 +440,16 @@ def lin20(self) -> PhysicalQuantity | float:
ValueError
If a power quantity is converted
"""

val = pow(10, self.value / 20)
if self.unit.physicalunit is not None:
if self.unit.is_power is True:
raise ValueError('Invalid 10^(x/20) conversion of a power quantity')
else:
# This property is for amplitude-like units (is_power is False)
if self.unit.is_power is False:
return PhysicalQuantity(val, self.unit.physicalunit)
else:
# Raise error if called on a power unit
raise ValueError('Invalid 10^(x/20) conversion of a power quantity')
else:
# No physical unit, return raw value (e.g., for plain dB)
return val

def __add__(self, other):
Expand Down Expand Up @@ -620,15 +622,21 @@ def __gt__(self, other):
Raises
------
UnitError
If different dBunit or type are compared
If different dBunit or type are compared, or if underlying
physical units are dimensionally incompatible.

"""
if isinstance(other, dBQuantity):
# dB values without scaling
if self.unit.name == other.unit.name:
return self.value > other.value
elif self.lin.base.unit == other.lin.base.unit:
# If dB units differ, check underlying physical units
elif self.lin.base.unit.powers == other.lin.base.unit.powers:
# If dimensions match, compare linear values in base units
return self.lin.base.value > other.lin.base.value
else:
# If dimensions differ, raise error
raise UnitError(f'Cannot compare dB quantities with incompatible underlying units: {self.unit.physicalunit.name} and {other.unit.physicalunit.name}')
else:
raise UnitError('Cannot compare dBQuantity with type %s' % type(other))

Expand All @@ -648,14 +656,17 @@ def __ge__(self, other):
Raises
------
UnitError
If different dBunit or type are compared
If different dBunit or type are compared, or if underlying
physical units are dimensionally incompatible.

"""
if isinstance(other, dBQuantity):
if self.unit.name is other.unit.name:
if self.unit.name == other.unit.name:
return self.value >= other.value
elif self.lin.base.unit == other.lin.base.unit:
elif self.lin.base.unit.powers == other.lin.base.unit.powers:
return self.lin.base.value >= other.lin.base.value
else:
raise UnitError(f'Cannot compare dB quantities with incompatible underlying units: {self.unit.physicalunit.name} and {other.unit.physicalunit.name}')
else:
raise UnitError('Cannot compare dBQuantity with type %s' % type(other))

Expand All @@ -675,14 +686,17 @@ def __lt__(self, other):
Raises
------
UnitError
If different dBunit or type are compared
If different dBunit or type are compared, or if underlying
physical units are dimensionally incompatible.

"""
if isinstance(other, dBQuantity):
if self.unit.name == other.unit.name:
return self.value < other.value
elif self.lin.base.unit == other.lin.base.unit:
elif self.lin.base.unit.powers == other.lin.base.unit.powers:
return self.lin.base.value < other.lin.base.value
else:
raise UnitError(f'Cannot compare dB quantities with incompatible underlying units: {self.unit.physicalunit.name} and {other.unit.physicalunit.name}')
else:
raise UnitError('Cannot compare dBQuantity with type %s' % type(other))

Expand All @@ -702,14 +716,17 @@ def __le__(self, other):
Raises
------
UnitError
If different dBUnit or type are compared
If different dBUnit or type are compared, or if underlying
physical units are dimensionally incompatible.

"""
if isinstance(other, dBQuantity):
if self.unit.name == other.unit.name:
return self.value <= other.value
elif self.lin.base.unit == other.lin.base.unit:
elif self.lin.base.unit.powers == other.lin.base.unit.powers:
return self.lin.base.value <= other.lin.base.value
else:
raise UnitError(f'Cannot compare dB quantities with incompatible underlying units: {self.unit.physicalunit.name} and {other.unit.physicalunit.name}')
else:
raise UnitError('Cannot compare dBQuantity with type %s' % type(other))

Expand All @@ -729,16 +746,20 @@ def __eq__(self, other):
Raises
------
UnitError
If different dBunit or type are compared
If different dBunit or type are compared, or if underlying
physical units are dimensionally incompatible.

"""
if isinstance(other, dBQuantity):
if self.unit.name == other.unit.name:
return self.value == other.value
elif self.lin.base.unit == other.lin.base.unit:
elif self.lin.base.unit.powers == other.lin.base.unit.powers:
return self.lin.base.value == other.lin.base.value
else:
raise UnitError(f'Cannot compare dB quantities with incompatible underlying units: {self.unit.physicalunit.name} and {other.unit.physicalunit.name}')
else:
raise UnitError('Cannot compare dBQuantity with type %s' % type(other))
# Return False for comparison with non-dBQuantity types
return False

def __ne__(self, other):
""" Test if two quantities are not equal
Expand All @@ -756,13 +777,17 @@ def __ne__(self, other):
Raises
------
UnitError
If different dBUnit or type are compared
If different dBUnit or type are compared, or if underlying
physical units are dimensionally incompatible.

"""
if isinstance(other, dBQuantity):
if self.unit.name == other.unit.name:
return self.value != other.value
elif self.lin.base.unit == other.lin.base.unit:
elif self.lin.base.unit.powers == other.lin.base.unit.powers:
return self.lin.base.value != other.lin.base.value
else:
raise UnitError(f'Cannot compare dB quantities with incompatible underlying units: {self.unit.physicalunit.name} and {other.unit.physicalunit.name}')
else:
raise UnitError('Cannot compare dBQuantity with type %s' % type(other))
# Return True for comparison with non-dBQuantity types
return True
Loading