Skip to content

Commit

Permalink
Install dependencies for MyPy analysis (#753)
Browse files Browse the repository at this point in the history
* fixes #752

* use py3.9

* checkout

* test mypy annotator

* Fix mypy issue

* Ignore mypy issue

* Fix mypy issue

* Fix sympy issue

* Fix some errors

* Update weldx/time.py

* [setup.cfg] mypy uses sqlite cache (installed in all conda-envs)

* renamed loop vars

* Fix issues in core.py

* Fix last issues

* Fix pre-commit issue

* Update constraints of SpatialSeries

* use pint.Units

* defaults

* Update CHANGELOG.rst

* cache mypy sqlite cache

Co-authored-by: Cagtay Fabry <[email protected]>
Co-authored-by: vhirtham <[email protected]>
Co-authored-by: vhirtham <[email protected]>
  • Loading branch information
4 people authored May 19, 2022
1 parent bab2c5d commit 95a23ff
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 30 deletions.
40 changes: 37 additions & 3 deletions .github/workflows/static_analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ on:
schedule:
- cron: '0 6 * * 1'

# execute commands with conda aware shell by default:
defaults:
run:
shell: bash -l {0}

jobs:
pre-commit:
runs-on: ubuntu-latest
Expand All @@ -26,11 +31,40 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
with:
python-version: '3.9'
- name: pip installs
fetch-depth: 0 # Fetch all history for all tags and branches

- uses: CagtayFabry/pydeps2env@main
with:
channels: 'conda-forge defaults'
extras: ''
setup_requires: 'include'

- name: Cache mypy cache
uses: actions/cache@v3
with:
path: |
./.mypy_cache
key: ${{ runner.os }}-${{ hashFiles('./environment.yml') }}

- uses: mamba-org/provision-with-micromamba@main
with:
environment-file: ./environment.yml
environment-name: weldx
cache-env: true
extra-specs: |
python=3.9
wheel
pip
- name: activate env
run: micromamba activate weldx

- name: pip installs # use mypy from pypy, as the conda-forge pkg lacks behind.
run: pip install mypy

- name: Add mypy annotator
uses: pr-annotators/[email protected]

- name: run mypy
run: |
mypy --install-types --non-interactive .
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ fixes
=====

- Fix interactive ``view_tree`` display [:pull:`756`].
- Increase ``mypy`` coverage and update type hints and GH action [:pull:`753`].

documentation
=============
Expand Down
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,21 @@ allow_redefinition = True
show_error_context = True
show_error_codes = True
show_column_numbers = True
sqlite_cache = True

[mypy-weldx.asdf.*]
ignore_errors = True
[mypy-weldx.tags.*]
ignore_errors = True
[mypy-weldx.tests.*]
ignore_errors = True
[mypy-weldx.welding.groove.*]
ignore_errors = True
# remove after refactoring the geometry module
[mypy-weldx.geometry.*]
ignore_errors = True


# this is a workaround for an xarray related mypy bug
# see https://github.com/python/mypy/pull/9495
# and https://github.com/space-physics/msise00/commit/8b59a9383dd6fcc54b7dac74eb95a350308d7b62
Expand Down
52 changes: 30 additions & 22 deletions weldx/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ def __init__(
"""
if not isinstance(expression, sympy.Expr):
expression = sympy.sympify(expression)
self._expression = expression
if not isinstance(expression, sympy.Expr):
raise TypeError("'expression' can't be converted to a sympy expression")
self._expression: sympy.Expr = expression

self.function = sympy.lambdify(
tuple(self._expression.free_symbols), self._expression, "numpy"
Expand Down Expand Up @@ -490,7 +492,7 @@ def _interp_time_expression(self, time: Time, time_unit: str) -> xr.DataArray:
"""Interpolate the time series if its data is a mathematical expression."""
time_q = time.as_quantity(unit=time_unit)
if len(time_q.shape) == 0:
time_q = np.expand_dims(time_q, 0)
time_q = np.expand_dims(time_q, 0) # type: ignore[assignment]

time_xr = xr.DataArray(time_q, dims=["time"])

Expand Down Expand Up @@ -729,7 +731,7 @@ def units(self) -> pint.Unit:

def _quantity_to_coord_tuple(
v: pint.Quantity, dim
) -> tuple[str, np.array, dict[str, pint.Unit]]:
) -> tuple[str, np.ndarray, dict[str, pint.Unit]]:
return (dim, v.m, {UNITS_KEY: v.u})


Expand All @@ -739,7 +741,7 @@ def _quantity_to_xarray(v: pint.Quantity, dim: str = None) -> xr.DataArray:


def _quantities_to_xarray(q_dict: dict[str, pint.Quantity]) -> dict[str, xr.DataArray]:
"""Convert a str:Quantity mapping into a mapping of xarray Datarrays."""
"""Convert a str:Quantity mapping into a mapping of `xarray.DataArray`."""
return {k: _quantity_to_xarray(v, k) for k, v in q_dict.items()}


Expand All @@ -751,7 +753,7 @@ class SeriesParameter:
(DataArray is stored 'as is', other inputs will be converted to quantities).
In addition, the desired dimension on the Parameter and an optional symbol
representation for mathexpressions can be added.
representation for math expressions can be added.
The stored value can be converted to different formats available as properties.
"""
Expand Down Expand Up @@ -797,7 +799,7 @@ def data_array(self) -> xr.DataArray:
values = self.values
if not values.shape:
values = np.expand_dims(values, 0)
return _quantity_to_xarray(values, self.dim)
return _quantity_to_xarray(values, self.dim) # type: ignore[arg-type]

@property
def quantity(self) -> pint.Quantity:
Expand Down Expand Up @@ -889,7 +891,7 @@ def __init__(
self,
obj: Union[pint.Quantity, xr.DataArray, str, MathematicalExpression],
dims: Union[list[str], dict[str, str]] = None,
coords: dict[str, pint.Quantity] = None,
coords: dict[str, Union[list, pint.Quantity]] = None,
units: dict[str, Union[str, pint.Unit]] = None,
interpolation: str = None,
parameters: dict[str, Union[str, pint.Quantity, xr.DataArray]] = None,
Expand Down Expand Up @@ -1007,6 +1009,9 @@ def __init__(
"""
if units is None:
units = {}

self._obj: Union[xr.DataArray, MathematicalExpression] = None
self._variable_units: dict[str, pint.Unit] = None
self._symbol_dims: bidict = bidict({})
Expand All @@ -1020,7 +1025,9 @@ def __init__(
elif isinstance(obj, (MathematicalExpression, str, sympy.Expr)):
if dims is not None and not isinstance(dims, dict):
raise ValueError(f"Argument 'dims' must be dict, not {dims}")
self._init_expression(obj, dims, parameters, units)
self._init_expression(
obj, dims, parameters, {k: U_(v) for k, v in units.items()} # catch str
)
else:
raise TypeError(f'The data type "{type(obj)}" is not supported.')

Expand Down Expand Up @@ -1050,13 +1057,14 @@ def _init_discrete(
self,
data: Union[pint.Quantity, xr.DataArray],
dims: list[str],
coords: dict[str, pint.Quantity],
coords: dict[str, Union[list, pint.Quantity]],
):
"""Initialize the internal data with discrete values."""
if not isinstance(data, xr.DataArray):
if coords is not None:
coords = {
k: SeriesParameter(v, k).coord_tuple for k, v in coords.items()
k: SeriesParameter(v, k).coord_tuple # type: ignore[misc]
for k, v in coords.items()
}
data = xr.DataArray(data=data, dims=dims, coords=coords).weldx.quantify()
else:
Expand Down Expand Up @@ -1087,14 +1095,14 @@ def _init_get_updated_units(
for k, v in self._required_dimension_units.items():
if k not in units and k not in expr.parameters:
units[k] = v
for k, v in units.items():
if k not in expr.get_variable_names():
raise KeyError(f"{k} is not a variable of the expression:\n{expr}")
units[k] = U_(v)
for k2, v2 in units.items():
if k2 not in expr.get_variable_names():
raise KeyError(f"{k2} is not a variable of the expression:\n{expr}")
units[k2] = U_(v2)

for v in expr.get_variable_names():
if v not in units:
units[v] = U_("")
for val in expr.get_variable_names():
if val not in units:
units[val] = U_("")

return units

Expand Down Expand Up @@ -1134,7 +1142,7 @@ def _init_expression(
self._symbol_dims = bidict(dims)

@staticmethod
def _test_expr(expr, dims, units):
def _test_expr(expr, dims, units: dict[str, pint.Unit]) -> pint.Unit:
"""Perform a test evaluation of the expression to determine the resulting units.
This function assures that all of the provided information are compatible
Expand Down Expand Up @@ -1184,7 +1192,7 @@ def _test_expr(expr, dims, units):

@staticmethod
def _format_expression_params(
parameters: dict[str, Union[str, pint.Quantity, xr.DataArray]]
parameters: dict[str, Union[pint.Quantity, xr.DataArray]]
) -> dict[str, Union[pint.Quantity, xr.DataArray]]:
"""Create expression parameters as a valid internal type.
Expand Down Expand Up @@ -1602,7 +1610,7 @@ class SpatialSeries(GenericSeries):

_required_dimensions: list[str] = [_position_dim_name, "c"]
"""Required dimensions"""
_required_dimension_units: dict[str, pint.Unit] = {_position_dim_name: ""}
_required_dimension_units: dict[str, pint.Unit] = {_position_dim_name: U_("")}
"""Required units of a dimension"""
_required_dimension_coordinates: dict[str, list] = {"c": ["x", "y", "z"]}
"""Required coordinates of a dimension."""
Expand All @@ -1611,7 +1619,7 @@ def __init__(
self,
obj: Union[pint.Quantity, xr.DataArray, str, MathematicalExpression],
dims: Union[list[str], dict[str, str]] = None,
coords: dict[str, pint.Quantity] = None,
coords: dict[str, Union[list, pint.Quantity]] = None,
units: dict[str, Union[str, pint.Unit]] = None,
interpolation: str = None,
parameters: dict[str, Union[str, pint.Quantity, xr.DataArray]] = None,
Expand All @@ -1629,7 +1637,7 @@ def _process_quantity(
cls,
obj: Union[pint.Quantity, xr.DataArray, str, MathematicalExpression],
dims: Union[list[str], dict[str, str]],
coords: dict[str, pint.Quantity],
coords: dict[str, Union[list, pint.Quantity]],
) -> xr.DataArray:
"""Turn a quantity into a a correctly formatted data array."""
if isinstance(coords, dict):
Expand Down
2 changes: 1 addition & 1 deletion weldx/measurement.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ def _determine_output_signal_type(type_transformation: str, input_type: str) ->

@staticmethod
def _determine_output_signal_unit(
func: MathematicalExpression, input_unit: Union[str, Union]
func: MathematicalExpression, input_unit: UnitLike
) -> pint.Unit:
"""Determine the unit of a transformations' output signal.
Expand Down
10 changes: 8 additions & 2 deletions weldx/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,8 @@ def as_quantity(self, unit: str = "s") -> pint.Quantity:
nanoseconds = nanoseconds[0]
q = Q_(nanoseconds, "ns").to(unit)
if self.is_absolute:
q.time_ref = self.reference_time # store time_ref info
# store time_ref info
q.time_ref = self.reference_time # type: ignore[attr-defined]
return q

def as_timedelta(self) -> Union[Timedelta, TimedeltaIndex]:
Expand Down Expand Up @@ -654,8 +655,13 @@ def _convert_quantity(
"""Build a time-like pandas.Index from pint.Quantity."""
time_ref = getattr(time, "time_ref", None)
base = "s" # using low base unit could cause rounding errors

if not np.iterable(time): # catch zero-dim arrays
time = np.expand_dims(time, 0)
# The mypy error in the next line is ignored. `np.expand_dims` only expects
# `ndarray` types and does not know about quantities, but pint provides the
# necessary interfaces so that the function works as expected
time = np.expand_dims(time, 0) # type: ignore[assignment]

delta = pd.TimedeltaIndex(data=time.to(base).magnitude, unit=base)
if time_ref is not None:
delta = delta + time_ref
Expand Down
3 changes: 2 additions & 1 deletion weldx/transformations/local_cs.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,8 @@ def from_axis_vectors(

if num_none == 1:
idx = next(i for i, v in enumerate(mat) if v is None) # skipcq: PTC-W0063
mat[idx] = np.cross(mat[(idx - 2) % 3], mat[(idx - 1) % 3])
# type: ignore[arg-type]
mat[idx] = np.cross(mat[(idx - 2) % 3], mat[(idx - 1) % 3]) # type: ignore
elif num_none > 1:
raise ValueError("You need to specify two or more vectors.")

Expand Down
2 changes: 1 addition & 1 deletion weldx/util/xarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ def _get_coordinate_quantities(da) -> dict[str, pint.Quantity]:

def _coordinates_from_quantities(
q_dict: dict[str, pint.Quantity]
) -> dict[str, tuple[str, np.array, dict[str, pint.Unit]]]:
) -> dict[str, tuple[str, np.ndarray, dict[str, pint.Unit]]]:
"""Create a dict with unit information that can be passed as coords for xarray."""
return {
k: (k, v.m, {UNITS_KEY: v.u}) if isinstance(v, pint.Quantity) else v
Expand Down

0 comments on commit 95a23ff

Please sign in to comment.