Skip to content

Commit

Permalink
Add Python 3.11 to workflows and apply ruff linting (#51)
Browse files Browse the repository at this point in the history
* Add Python 3.11 to workflows

* Add ruff to dependencies

* Update makefile clean commands

* Apply ruff linting suggestions
  • Loading branch information
jessicasyu authored Sep 20, 2024
1 parent e55faed commit 78e793e
Show file tree
Hide file tree
Showing 23 changed files with 341 additions and 244 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ jobs:

strategy:
matrix:
python-version: ["3.9", "3.10"]
python-version: ["3.9", "3.10", "3.11"]

steps:

- name: Checkout the repo
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
id: setup-python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
Expand All @@ -31,7 +32,7 @@ jobs:
uses: actions/cache@v4
with:
path: .venv
key: ${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Install dependencies
if: steps.cached-dependencies.outputs.cache-hit != 'true'
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
uses: actions/checkout@v4

- name: Set up Python 3.9
id: setup-python
uses: actions/setup-python@v5
with:
python-version: 3.9
Expand All @@ -30,7 +31,7 @@ jobs:
uses: actions/cache@v4
with:
path: .venv
key: ${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Install dependencies
if: steps.cached-dependencies.outputs.cache-hit != 'true'
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
uses: actions/checkout@v4

- name: Set up Python 3.9
id: setup-python
uses: actions/setup-python@v5
with:
python-version: 3.9
Expand All @@ -27,7 +28,7 @@ jobs:
uses: actions/cache@v4
with:
path: .venv
key: ${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Install dependencies
if: steps.cached-dependencies.outputs.cache-hit != 'true'
Expand Down
23 changes: 12 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
.PHONY: clean build docs

clean: # clean all build, python, and testing files
rm -fr build/
rm -fr dist/
rm -fr .eggs/
find . -name '*.egg-info' -exec rm -fr {} +
find . -name '*.egg' -exec rm -f {} +
rm -rf build/
rm -rf dist/
rm -rf .eggs/
find . -name '*.egg-info' -exec rm -rf {} +
find . -name '*.egg' -exec rm -rf {} +
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +
find . -name '__pycache__' -exec rm -fr {} +
rm -fr .tox/
rm -fr .coverage
rm -fr coverage.xml
rm -fr htmlcov/
rm -fr .pytest_cache
rm -fr .mypy_cache
rm -rf .tox/
rm -rf .coverage
rm -rf coverage.xml
rm -rf htmlcov/
rm -rf .pytest_cache
rm -rf .mypy_cache
rm -rf .ruff_cache

build: # run tox tests and lint
tox
Expand Down
335 changes: 190 additions & 145 deletions poetry.lock

Large diffs are not rendered by default.

47 changes: 45 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ python = "^3.9"
prefect = "^2.8.2"
numpy = "^1.24.2"
pandas = "^1.5.3"
aicsshparam = "^0.1.7"
aicsshparam = "^0.1.10"
vtk = "^9.2.5"
trimesh = "^3.18.3"
scikit-image = "^0.21.0"

[tool.poetry.group.dev.dependencies]
black = "^24.3.0"
Expand All @@ -30,6 +31,7 @@ furo = "^2023.5.20"
myst-parser = "^2.0.0"
sphinx-copybutton = "^0.5.2"
tox = "^4.5.1"
ruff = "^0.6.3"

[tool.isort]
profile = "black"
Expand Down Expand Up @@ -67,11 +69,51 @@ module = [
]
ignore_missing_imports = true

[tool.ruff]
line-length = 100
target-version = "py39"

[tool.ruff.lint]
select = ["ALL"]
ignore = [
"COM812", # missing-trailing-comma
"D100", # undocumented-public-module
"D105", # undocumented-magic-method
"D107", # undocumented-public-init
"D202", # no-blank-line-after-function
"D203", # one-blank-line-before-class
"D212", # multi-line-summary-first-line
"D413", # blank-line-after-last-section
"D416", # section-name-ends-in-colon
]

[tool.ruff.lint.pylint]
max-args = 10

[tool.ruff.lint.per-file-ignores]
"tests/*.py" = [
"D", # pydocstyle
"PT009", # pytest-unittest-assertion
"PT027", # pytest-unittest-raises-assertion
"INP001", # implicit-namespace-package
"ANN201", # missing-return-type-undocumented-public-function
"S311", # suspicious-non-cryptographic-random-usage
"ANN001", # missing-type-function-argument
"ANN003", # missing-type-kwargs
"ANN202", # missing-type-args
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"if TYPE_CHECKING:",
]

[tool.tox]
legacy_tox_ini = """
[tox]
isolated_build = True
envlist = py{39,310}, format, lint, typecheck
envlist = py{39,310,311}, format, lint, typecheck
skipsdist=True
[testenv]
Expand All @@ -87,6 +129,7 @@ commands =
[testenv:lint]
commands =
poetry run pylint --ignore-patterns=test.*?py src/ tests/ --fail-under=9.0
poetry run ruff check src/ tests/
[testenv:typecheck]
commands =
Expand Down
2 changes: 1 addition & 1 deletion src/abm_shape_collection/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Tasks for analyzing cell shapes including spherical harmonics and shape modes."""
"""Collection of tasks for analyzing cell shapes."""

from prefect import task

Expand Down
6 changes: 3 additions & 3 deletions src/abm_shape_collection/calculate_feature_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ def calculate_feature_statistics(

for feature in features:
# Extract values for specific component.
ref_values = ref_data[feature].values
values = data[feature].values
ref_values = ref_data[feature].to_numpy()
values = data[feature].to_numpy()

# Calculate KolmogorovSmirnov statistic.
# Calculate Kolmogorov-Smirnov statistic.
ks_result = ks_2samp(values, ref_values, mode="asymp")

statistics.append(
Expand Down
5 changes: 2 additions & 3 deletions src/abm_shape_collection/calculate_shape_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ def calculate_shape_statistics(
label: str = "shcoeffs",
) -> pd.DataFrame:
"""
Perform two-sample Kolmogorov-Smirnov test for goodness of fit on shape
components.
Perform two-sample Kolmogorov-Smirnov test for goodness of fit on shapes.
Parameters
----------
Expand Down Expand Up @@ -45,7 +44,7 @@ def calculate_shape_statistics(
ref_values = ref_transform[:, component]
values = transform[:, component]

# Calculate KolmogorovSmirnov statistic.
# Calculate Kolmogorov-Smirnov statistic.
ks_result = ks_2samp(values, ref_values, mode="asymp")

statistics.append(
Expand Down
2 changes: 1 addition & 1 deletion src/abm_shape_collection/construct_mesh_from_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

def construct_mesh_from_array(array: np.ndarray, reference: np.ndarray) -> vtkPolyData:
"""
Constructs a mesh from binary image array.
Construct a mesh from binary image array.
Parameters
----------
Expand Down
2 changes: 1 addition & 1 deletion src/abm_shape_collection/construct_mesh_from_coeffs.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def construct_mesh_from_coeffs(
scale: float = 1.0,
) -> vtkPolyData:
"""
Constructs a mesh from spherical harmonic coefficients.
Construct a mesh from spherical harmonic coefficients.
Parameters
----------
Expand Down
2 changes: 1 addition & 1 deletion src/abm_shape_collection/construct_mesh_from_points.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def construct_mesh_from_points(
suffix: str = "",
) -> vtkPolyData:
"""
Constructs mesh given PCA transformation points.
Construct mesh given PCA transformation points.
Parameters
----------
Expand Down
47 changes: 25 additions & 22 deletions src/abm_shape_collection/extract_mesh_projections.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import tempfile
from typing import Optional, Union
from enum import Enum

import numpy as np
import trimesh
Expand All @@ -13,11 +15,20 @@
"""Mesh projection names, normals, and extent axes."""


class ProjectionType(Enum):
"""Projection slice types."""

SLICE = 1
"""Slice projection type."""

EXTENT = 2
"""Extent projection type."""


def extract_mesh_projections(
mesh: Union[vtkPolyData, trimesh.Trimesh],
slices: bool = True,
extents: bool = True,
offset: Optional[tuple[float, float, float]] = None,
mesh: vtkPolyData | trimesh.Trimesh,
projection_types: list[ProjectionType],
offset: tuple[float, float, float] | None = None,
) -> dict:
"""
Extract slices and/or extents from mesh.
Expand All @@ -31,10 +42,8 @@ def extract_mesh_projections(
----------
mesh
Mesh object.
slices
True to extract slices, False otherwise.
extents : bool, optional
True to extract extents, False otherwise.
projection_types
Mesh projection types.
offset
Mesh translation applied before extracting slices and/or meshes.
Expand All @@ -50,15 +59,13 @@ def extract_mesh_projections(
if offset is not None:
mesh.apply_translation(offset)

projections: dict[str, Union[list[list[list[float]]], dict[float, list[list[list[float]]]]]] = (
{}
)
projections: dict[str, list[list[list[float]]] | dict[float, list[list[list[float]]]]] = {}

if slices:
if ProjectionType.SLICE in projection_types:
for projection, normal, _ in PROJECTIONS:
projections[f"{projection}_slice"] = get_mesh_slice(mesh, normal)

if extents:
if ProjectionType.EXTENT in projection_types:
for projection, normal, index in PROJECTIONS:
projections[f"{projection}_extent"] = get_mesh_extent(mesh, normal, index)

Expand All @@ -67,7 +74,7 @@ def extract_mesh_projections(

def convert_vtk_to_trimesh(mesh: vtkPolyData) -> trimesh.Trimesh:
"""
Converts VTK polydata to trimesh object.
Convert VTK polydata to trimesh object.
Parameters
----------
Expand All @@ -85,9 +92,7 @@ def convert_vtk_to_trimesh(mesh: vtkPolyData) -> trimesh.Trimesh:
writer.SetFileTypeToASCII()
writer.SetFileName(f"{temp.name}.ply")
_ = writer.Write()
mesh = trimesh.load(f"{temp.name}.ply")

return mesh
return trimesh.load(f"{temp.name}.ply")


def get_mesh_slice(mesh: trimesh.Trimesh, normal: tuple[int, int, int]) -> list[list[list[float]]]:
Expand All @@ -108,8 +113,7 @@ def get_mesh_slice(mesh: trimesh.Trimesh, normal: tuple[int, int, int]) -> list[
"""

mesh_slice = mesh.section_multiplane((0, 0, 0), normal, [0])
points = [[list(point) for point in entity] for entity in mesh_slice[0].discrete]
return points
return [[list(point) for point in entity] for entity in mesh_slice[0].discrete]


def get_mesh_extent(
Expand All @@ -136,9 +140,8 @@ def get_mesh_extent(
layers = int(mesh.extents[index] + 2)
plane_indices = list(np.arange(-layers, layers + 1, 0.5))
mesh_extents = mesh.section_multiplane((0, 0, 0), normal, plane_indices)
points = {
return {
index: [[list(point) for point in entity] for entity in mesh_extent.discrete]
for mesh_extent, index in zip(mesh_extents, plane_indices)
if mesh_extent is not None
}
return points
14 changes: 8 additions & 6 deletions src/abm_shape_collection/extract_mesh_wireframe.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from typing import Optional, Union
from __future__ import annotations

from typing import TYPE_CHECKING

import trimesh
from vtk import vtkPolyData # pylint: disable=no-name-in-module

from abm_shape_collection.extract_mesh_projections import convert_vtk_to_trimesh

if TYPE_CHECKING:
import trimesh


def extract_mesh_wireframe(
mesh: Union[vtkPolyData, trimesh.Trimesh], offset: Optional[tuple[float, float, float]] = None
mesh: vtkPolyData | trimesh.Trimesh, offset: tuple[float, float, float] | None = None
) -> list[list[tuple[float, float, float]]]:
"""
Extract wireframe edges from mesh.
Expand All @@ -32,9 +36,7 @@ def extract_mesh_wireframe(
mesh.apply_translation(offset)

all_edges = [[tuple(mesh.vertices[a]), tuple(mesh.vertices[b])] for a, b in mesh.edges]
wireframe = [
return [
[(x1, y1, z1), (x2, y2, z2)]
for (x1, y1, z1), (x2, y2, z2) in {frozenset(edge) for edge in all_edges}
]

return wireframe
Loading

0 comments on commit 78e793e

Please sign in to comment.