Skip to content

Commit

Permalink
Drop py 39 and upgrade (#230)
Browse files Browse the repository at this point in the history
* go to py 3.10 following SPEC0

* auto-fix with 'ruff check  --extend-select=UP --fix'

* deal with unsafe fixes
  • Loading branch information
sroet authored Oct 2, 2024
1 parent 9f1e4e7 commit 2f5bb47
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 102 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ classifiers = [
"Topic :: Scientific/Engineering",
"Typing :: Typed"
]
requires-python = ">= 3.9"
requires-python = ">= 3.10"
dependencies = [
"numpy",
"cupy!=13.0.*", #see https://github.com/SBC-Utrecht/pytom-match-pick/issues/106
Expand Down
5 changes: 2 additions & 3 deletions src/pytom_tm/angles.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import pathlib
import os
from typing import Union
from scipy.spatial.transform import Rotation
import numpy as np
import healpix as hp
Expand Down Expand Up @@ -94,7 +93,7 @@ def load_angle_list(


def get_angle_list(
angle: Union[pathlib.Path, float],
angle: pathlib.Path | float,
sort_angles: bool = True,
symmetry: int = 1,
log_level: int = logging.DEBUG,
Expand Down Expand Up @@ -135,7 +134,7 @@ def get_angle_list(
f"Will generate an angle list with a maximum increment of {angle}",
)
out = angle_to_angle_list(angle, sort_angles, log_level)
elif isinstance(angle, (str, os.PathLike)):
elif isinstance(angle, str | os.PathLike):
possible_file_path = pathlib.Path(angle)
if possible_file_path.exists() and possible_file_path.suffix == ".txt":
logging.log(
Expand Down
33 changes: 16 additions & 17 deletions src/pytom_tm/correlation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@

import numpy.typing as npt
import cupy.typing as cpt
from typing import Optional, Union


def mean_under_mask(
data: Union[npt.NDArray[float], cpt.NDArray[float]],
mask: Union[npt.NDArray[float], cpt.NDArray[float]],
mask_weight: Optional[float] = None,
) -> Union[float, cpt.NDArray[float]]:
data: npt.NDArray[float] | cpt.NDArray[float],
mask: npt.NDArray[float] | cpt.NDArray[float],
mask_weight: float | None = None,
) -> float | cpt.NDArray[float]:
"""Calculate mean of array in the mask region.
data and mask can be cupy or numpy arrays.
Expand All @@ -37,11 +36,11 @@ def mean_under_mask(


def std_under_mask(
data: Union[npt.NDArray[float], cpt.NDArray[float]],
mask: Union[npt.NDArray[float], cpt.NDArray[float]],
data: npt.NDArray[float] | cpt.NDArray[float],
mask: npt.NDArray[float] | cpt.NDArray[float],
mean: float,
mask_weight: Optional[float] = None,
) -> Union[float, cpt.NDArray[float]]:
mask_weight: float | None = None,
) -> float | cpt.NDArray[float]:
"""Calculate standard deviation of array in the mask region. Uses mean_under_mask()
to calculate the mean of data**2 within the mask.
Expand All @@ -68,10 +67,10 @@ def std_under_mask(


def normalise(
data: Union[npt.NDArray[float], cpt.NDArray[float]],
mask: Optional[Union[npt.NDArray[float], cpt.NDArray[float]]] = None,
mask_weight: Optional[float] = None,
) -> Union[npt.NDArray[float], cpt.NDArray[float]]:
data: npt.NDArray[float] | cpt.NDArray[float],
mask: npt.NDArray[float] | cpt.NDArray[float] | None = None,
mask_weight: float | None = None,
) -> npt.NDArray[float] | cpt.NDArray[float]:
"""Normalise array by subtracting mean and dividing by standard deviation. If a mask
is provided the array is normalised with the mean and std calculated within the
mask.
Expand Down Expand Up @@ -102,10 +101,10 @@ def normalise(


def normalised_cross_correlation(
data1: Union[npt.NDArray[float], cpt.NDArray[float]],
data2: Union[npt.NDArray[float], cpt.NDArray[float]],
mask: Optional[Union[npt.NDArray[float], cpt.NDArray[float]]] = None,
) -> Union[float, cpt.NDArray[float]]:
data1: npt.NDArray[float] | cpt.NDArray[float],
data2: npt.NDArray[float] | cpt.NDArray[float],
mask: npt.NDArray[float] | cpt.NDArray[float] | None = None,
) -> float | cpt.NDArray[float]:
"""Calculate normalised cross correlation between two arrays. Optionally only in a
masked region.
Expand Down
7 changes: 3 additions & 4 deletions src/pytom_tm/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import logging
import scipy.ndimage as ndimage
import pathlib
from typing import Optional
from pytom_tm.tmjob import TMJob
from pytom_tm.mask import spherical_mask
from pytom_tm.angles import get_angle_list, convert_euler
Expand All @@ -28,7 +27,7 @@

def predict_tophat_mask(
score_volume: npt.NDArray[float],
output_path: Optional[pathlib.Path] = None,
output_path: pathlib.Path | None = None,
n_false_positives: float = 1.0,
create_plot: bool = True,
tophat_connectivity: int = 1,
Expand Down Expand Up @@ -148,9 +147,9 @@ def extract_particles(
job: TMJob,
particle_radius_px: int,
n_particles: int,
cut_off: Optional[float] = None,
cut_off: float | None = None,
n_false_positives: float = 1.0,
tomogram_mask_path: Optional[pathlib.Path] = None,
tomogram_mask_path: pathlib.Path | None = None,
tophat_filter: bool = False,
create_plot: bool = True,
tophat_connectivity: int = 1,
Expand Down
53 changes: 22 additions & 31 deletions src/pytom_tm/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
import numpy as np
from contextlib import contextmanager
from operator import attrgetter
from typing import Optional, Union


class ParseLogging(argparse.Action):
"""argparse.Action subclass to parse logging parameter from input scripts. Users can
set these to info/debug."""

def __call__(
self, parser, namespace, values: str, option_string: Optional[str] = None
self, parser, namespace, values: str, option_string: str | None = None
):
if values.upper() not in ["INFO", "DEBUG"]:
parser.error(
Expand All @@ -34,12 +33,10 @@ def __call__(
parser,
namespace,
values: pathlib.Path,
option_string: Optional[str] = None,
option_string: str | None = None,
):
if not values.is_dir():
parser.error(
"{0} got a file path that does not exist ".format(option_string)
)
parser.error(f"{option_string} got a file path that does not exist ")

setattr(namespace, self.dest, values)

Expand All @@ -52,12 +49,10 @@ def __call__(
parser,
namespace,
values: pathlib.Path,
option_string: Optional[str] = None,
option_string: str | None = None,
):
if not values.exists():
parser.error(
"{0} got a file path that does not exist ".format(option_string)
)
parser.error(f"{option_string} got a file path that does not exist ")

setattr(namespace, self.dest, values)

Expand All @@ -69,11 +64,11 @@ def __call__(
self,
parser,
namespace,
values: Union[int, float],
option_string: Optional[str] = None,
values: int | float,
option_string: str | None = None,
):
if values <= 0.0:
parser.error("{0} must be larger than 0".format(option_string))
parser.error(f"{option_string} must be larger than 0")

setattr(namespace, self.dest, values)

Expand All @@ -83,13 +78,11 @@ class BetweenZeroAndOne(argparse.Action):
0 and 1."""

def __call__(
self, parser, namespace, values: float, option_string: Optional[str] = None
self, parser, namespace, values: float, option_string: str | None = None
):
if 1.0 <= values <= 0.0:
parser.error(
"{0} is a fraction and can only range between 0 and 1".format(
option_string
)
f"{option_string} is a fraction and can only range between 0 and 1"
)

setattr(namespace, self.dest, values)
Expand All @@ -105,7 +98,7 @@ def __call__(
parser,
namespace,
values: list[int, int],
option_string: Optional[str] = None,
option_string: str | None = None,
):
if not (0 <= values[0] < values[1]):
parser.error(
Expand All @@ -126,8 +119,8 @@ def __call__(
self,
parser,
namespace,
values: Union[list[str, str], str],
option_string: Optional[str] = None,
values: list[str, str] | str,
option_string: str | None = None,
):
if len(values) == 2: # two wedge angles provided the min and max
try:
Expand All @@ -147,7 +140,7 @@ def __call__(
)
setattr(namespace, self.dest, read_tlt_file(values))
else:
parser.error("{0} can only take one or two arguments".format(option_string))
parser.error(f"{option_string} can only take one or two arguments")


class ParseGPUIndices(argparse.Action):
Expand All @@ -160,7 +153,7 @@ def __call__(
parser,
namespace,
values: list[int, ...],
option_string: Optional[str] = None,
option_string: str | None = None,
):
import cupy

Expand All @@ -180,20 +173,18 @@ class ParseDoseFile(argparse.Action):
dose per tilt."""

def __call__(
self, parser, namespace, values: str, option_string: Optional[str] = None
self, parser, namespace, values: str, option_string: str | None = None
):
file_path = pathlib.Path(values)
if not file_path.exists():
parser.error(
"{0} provided dose accumulation file does not exist".format(
option_string
)
f"{option_string} provided dose accumulation file does not exist"
)
allowed_suffixes = [".txt"]
if file_path.suffix not in allowed_suffixes:
parser.error(
"{0} provided dose accumulation file does not have the right suffix, "
"allowed are: {1}".format(option_string, ", ".join(allowed_suffixes))
f"{option_string} provided dose accumulation file does not have the "
f"right suffix, allowed are: {', '.join(allowed_suffixes)}"
)
setattr(namespace, self.dest, read_dose_file(file_path))

Expand All @@ -203,7 +194,7 @@ class ParseDefocus(argparse.Action):
to their file format, or a txt file containing per line the defocus of each tilt."""

def __call__(
self, parser, namespace, values: str, option_string: Optional[str] = None
self, parser, namespace, values: str, option_string: str | None = None
):
if values.endswith((".defocus", ".txt")):
file_path = pathlib.Path(values)
Expand Down Expand Up @@ -395,7 +386,7 @@ def read_txt_file(file_name: pathlib.Path) -> list[float, ...]:
output: list[float, ...]
list of floats
"""
with open(file_name, "r") as fstream:
with open(file_name) as fstream:
lines = fstream.readlines()
return list(map(float, [x.strip() for x in lines if not x.isspace()]))

Expand Down Expand Up @@ -450,7 +441,7 @@ def read_imod_defocus_file(file_name: pathlib.Path) -> list[float, ...]:
output: list[float, ...]
list of floats with defocus (in μm)
"""
with open(file_name, "r") as fstream:
with open(file_name) as fstream:
lines = fstream.readlines()
imod_defocus_version = float(lines[0].strip().split()[5])
# imod defocus files have the values specified in nm:
Expand Down
9 changes: 4 additions & 5 deletions src/pytom_tm/mask.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import numpy as np
import numpy.typing as npt
from typing import Optional


def spherical_mask(
box_size: int,
radius: float,
smooth: Optional[float] = None,
smooth: float | None = None,
cutoff_sd: int = 3,
center: Optional[float] = None,
center: float | None = None,
) -> npt.NDArray[float]:
"""Wrapper around ellipsoidal_mask() to create a spherical mask with just a single
radius.
Expand Down Expand Up @@ -42,9 +41,9 @@ def ellipsoidal_mask(
major: float,
minor1: float,
minor2: float,
smooth: Optional[float] = None,
smooth: float | None = None,
cutoff_sd: int = 3,
center: Optional[float] = None,
center: float | None = None,
) -> npt.NDArray[float]:
"""Create an ellipsoidal mask in the specified square box. Ellipsoid is defined by 3
radius on x,y, and z axis.
Expand Down
11 changes: 5 additions & 6 deletions src/pytom_tm/matching.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import numpy.typing as npt
import voltools as vt
import gc
from typing import Optional
from cupyx.scipy.fft import rfftn, irfftn
from tqdm import tqdm
from pytom_tm.correlation import mean_under_mask, std_under_mask
Expand All @@ -17,8 +16,8 @@ def __init__(
template: npt.NDArray[float],
mask: npt.NDArray[float],
device_id: int,
wedge: Optional[npt.NDArray[float]] = None,
phase_randomized_template: Optional[npt.NDArray[float]] = None,
wedge: npt.NDArray[float] | None = None,
phase_randomized_template: npt.NDArray[float] | None = None,
):
"""Initialize a template matching plan. All the necessary cupy arrays will be
allocated on the GPU.
Expand Down Expand Up @@ -126,8 +125,8 @@ def __init__(
angle_list: list[tuple[float, float, float]],
angle_ids: list[int],
mask_is_spherical: bool = True,
wedge: Optional[npt.NDArray[float]] = None,
stats_roi: Optional[tuple[slice, slice, slice]] = None,
wedge: npt.NDArray[float] | None = None,
stats_roi: tuple[slice, slice, slice] | None = None,
noise_correction: bool = False,
rng_seed: int = 321,
):
Expand Down Expand Up @@ -220,7 +219,7 @@ def run(self) -> tuple[npt.NDArray[float], npt.NDArray[float], dict]:
- a dictionary with three floats of search statistics;
'search_space', 'variance', and 'std'
"""
print("Progress job_{} on device {:d}:".format(self.job_id, self.device_id))
print(f"Progress job_{self.job_id} on device {self.device_id:d}:")

# Size x template (sxz) and center x template (cxt)
sxt, syt, szt = self.plan.template.shape
Expand Down
5 changes: 2 additions & 3 deletions src/pytom_tm/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import logging
from scipy.ndimage import center_of_mass, zoom
from scipy.fft import rfftn, irfftn
from typing import Optional
from pytom_tm.weights import (
create_gaussian_low_pass,
radial_reduced_grid,
Expand All @@ -16,8 +15,8 @@ def generate_template_from_map(
input_spacing: float,
output_spacing: float,
center: bool = False,
filter_to_resolution: Optional[float] = None,
output_box_size: Optional[int] = None,
filter_to_resolution: float | None = None,
output_box_size: int | None = None,
) -> npt.NDArray[float]:
"""Generate a template from a density map.
Expand Down
Loading

0 comments on commit 2f5bb47

Please sign in to comment.