diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index fc295cc..55b8844 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -43,9 +43,11 @@ jobs: run: | tox + # 2024-07-02: disable CodeCov (temporarily?), uploads do not work even with a token # Code coverage is run on Python 3.10, see tox.ini - - if: ${{ matrix.python-version == '3.10' }} - uses: codecov/codecov-action@v3 - with: - fail_ci_if_error: true # optional (default = false) - verbose: true # optional (default = false) + #- if: ${{ matrix.python-version == '3.10' }} + # uses: codecov/codecov-action@v4 + # with: + # token: ${{ secrets.CODECOV_TOKEN }} + # fail_ci_if_error: true # optional (default = false) + # verbose: true # optional (default = false) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bb88e78..b54ee7f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -15,11 +15,12 @@ repos: - id: name-tests-test - id: trailing-whitespace -- repo: https://github.com/pycqa/flake8 - rev: 6.0.0 - hooks: - - id: flake8 - additional_dependencies: [pep8-naming] +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.5.0 + hooks: + # Run the linter. + - id: ruff - repo: https://github.com/mgedmin/check-manifest rev: "0.49" diff --git a/docs/conf.py b/docs/conf.py index b5247d6..ef6c940 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # neuroglancer-scripts documentation build configuration file, created by # sphinx-quickstart on Fri Feb 2 15:05:24 2018. @@ -19,11 +18,11 @@ # import os import sys + sys.path.insert(0, os.path.abspath('../src/')) import neuroglancer_scripts # noqa: E402 - # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. diff --git a/experimental/mesh_to_vtk.py b/experimental/mesh_to_vtk.py index de27472..3406b2b 100755 --- a/experimental/mesh_to_vtk.py +++ b/experimental/mesh_to_vtk.py @@ -7,16 +7,15 @@ import sys +import neuroglancer_scripts.mesh import nibabel import numpy as np -import neuroglancer_scripts.mesh - def mesh_file_to_vtk(input_filename, output_filename, data_format="ascii", coord_transform=None): """Convert a mesh file read by nibabel to VTK format""" - print("Reading {}".format(input_filename)) + print(f"Reading {input_filename}") mesh = nibabel.load(input_filename) print() print("Summary") @@ -45,7 +44,7 @@ def mesh_file_to_vtk(input_filename, output_filename, data_format="ascii", # Gifti uses millimetres, Neuroglancer expects nanometres points *= 1e6 - with open(output_filename, "wt") as output_file: + with open(output_filename, "w") as output_file: neuroglancer_scripts.mesh.save_mesh_as_neuroglancer_vtk( output_file, points, triangles ) @@ -79,15 +78,15 @@ def parse_command_line(argv): try: matrix = np.fromstring(args.coord_transform, sep=",") except ValueError as exc: - parser.error("cannot parse --coord-transform: {}" - .format(exc.args[0])) + parser.error(f"cannot parse --coord-transform: {exc.args[0]}" + ) if len(matrix) == 12: matrix = matrix.reshape(3, 4) elif len(matrix) == 16: matrix = matrix.reshape(4, 4) else: parser.error("--coord-transform must have 12 or 16 elements" - " ({} passed)".format(len(matrix))) + f" ({len(matrix)} passed)") args.coord_transform = matrix diff --git a/experimental/off_to_vtk.py b/experimental/off_to_vtk.py index 4e3b880..bb56528 100755 --- a/experimental/off_to_vtk.py +++ b/experimental/off_to_vtk.py @@ -19,7 +19,7 @@ def off_mesh_file_to_vtk(input_filename, output_filename, data_format="binary", coord_transform=None): """Convert a mesh file from OFF format to VTK format""" - print("Reading {}".format(input_filename)) + print(f"Reading {input_filename}") with gzip.open(input_filename, "rt") as f: header_keyword = f.readline().strip() match = re.match(r"(ST)?(C)?(N)?(4)?(n)?OFF", header_keyword) @@ -32,7 +32,7 @@ def off_mesh_file_to_vtk(input_filename, output_filename, data_format="binary", assert match num_vertices = int(match.group(1)) num_triangles = int(match.group(2)) - vertices = np.empty((num_vertices, 3), dtype=np.float) + vertices = np.empty((num_vertices, 3), dtype=float) for i in range(num_vertices): components = f.readline().split() assert len(components) >= 3 @@ -48,8 +48,8 @@ def off_mesh_file_to_vtk(input_filename, output_filename, data_format="binary", triangles[i, 1] = float(components[2]) triangles[i, 2] = float(components[3]) print() - print("{0} vertices and {1} triangles read" - .format(num_vertices, num_triangles)) + print(f"{num_vertices} vertices and {num_triangles} triangles read" + ) points = vertices @@ -108,15 +108,15 @@ def parse_command_line(argv): try: matrix = np.fromstring(args.coord_transform, sep=",") except ValueError as exc: - parser.error("cannot parse --coord-transform: {}" - .format(exc.args[0])) + parser.error(f"cannot parse --coord-transform: {exc.args[0]}" + ) if len(matrix) == 12: matrix = matrix.reshape(3, 4) elif len(matrix) == 16: matrix = matrix.reshape(4, 4) else: parser.error("--coord-transform must have 12 or 16 elements" - " ({} passed)".format(len(matrix))) + f" ({len(matrix)} passed)") args.coord_transform = matrix diff --git a/experimental/stl_to_precomputed.py b/experimental/stl_to_precomputed.py index b79077b..637678b 100644 --- a/experimental/stl_to_precomputed.py +++ b/experimental/stl_to_precomputed.py @@ -5,13 +5,13 @@ # # This software is made available under the MIT licence, see LICENCE.txt. -# flake8: noqa +# noqa """ Convert a mesh from STL ASCII to Neuroglancer pre-computed mesh format -Currently STL triangles are just written to the output as is, i.e. normals are not considered -and equal vertices are not reused. +Currently STL triangles are just written to the output as is, i.e. normals are +not considered and equal vertices are not reused. """ import gzip @@ -78,10 +78,13 @@ def parse_command_line(argv): """Parse the script's command line.""" import argparse parser = argparse.ArgumentParser( - description="""Convert a mesh from STL ASCII to Neuroglancer pre-computed mesh format""") + description="Convert a mesh from STL ASCII to Neuroglancer " + "pre-computed mesh format") parser.add_argument("input_filename") parser.add_argument("output_filename") - parser.add_argument("--voxel-size", help="Voxel size in mm. Only isotropic voxels are supported for now. Default is 1.0", + parser.add_argument("--voxel-size", help="Voxel size in mm. Only " + "isotropic voxels are supported for now. Default is " + "1.0", type=float, default=1.0) parser.add_argument("--no-compression", help="Don't gzip the output.", action="store_false", dest="compress") @@ -93,7 +96,8 @@ def main(argv): """The script's entry point.""" args = parse_command_line(argv) return stl_file_to_precomputed( - args.input_filename, args.output_filename, args.voxel_size, args.compress) or 0 + args.input_filename, args.output_filename, args.voxel_size, + args.compress) or 0 if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 9e03e86..3103c98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,3 +4,22 @@ requires = [ "wheel", ] build-backend = "setuptools.build_meta" + +[tool.ruff] +target-version = "py37" # py36 does not exist +line-length = 79 +indent-width = 4 + +[tool.ruff.lint] +extend-select = [ + "F", + "E", + "W", + "I", + "N", + "NPY", + "UP", +] +ignore = [ + "N802", # Gives false positives when a name contains an uppercase acronym +] diff --git a/script_tests/test_scripts.py b/script_tests/test_scripts.py index ffe19df..24230a1 100644 --- a/script_tests/test_scripts.py +++ b/script_tests/test_scripts.py @@ -13,12 +13,10 @@ import nibabel import numpy as np -import pytest import PIL.Image - +import pytest from neuroglancer_scripts.mesh import read_precomputed_mesh - # Environment passed to sub-processes so that they raise an error on warnings env = os.environ.copy() env['PYTHONWARNINGS'] = 'error' @@ -36,8 +34,8 @@ def test_jubrain_example_MPM(examples_dir, tmpdir): try: gzip.open(str(input_nifti)).read(348) except OSError as exc: - pytest.skip("Cannot find a valid example file {0} for testing: {1}" - .format(input_nifti, exc)) + pytest.skip(f"Cannot find a valid example file {input_nifti} for " + f"testing: {exc}") output_dir = tmpdir / "MPM" assert subprocess.call([ @@ -82,8 +80,8 @@ def test_all_in_one_conversion(examples_dir, tmpdir): try: gzip.open(str(input_nifti)).read(348) except OSError as exc: - pytest.skip("Cannot find a valid example file {0} for testing: {1}" - .format(input_nifti, exc)) + pytest.skip(f"Cannot find a valid example file {input_nifti} for " + f"testing: {exc}") output_dir = tmpdir / "colin27T1_seg" assert subprocess.call([ @@ -106,8 +104,8 @@ def test_sharded_conversion(examples_dir, tmpdir): try: gzip.open(str(input_nifti)).read(348) except OSError as exc: - pytest.skip("Cannot find a valid example file {0} for testing: {1}" - .format(input_nifti, exc)) + pytest.skip(f"Cannot find a valid example file {input_nifti} for " + f"testing: {exc}") output_dir = tmpdir / "colin27T1_seg_sharded" assert subprocess.call([ @@ -118,7 +116,7 @@ def test_sharded_conversion(examples_dir, tmpdir): str(output_dir) ], env=env) == 4 # datatype not supported by neuroglancer - with open(output_dir / "info_fullres.json", "r") as fp: + with open(output_dir / "info_fullres.json") as fp: fullres_info = json.load(fp=fp) with open(output_dir / "info_fullres.json", "w") as fp: fullres_info["data_type"] = "uint8" diff --git a/setup.cfg b/setup.cfg index 43c79ee..422fc5b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ classifiers = Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Topic :: Scientific/Engineering :: Medical Science Apps. Topic :: Scientific/Engineering :: Visualization keywords = neuroimaging @@ -30,7 +31,7 @@ packages = find: python_requires = ~=3.6 install_requires = nibabel >= 2 - numpy >= 1.11.0 + numpy >= 1.17 pillow >= 1.1.6 requests >= 2 scikit-image # TODO use pillow instead @@ -47,11 +48,11 @@ dev = pytest requests-mock check-manifest - flake8 pep8-naming pre-commit pytest-cov readme_renderer + ruff sphinx tox docs = diff --git a/src/neuroglancer_scripts/_compressed_segmentation.py b/src/neuroglancer_scripts/_compressed_segmentation.py index 743f60e..554228f 100644 --- a/src/neuroglancer_scripts/_compressed_segmentation.py +++ b/src/neuroglancer_scripts/_compressed_segmentation.py @@ -9,8 +9,8 @@ import numpy as np -from neuroglancer_scripts.utils import ceil_div from neuroglancer_scripts.chunk_encoding import InvalidFormatError +from neuroglancer_scripts.utils import ceil_div def pad_block(block, block_size): @@ -104,7 +104,7 @@ def _encode_channel(chunk_channel, block_size): def _pack_encoded_values(encoded_values, bits): if bits == 0: - return bytes() + return b"" else: assert 32 % bits == 0 assert np.array_equal(encoded_values, @@ -162,8 +162,8 @@ def _decode_channel_into(chunk, channel, buf, block_size): bits = res[0] >> 24 if bits not in (0, 1, 2, 4, 8, 16, 32): raise InvalidFormatError("Invalid number of encoding bits for " - "compressed_segmentation block ({0})" - .format(bits)) + f"compressed_segmentation block ({bits})" + ) encoded_values_offset = 4 * res[1] lookup_table_past_end = lookup_table_offset + chunk.itemsize * min( (2 ** bits), diff --git a/src/neuroglancer_scripts/_jpeg.py b/src/neuroglancer_scripts/_jpeg.py index 2fe136a..cdef05f 100644 --- a/src/neuroglancer_scripts/_jpeg.py +++ b/src/neuroglancer_scripts/_jpeg.py @@ -10,7 +10,6 @@ import numpy as np import PIL.Image - from neuroglancer_scripts.chunk_encoding import InvalidFormatError @@ -47,17 +46,17 @@ def decode_chunk(buf, chunk_size, num_channels): img = PIL.Image.open(io_buf) except Exception as exc: raise InvalidFormatError( - "The JPEG-encoded chunk could not be decoded: {0}" - .format(exc)) from exc + f"The JPEG-encoded chunk could not be decoded: {exc}" + ) from exc if num_channels == 1 and img.mode != "L": raise InvalidFormatError( - "The JPEG chunk is encoded with mode={0} instead of L" - .format(img.mode)) + f"The JPEG chunk is encoded with mode={img.mode} instead of L" + ) if num_channels == 3 and img.mode != "RGB": raise InvalidFormatError( - "The JPEG chunk is encoded with mode={0} instead of RGB" - .format(img.mode)) + f"The JPEG chunk is encoded with mode={img.mode} instead of RGB" + ) flat_chunk = np.asarray(img) if num_channels == 3: @@ -68,7 +67,6 @@ def decode_chunk(buf, chunk_size, num_channels): chunk_size[2], chunk_size[1], chunk_size[0]) except Exception: raise InvalidFormatError("The JPEG-encoded chunk has an incompatible " - "shape ({0} elements, expecting {1})" - .format(flat_chunk.size // num_channels, - np.prod(chunk_size))) + f"shape ({flat_chunk.size // num_channels} " + f"elements, expecting {np.prod(chunk_size)})") return chunk diff --git a/src/neuroglancer_scripts/accessor.py b/src/neuroglancer_scripts/accessor.py index 4a4fdcb..b678a5f 100644 --- a/src/neuroglancer_scripts/accessor.py +++ b/src/neuroglancer_scripts/accessor.py @@ -11,8 +11,8 @@ :func:`get_accessor_for_url` for instantiating a concrete accessor object. """ -import urllib.parse import json +import urllib.parse __all__ = [ "get_accessor_for_url", @@ -37,8 +37,7 @@ def get_accessor_for_url(url, accessor_options={}): url = _strip_precomputed(url) r = urllib.parse.urlsplit(url) if r.scheme in ("", "file"): - from neuroglancer_scripts import file_accessor - from neuroglancer_scripts import sharded_base + from neuroglancer_scripts import file_accessor, sharded_base flat = accessor_options.get("flat", False) gzip = accessor_options.get("gzip", True) compresslevel = accessor_options.get("compresslevel", 9) @@ -67,8 +66,7 @@ def get_accessor_for_url(url, accessor_options={}): return accessor elif r.scheme in ("http", "https"): - from neuroglancer_scripts import http_accessor - from neuroglancer_scripts import sharded_base + from neuroglancer_scripts import http_accessor, sharded_base accessor = http_accessor.HttpAccessor(url) is_sharding = False @@ -90,8 +88,8 @@ def get_accessor_for_url(url, accessor_options={}): return sharded_http_accessor.ShardedHttpAccessor(url) return accessor else: - raise URLError("Unsupported URL scheme {0} (must be file, http, or " - "https)".format(r.scheme)) + raise URLError(f"Unsupported URL scheme {r.scheme} (must be file, " + "http, or https)") def add_argparse_options(parser, write_chunks=True, write_files=True): diff --git a/src/neuroglancer_scripts/chunk_encoding.py b/src/neuroglancer_scripts/chunk_encoding.py index ab44f5c..164033e 100644 --- a/src/neuroglancer_scripts/chunk_encoding.py +++ b/src/neuroglancer_scripts/chunk_encoding.py @@ -11,7 +11,6 @@ import numpy as np - __all__ = [ "get_encoder", "add_argparse_options", @@ -49,14 +48,14 @@ def get_encoder(info, scale_info, encoder_options={}): num_channels = info["num_channels"] encoding = scale_info["encoding"] except KeyError as exc: - raise InvalidInfoError("The info dict is missing an essential key {0}" - .format(exc)) from exc + raise InvalidInfoError("The info dict is missing an essential key " + f"{exc}") from exc if not isinstance(num_channels, int) or not num_channels > 0: - raise InvalidInfoError("Invalid value {0} for num_channels (must be " - "a positive integer)".format(num_channels)) + raise InvalidInfoError(f"Invalid value {num_channels} for " + "num_channels (must be a positive integer)") if data_type not in NEUROGLANCER_DATA_TYPES: - raise InvalidInfoError("Invalid data_type {0} (should be one of {1})" - .format(data_type, NEUROGLANCER_DATA_TYPES)) + raise InvalidInfoError(f"Invalid data_type {data_type} (should be one " + f"of {NEUROGLANCER_DATA_TYPES})") try: if encoding == "raw": return RawChunkEncoder(data_type, num_channels) @@ -76,7 +75,7 @@ def get_encoder(info, scale_info, encoder_options={}): jpeg_plane=jpeg_plane, jpeg_quality=jpeg_quality) else: - raise InvalidInfoError("Invalid encoding {0}".format(encoding)) + raise InvalidInfoError(f"Invalid encoding {encoding}") except IncompatibleEncoderError as exc: raise InvalidInfoError(str(exc)) from exc @@ -192,8 +191,8 @@ def decode(self, buf, chunk_size): (self.num_channels, chunk_size[2], chunk_size[1], chunk_size[0])) except Exception as exc: - raise InvalidFormatError("Cannot decode raw-encoded chunk: {0}" - .format(exc)) from exc + raise InvalidFormatError(f"Cannot decode raw-encoded chunk: {exc}" + ) from exc class CompressedSegmentationEncoder(ChunkEncoder): diff --git a/src/neuroglancer_scripts/data_types.py b/src/neuroglancer_scripts/data_types.py index 6c3b3dd..6363ac7 100644 --- a/src/neuroglancer_scripts/data_types.py +++ b/src/neuroglancer_scripts/data_types.py @@ -7,7 +7,6 @@ import numpy as np - __all__ = [ "NG_DATA_TYPES", "NG_INTEGER_DATA_TYPES", @@ -88,7 +87,7 @@ def get_dtype(input_dtype): if input_dtype.names is None: return input_dtype, False if input_dtype.names not in NG_MULTICHANNEL_DATATYPES: - err = 'tuple datatype {} not yet supported'.format(input_dtype.names) + err = f'tuple datatype {input_dtype.names} not yet supported' raise NotImplementedError(err) for index, value in enumerate(input_dtype.names): err = 'Multichanneled datatype should have the same datatype' diff --git a/src/neuroglancer_scripts/downscaling.py b/src/neuroglancer_scripts/downscaling.py index 6626288..f801dd7 100644 --- a/src/neuroglancer_scripts/downscaling.py +++ b/src/neuroglancer_scripts/downscaling.py @@ -13,9 +13,8 @@ import numpy as np -from neuroglancer_scripts.utils import ceil_div from neuroglancer_scripts.data_types import get_chunk_dtype_transformer - +from neuroglancer_scripts.utils import ceil_div __all__ = [ "get_downscaler", @@ -49,8 +48,8 @@ def get_downscaler(downscaling_method, info=None, options={}): elif downscaling_method == "stride": return StridingDownscaler() else: - raise NotImplementedError("invalid downscaling method {0}" - .format(downscaling_method)) + raise NotImplementedError("invalid downscaling method " + + downscaling_method) def add_argparse_options(parser): diff --git a/src/neuroglancer_scripts/dyadic_pyramid.py b/src/neuroglancer_scripts/dyadic_pyramid.py index 6e83184..a1969a8 100644 --- a/src/neuroglancer_scripts/dyadic_pyramid.py +++ b/src/neuroglancer_scripts/dyadic_pyramid.py @@ -10,8 +10,7 @@ import numpy as np from tqdm import tqdm -from neuroglancer_scripts.utils import (LENGTH_UNITS, ceil_div, format_length) - +from neuroglancer_scripts.utils import LENGTH_UNITS, ceil_div, format_length __all__ = [ "choose_unit_for_key", @@ -31,8 +30,8 @@ def choose_unit_for_key(resolution_nm): and (format_length(resolution_nm, unit) != format_length(resolution_nm * 2, unit))): return unit - raise NotImplementedError("cannot find a suitable unit for {} nm" - .format(resolution_nm)) + raise NotImplementedError("cannot find a suitable unit for " + f"{resolution_nm} nm") def fill_scales_for_dyadic_pyramid(info, target_chunk_size=64, @@ -176,15 +175,15 @@ def compute_dyadic_downscaling(info, source_scale_index, downscaler, if new_size != [ceil_div(os, ds) for os, ds in zip(old_size, downscaling_factors)]: raise ValueError("Unsupported downscaling factor between scales " - "{} and {} (only 1 and 2 are supported)" - .format(old_key, new_key)) + f"{old_key} and {new_key} " + "(only 1 and 2 are supported)") downscaler.check_factors(downscaling_factors) if chunk_reader.scale_is_lossy(old_key): logger.warning( "Using data stored in a lossy format (scale %s) as an input " - "for downscaling (to scale %s)" % (old_key, new_key) + "for downscaling (to scale %s)", old_key, new_key ) half_chunk = [osz // f @@ -211,7 +210,7 @@ def load_and_downscale_old_chunk(z_idx, y_idx, x_idx): # TODO how to do progress report correctly with logging? for x_idx, y_idx, z_idx in tqdm( np.ndindex(chunk_range), total=np.prod(chunk_range), - desc="computing scale {}".format(new_key), + desc=f"computing scale {new_key}", unit="chunks", leave=True): xmin = new_chunk_size[0] * x_idx xmax = min(new_chunk_size[0] * (x_idx + 1), new_size[0]) diff --git a/src/neuroglancer_scripts/file_accessor.py b/src/neuroglancer_scripts/file_accessor.py index 0267e07..034bff4 100644 --- a/src/neuroglancer_scripts/file_accessor.py +++ b/src/neuroglancer_scripts/file_accessor.py @@ -16,7 +16,6 @@ import neuroglancer_scripts.accessor from neuroglancer_scripts.accessor import _CHUNK_PATTERN_FLAT, DataAccessError - __all__ = [ "FileAccessor", ] @@ -64,7 +63,7 @@ def file_exists(self, relative_path): return True except OSError as exc: raise DataAccessError( - "Error fetching {0}: {1}".format(file_path, exc)) from exc + f"Error fetching {file_path}: {exc}") from exc return False def fetch_file(self, relative_path): @@ -80,13 +79,13 @@ def fetch_file(self, relative_path): f = gzip.open(str(file_path.with_name(file_path.name + ".gz")), "rb") else: - raise DataAccessError("Cannot find {0} in {1}".format( - relative_path, self.base_path)) + raise DataAccessError(f"Cannot find {relative_path} in " + f"{self.base_path}") with f: return f.read() except OSError as exc: raise DataAccessError( - "Error fetching {0}: {1}".format(file_path, exc)) from exc + f"Error fetching {file_path}: {exc}") from exc def store_file(self, relative_path, buf, mime_type="application/octet-stream", @@ -108,8 +107,8 @@ def store_file(self, relative_path, buf, with file_path.open(mode) as f: f.write(buf) except OSError as exc: - raise DataAccessError("Error storing {0}: {1}" - .format(file_path, exc)) from exc + raise DataAccessError(f"Error storing {file_path}: {exc}" + ) from exc def fetch_chunk(self, key, chunk_coords): f = None @@ -125,17 +124,17 @@ def fetch_chunk(self, key, chunk_coords): ) if f is None: raise DataAccessError( - "Cannot find chunk {0} in {1}" .format( - self._flat_chunk_basename(key, chunk_coords), - self.base_path) + "Cannot find chunk " + f"{self._flat_chunk_basename(key, chunk_coords)} in " + f"{self.base_path}" ) with f: return f.read() except OSError as exc: raise DataAccessError( - "Error accessing chunk {0} in {1}: {2}" .format( - self._flat_chunk_basename(key, chunk_coords), - self.base_path, exc)) from exc + "Error accessing chunk " + f"{self._flat_chunk_basename(key, chunk_coords)} in " + f"{self.base_path}: {exc}" ) from exc def store_chunk(self, buf, key, chunk_coords, mime_type="application/octet-stream", @@ -154,9 +153,9 @@ def store_chunk(self, buf, key, chunk_coords, f.write(buf) except OSError as exc: raise DataAccessError( - "Error storing chunk {0} in {1}: {2}" .format( - self._flat_chunk_basename(key, chunk_coords), - self.base_path, exc)) from exc + "Error storing chunk " + f"{self._flat_chunk_basename(key, chunk_coords)} in " + f"{self.base_path}: {exc}" ) from exc def _chunk_path(self, key, chunk_coords, pattern=None): if pattern is None: diff --git a/src/neuroglancer_scripts/http_accessor.py b/src/neuroglancer_scripts/http_accessor.py index 0813e9f..da423a4 100644 --- a/src/neuroglancer_scripts/http_accessor.py +++ b/src/neuroglancer_scripts/http_accessor.py @@ -16,7 +16,6 @@ import neuroglancer_scripts.accessor from neuroglancer_scripts.accessor import _CHUNK_PATTERN_FLAT, DataAccessError - __all__ = [ "HttpAccessor", ] @@ -62,8 +61,8 @@ def file_exists(self, relative_path): return False r.raise_for_status() except requests.exceptions.RequestException as exc: - raise DataAccessError("Error probing the existence of {0}: {1}" - .format(file_url, exc)) from exc + raise DataAccessError("Error probing the existence of " + f"{file_url}: {exc}") from exc return True def fetch_file(self, relative_path): @@ -72,6 +71,5 @@ def fetch_file(self, relative_path): r = self._session.get(file_url) r.raise_for_status() except requests.exceptions.RequestException as exc: - raise DataAccessError("Error reading {0}: {1}" - .format(file_url, exc)) from exc + raise DataAccessError(f"Error reading {file_url}: {exc}") from exc return r.content diff --git a/src/neuroglancer_scripts/mesh.py b/src/neuroglancer_scripts/mesh.py index f9bb464..3f693b3 100644 --- a/src/neuroglancer_scripts/mesh.py +++ b/src/neuroglancer_scripts/mesh.py @@ -25,7 +25,6 @@ import neuroglancer_scripts - __all__ = [ "InvalidMeshDataError", "save_mesh_as_neuroglancer_vtk", @@ -85,23 +84,22 @@ def save_mesh_as_neuroglancer_vtk(file, vertices, triangles, file.write("# vtk DataFile Version 3.0\n") if title: title += ". " - title += "Written by neuroglancer-scripts-{0}.".format( - neuroglancer_scripts.__version__ - ) - file.write("{0}\n".format(title[:255])) + title += ("Written by neuroglancer-scripts-" + f"{neuroglancer_scripts.__version__}.") + file.write(f"{title[:255]}\n") file.write("ASCII\n") file.write("DATASET POLYDATA\n") - file.write("POINTS {0:d} {1}\n".format(vertices.shape[0], "float")) + file.write("POINTS {:d} {}\n".format(vertices.shape[0], "float")) if not np.can_cast(vertices.dtype, np.float32): # As of a8ce681660864ab3ac7c1086c0b4262e40f24707 Neuroglancer reads # everything as float32 anyway logger.warning("Vertex coordinates will be converted to float32") np.savetxt(file, vertices.astype(np.float32), fmt="%.9g") - file.write("POLYGONS {0:d} {1:d}\n" - .format(triangles.shape[0], 4 * triangles.shape[0])) + file.write(f"POLYGONS {triangles.shape[0]:d} {4 * triangles.shape[0]:d}\n" + ) np.savetxt(file, np.insert(triangles, 0, 3, axis=1), fmt="%d") if vertex_attributes: - file.write("POINT_DATA {0:d}\n".format(vertices.shape[0])) + file.write(f"POINT_DATA {vertices.shape[0]:d}\n") for vertex_attribute in vertex_attributes: name = vertex_attribute["name"] assert re.match("\\s", name) is None @@ -118,12 +116,12 @@ def save_mesh_as_neuroglancer_vtk(file, vertices, triangles, if not np.can_cast(values.dtype, np.float32): # As of a8ce681660864ab3ac7c1086c0b4262e40f24707 Neuroglancer # reads everything as float32 anyway - logger.warning("Data for the '{0}' vertex attribute will be " - "converted to float32".format(name)) - file.write("SCALARS {0} {1}".format(name, "float")) + logger.warning(f"Data for the '{name}' vertex attribute will " + "be converted to float32") + file.write("SCALARS {} {}".format(name, "float")) if num_components != 1: - file.write(" {0:d}".format(num_components)) - file.write("\nLOOKUP_TABLE {0}\n".format("default")) + file.write(f" {num_components:d}") + file.write("\nLOOKUP_TABLE {}\n".format("default")) np.savetxt(file, values.astype(np.float32), fmt="%.9g") diff --git a/src/neuroglancer_scripts/precomputed_io.py b/src/neuroglancer_scripts/precomputed_io.py index b049604..daf8bf6 100644 --- a/src/neuroglancer_scripts/precomputed_io.py +++ b/src/neuroglancer_scripts/precomputed_io.py @@ -15,7 +15,6 @@ from neuroglancer_scripts import chunk_encoding from neuroglancer_scripts.chunk_encoding import InvalidInfoError - __all__ = [ "get_IO_for_existing_dataset", "get_IO_for_new_dataset", diff --git a/src/neuroglancer_scripts/scripts/compute_scales.py b/src/neuroglancer_scripts/scripts/compute_scales.py index c5d38e9..0b002b4 100755 --- a/src/neuroglancer_scripts/scripts/compute_scales.py +++ b/src/neuroglancer_scripts/scripts/compute_scales.py @@ -10,8 +10,8 @@ import neuroglancer_scripts.accessor import neuroglancer_scripts.chunk_encoding -import neuroglancer_scripts.dyadic_pyramid import neuroglancer_scripts.downscaling +import neuroglancer_scripts.dyadic_pyramid from neuroglancer_scripts import precomputed_io diff --git a/src/neuroglancer_scripts/scripts/convert_chunks.py b/src/neuroglancer_scripts/scripts/convert_chunks.py index c1567d6..c0f0947 100755 --- a/src/neuroglancer_scripts/scripts/convert_chunks.py +++ b/src/neuroglancer_scripts/scripts/convert_chunks.py @@ -13,9 +13,7 @@ import neuroglancer_scripts.accessor import neuroglancer_scripts.chunk_encoding -from neuroglancer_scripts import data_types -from neuroglancer_scripts import precomputed_io - +from neuroglancer_scripts import data_types, precomputed_io logger = logging.getLogger(__name__) @@ -31,7 +29,7 @@ def convert_chunks_for_scale(chunk_reader, if chunk_reader.scale_is_lossy(key): logger.warning("Using data stored in a lossy format as an input for " - "conversion (for scale %s)" % key) + "conversion (for scale %s)", key) for chunk_size in scale_info["chunk_sizes"]: chunk_range = ((size[0] - 1) // chunk_size[0] + 1, @@ -40,7 +38,7 @@ def convert_chunks_for_scale(chunk_reader, for x_idx, y_idx, z_idx in tqdm( np.ndindex(chunk_range), total=np.prod(chunk_range), unit="chunk", - desc="converting scale {}".format(key)): + desc=f"converting scale {key}"): xmin = chunk_size[0] * x_idx xmax = min(chunk_size[0] * (x_idx + 1), size[0]) ymin = chunk_size[1] * y_idx diff --git a/src/neuroglancer_scripts/scripts/generate_scales_info.py b/src/neuroglancer_scripts/scripts/generate_scales_info.py index 76dc6f9..7ea0194 100755 --- a/src/neuroglancer_scripts/scripts/generate_scales_info.py +++ b/src/neuroglancer_scripts/scripts/generate_scales_info.py @@ -11,9 +11,8 @@ import sys import neuroglancer_scripts.accessor -from neuroglancer_scripts import data_types -from neuroglancer_scripts import precomputed_io import neuroglancer_scripts.dyadic_pyramid +from neuroglancer_scripts import data_types, precomputed_io logger = logging.getLogger(__name__) diff --git a/src/neuroglancer_scripts/scripts/link_mesh_fragments.py b/src/neuroglancer_scripts/scripts/link_mesh_fragments.py index 9def66d..7b1c89d 100644 --- a/src/neuroglancer_scripts/scripts/link_mesh_fragments.py +++ b/src/neuroglancer_scripts/scripts/link_mesh_fragments.py @@ -14,7 +14,6 @@ import neuroglancer_scripts.accessor import neuroglancer_scripts.precomputed_io as precomputed_io - logger = logging.getLogger(__name__) diff --git a/src/neuroglancer_scripts/scripts/mesh_to_precomputed.py b/src/neuroglancer_scripts/scripts/mesh_to_precomputed.py index 6e06561..2dcd6ea 100755 --- a/src/neuroglancer_scripts/scripts/mesh_to_precomputed.py +++ b/src/neuroglancer_scripts/scripts/mesh_to_precomputed.py @@ -17,7 +17,6 @@ import neuroglancer_scripts.mesh import neuroglancer_scripts.precomputed_io as precomputed_io - logger = logging.getLogger(__name__) @@ -125,15 +124,15 @@ def parse_command_line(argv): try: matrix = np.fromstring(args.coord_transform, sep=",") except ValueError as exc: - parser.error("cannot parse --coord-transform: {}" - .format(exc.args[0])) + parser.error(f"cannot parse --coord-transform: {exc.args[0]}" + ) if len(matrix) == 12: matrix = matrix.reshape(3, 4) elif len(matrix) == 16: matrix = matrix.reshape(4, 4) else: parser.error("--coord-transform must have 12 or 16 elements" - " ({} passed)".format(len(matrix))) + f" ({len(matrix)} passed)") args.coord_transform = matrix diff --git a/src/neuroglancer_scripts/scripts/scale_stats.py b/src/neuroglancer_scripts/scripts/scale_stats.py index 87fa516..54d2974 100755 --- a/src/neuroglancer_scripts/scripts/scale_stats.py +++ b/src/neuroglancer_scripts/scripts/scale_stats.py @@ -42,18 +42,15 @@ def show_scales_info(info): if sharding_num_directories is not None else size_in_chunks[0] * (1 + size_in_chunks[1])) size_bytes = np.prod(size) * dtype.itemsize * num_channels - print("Scale {}, {}, chunk size {}:" - " {:,d} chunks, {:,d} directories, raw uncompressed size {}B" - .format(scale_name, shard_info, chunk_size, - num_chunks, num_directories, - readable_count(size_bytes))) + print(f"Scale {scale_name}, {shard_info}, chunk size {chunk_size}:" + f" {num_chunks:,d} chunks, {num_directories:,d} directories," + f" raw uncompressed size {readable_count(size_bytes)}B") total_size += size_bytes total_chunks += num_chunks total_directories += num_directories print("---") - print("Total: {:,d} chunks, {:,d} directories, raw uncompressed size {}B" - .format(total_chunks, total_directories, - readable_count(total_size))) + print(f"Total: {total_chunks:,d} chunks, {total_directories:,d} " + f"directories, raw uncompressed size {readable_count(total_size)}B") def show_scale_file_info(url, options={}): diff --git a/src/neuroglancer_scripts/scripts/slices_to_precomputed.py b/src/neuroglancer_scripts/scripts/slices_to_precomputed.py index 296dace..6f3f6d6 100755 --- a/src/neuroglancer_scripts/scripts/slices_to_precomputed.py +++ b/src/neuroglancer_scripts/scripts/slices_to_precomputed.py @@ -5,8 +5,8 @@ # # This software is made available under the MIT licence, see LICENCE.txt. -from pathlib import Path import sys +from pathlib import Path import numpy as np import skimage.io @@ -14,11 +14,13 @@ import neuroglancer_scripts.accessor import neuroglancer_scripts.chunk_encoding -from neuroglancer_scripts.data_types import get_chunk_dtype_transformer from neuroglancer_scripts import precomputed_io -from neuroglancer_scripts.utils import (permute, invert_permutation, - readable_count) - +from neuroglancer_scripts.data_types import get_chunk_dtype_transformer +from neuroglancer_scripts.utils import ( + invert_permutation, + permute, + readable_count, +) # Generated with the following Python expression: # >>> from itertools import * @@ -104,8 +106,8 @@ def slices_to_raw_chunks(slice_filename_lists, dest_url, input_orientation, for filename_list in slice_filename_lists: if len(filename_list) != input_size[2]: - raise ValueError("{} slices found where {} were expected" - .format(len(filename_list), input_size[2])) + raise ValueError(f"{len(filename_list)} slices found where " + f"{input_size[2]} were expected") for slice_chunk_idx in trange((input_size[2] - 1) // input_chunk_size[2] + 1, @@ -124,7 +126,7 @@ def slices_to_raw_chunks(slice_filename_lists, dest_url, input_orientation, slice_slicing = np.s_[first_slice : last_slice : input_axis_inversions[2]] - tqdm.write("Reading slices {0} to {1} ({2}B memory needed)... " + tqdm.write("Reading slices {} to {} ({}B memory needed)... " .format(first_slice, last_slice - input_axis_inversions[2], readable_count(input_size[0] * input_size[1] @@ -150,8 +152,8 @@ def load_z_stack(slice_filenames): block = block[np.newaxis, :, :, :] else: raise ValueError( - "block has unexpected dimensionality (ndim={})" - .format(block.ndim) + f"block has unexpected dimensionality (ndim={block.ndim})" + ) return block diff --git a/src/neuroglancer_scripts/scripts/volume_to_precomputed_pyramid.py b/src/neuroglancer_scripts/scripts/volume_to_precomputed_pyramid.py index c85aac6..0e17334 100644 --- a/src/neuroglancer_scripts/scripts/volume_to_precomputed_pyramid.py +++ b/src/neuroglancer_scripts/scripts/volume_to_precomputed_pyramid.py @@ -16,9 +16,7 @@ import neuroglancer_scripts.downscaling import neuroglancer_scripts.dyadic_pyramid import neuroglancer_scripts.scripts.generate_scales_info -from neuroglancer_scripts import precomputed_io -from neuroglancer_scripts import volume_reader - +from neuroglancer_scripts import precomputed_io, volume_reader logger = logging.getLogger(__name__) @@ -58,7 +56,7 @@ def volume_to_precomputed_pyramid(volume_filename, info, accessor ) except neuroglancer_scripts.accessor.DataAccessError as exc: - logger.error("Cannot write info: {0}".format(exc)) + logger.error(f"Cannot write info: {exc}") return 1 volume_reader.nibabel_image_to_precomputed( img, precomputed_writer, diff --git a/src/neuroglancer_scripts/sharded_base.py b/src/neuroglancer_scripts/sharded_base.py index 0a32eac..19fde62 100644 --- a/src/neuroglancer_scripts/sharded_base.py +++ b/src/neuroglancer_scripts/sharded_base.py @@ -7,11 +7,12 @@ # BSD 3-clause licence by Copyright (c) 2017, Ignacio Tartavull, William # Silversmith, and later authors (see below). -from typing import List, Dict, Any -from abc import ABC, abstractmethod import math -import numpy as np import zlib +from abc import ABC, abstractmethod +from typing import Any, Dict, List + +import numpy as np _MAX_UINT64 = 0xffffffffffffffff @@ -419,7 +420,7 @@ def info(self, val): def get_scale(self, key) -> Dict[str, Any]: scales = self.info.get("scales") try: - scale, = [scale for scale in scales if scale.get("key") == key] + scale, = (scale for scale in scales if scale.get("key") == key) return scale except ValueError as e: raise ValueError(f"key {key!r} not found in scales. Possible " diff --git a/src/neuroglancer_scripts/sharded_file_accessor.py b/src/neuroglancer_scripts/sharded_file_accessor.py index 443db92..f77c323 100644 --- a/src/neuroglancer_scripts/sharded_file_accessor.py +++ b/src/neuroglancer_scripts/sharded_file_accessor.py @@ -9,24 +9,25 @@ API. """ -from typing import Iterator +import json +import pathlib +import struct +from tempfile import TemporaryDirectory +from typing import Any, Dict, Iterator, List, Union +from uuid import uuid4 + +import numpy as np + import neuroglancer_scripts.accessor from neuroglancer_scripts.sharded_base import ( - ShardSpec, CMCReadWrite, - ShardedScaleBase, + ShardCMC, ShardedAccessorBase, - ShardVolumeSpec, ShardedIOError, - ShardCMC, + ShardedScaleBase, + ShardSpec, + ShardVolumeSpec, ) -import pathlib -import numpy as np -from typing import Dict, List, Union, Any -import struct -import json -from tempfile import TemporaryDirectory -from uuid import uuid4 class OnDiskBytesDict(dict): @@ -421,7 +422,7 @@ def fetch_file(self, relative_path): def store_file(self, relative_path, buf, overwrite=False, **kwargs): if not overwrite and self.file_exists(relative_path): - raise IOError(f"file at {relative_path} already exists") + raise OSError(f"file at {relative_path} already exists") with open(self.base_dir / relative_path, "wb") as fp: fp.write(buf) diff --git a/src/neuroglancer_scripts/sharded_http_accessor.py b/src/neuroglancer_scripts/sharded_http_accessor.py index 9a5de50..72a383c 100644 --- a/src/neuroglancer_scripts/sharded_http_accessor.py +++ b/src/neuroglancer_scripts/sharded_http_accessor.py @@ -3,21 +3,22 @@ # # This software is made available under the MIT licence, see LICENCE.txt. +import json from typing import Dict + import numpy as np -import json +import requests +import neuroglancer_scripts.http_accessor from neuroglancer_scripts.sharded_base import ( - ShardSpec, CMCReadWrite, - ShardedScaleBase, + ShardCMC, ShardedAccessorBase, - ShardVolumeSpec, ShardedIOError, - ShardCMC, + ShardedScaleBase, + ShardSpec, + ShardVolumeSpec, ) -import neuroglancer_scripts.http_accessor -import requests class HttpShard(ShardCMC): diff --git a/src/neuroglancer_scripts/transform.py b/src/neuroglancer_scripts/transform.py index 4a88d95..b51a89a 100644 --- a/src/neuroglancer_scripts/transform.py +++ b/src/neuroglancer_scripts/transform.py @@ -7,7 +7,6 @@ import numpy as np - __all__ = [ "nifti_to_neuroglancer_transform", "matrix_as_compact_urlsafe_json", diff --git a/src/neuroglancer_scripts/utils.py b/src/neuroglancer_scripts/utils.py index 0d88eb1..bb77eca 100644 --- a/src/neuroglancer_scripts/utils.py +++ b/src/neuroglancer_scripts/utils.py @@ -11,7 +11,6 @@ import numpy as np - __all__ = [ "ceil_div", "permute", @@ -93,7 +92,7 @@ def readable_count(count): return num_str + " " + prefix # Fallback: use the last prefix factor, prefix = _IEC_PREFIXES[-1] - return "{:,.0f} {}".format(count / factor, prefix) + return f"{count / factor:,.0f} {prefix}" LENGTH_UNITS = collections.OrderedDict([ diff --git a/src/neuroglancer_scripts/volume_reader.py b/src/neuroglancer_scripts/volume_reader.py index 40a611d..9e9a9ae 100644 --- a/src/neuroglancer_scripts/volume_reader.py +++ b/src/neuroglancer_scripts/volume_reader.py @@ -6,16 +6,16 @@ import json import logging -import numpy as np import nibabel import nibabel.orientations +import numpy as np from tqdm import tqdm import neuroglancer_scripts.accessor -from neuroglancer_scripts.accessor import DataAccessError import neuroglancer_scripts.data_types -from neuroglancer_scripts import precomputed_io import neuroglancer_scripts.transform +from neuroglancer_scripts import precomputed_io +from neuroglancer_scripts.accessor import DataAccessError from neuroglancer_scripts.sharded_base import ShardSpec __all__ = [ @@ -111,23 +111,20 @@ def nibabel_image_to_info(img, guessed_dtype = input_dtype.name else: guessed_dtype = "float32" - formatted_info = """\ + formatted_info = f"""\ {{ "type": "image", - "num_channels": {num_channels}, - "data_type": "{data_type}", + "num_channels": {shape[3] if len(shape) >= 4 else 1}, + "data_type": "{guessed_dtype}", "scales": [ {{ "encoding": "raw", - "size": {size}, - "resolution": {resolution}, + "size": {list(shape[:3])}, + "resolution": {[float(vs * 1_000_000) for vs in voxel_sizes[:3]]}, "voxel_offset": [0, 0, 0] }} ] -}}""".format(num_channels=shape[3] if len(shape) >= 4 else 1, - data_type=guessed_dtype, - size=list(shape[:3]), - resolution=[float(vs * 1_000_000) for vs in voxel_sizes[:3]]) +}}""" info = json.loads(formatted_info) # ensure well-formed JSON diff --git a/tox.ini b/tox.ini index 8cd3d01..9383619 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ # NOTE: remember to update the classifiers in setup.cfg when Python versions # are added/removed [tox] -envlist = py36, py37, py38, py39, py310, py311, codestyle, docs, cov +envlist = py36, py37, py38, py39, py310, py311, py312, codestyle, docs, cov isolated_build = True [gh-actions] @@ -43,17 +43,6 @@ commands = pre-commit run --all-files deps = pre-commit -[flake8] -ignore = - # E203 (whitespace before ':') has false positive for array slicings - E203, - # these are on the default ignore list - E121, E126, E226, E133, E203, E241, - # We want line-break *before* the operator (new PEP8 style similar to math) - W503, - # Gives false positives when a name contains an uppercase acronym - N802 - [pytest] filterwarnings = default diff --git a/unit_tests/test_accessor.py b/unit_tests/test_accessor.py index 4b85f8f..abcba1f 100644 --- a/unit_tests/test_accessor.py +++ b/unit_tests/test_accessor.py @@ -5,19 +5,18 @@ # This software is made available under the MIT licence, see LICENCE.txt. import argparse -import pathlib import json - -import pytest +import pathlib from unittest.mock import patch +import pytest from neuroglancer_scripts.accessor import ( - get_accessor_for_url, - add_argparse_options, - convert_file_url_to_pathname, Accessor, - URLError, DataAccessError, + URLError, + add_argparse_options, + convert_file_url_to_pathname, + get_accessor_for_url, ) from neuroglancer_scripts.file_accessor import FileAccessor from neuroglancer_scripts.http_accessor import HttpAccessor diff --git a/unit_tests/test_chunk_encoding.py b/unit_tests/test_chunk_encoding.py index 0659593..1bc6e3e 100644 --- a/unit_tests/test_chunk_encoding.py +++ b/unit_tests/test_chunk_encoding.py @@ -7,16 +7,15 @@ import numpy as np import pytest - from neuroglancer_scripts.chunk_encoding import ( - get_encoder, - add_argparse_options, CompressedSegmentationEncoder, - JpegChunkEncoder, - RawChunkEncoder, + IncompatibleEncoderError, InvalidFormatError, InvalidInfoError, - IncompatibleEncoderError, + JpegChunkEncoder, + RawChunkEncoder, + add_argparse_options, + get_encoder, ) diff --git a/unit_tests/test_data_types.py b/unit_tests/test_data_types.py index 345da8d..38c2eef 100644 --- a/unit_tests/test_data_types.py +++ b/unit_tests/test_data_types.py @@ -5,7 +5,6 @@ import numpy as np import pytest - from neuroglancer_scripts.data_types import ( NG_DATA_TYPES, NG_INTEGER_DATA_TYPES, @@ -136,9 +135,8 @@ def test_dtype_conversion_integer_downcasting(dtype): def test_unsupported_tupled_dtype(): - - random_val = np.random.rand(81).reshape((3, 3, 3, 3)) * 255 - random_val = random_val.astype(np.uint8) + rng = np.random.default_rng() + random_val = rng.integers(256, size=(3, 3, 3, 3), dtype=np.uint8) wrong_type = np.dtype([('A', 'u1'), ('B', 'u1'), ('C', 'u1')]) new_data = random_val.copy().view(dtype=wrong_type).reshape((3, 3, 3)) @@ -147,8 +145,8 @@ def test_unsupported_tupled_dtype(): def test_supported_tupled_dtype(): - random_val = np.random.rand(81).reshape((3, 3, 3, 3)) * 255 - random_val = random_val.astype(np.uint8) + rng = np.random.default_rng() + random_val = rng.integers(256, size=(3, 3, 3, 3), dtype=np.uint8) right_type = np.dtype([('R', 'u1'), ('G', 'u1'), ('B', 'u1')]) new_data = random_val.copy().view(dtype=right_type).reshape((3, 3, 3)) dtype, isrgb = get_dtype(new_data.dtype) diff --git a/unit_tests/test_downscaling.py b/unit_tests/test_downscaling.py index 805c36d..ebdf441 100644 --- a/unit_tests/test_downscaling.py +++ b/unit_tests/test_downscaling.py @@ -5,14 +5,13 @@ import numpy as np import pytest - from neuroglancer_scripts.downscaling import ( - get_downscaler, - add_argparse_options, + AveragingDownscaler, Downscaler, + MajorityDownscaler, StridingDownscaler, - AveragingDownscaler, - MajorityDownscaler + add_argparse_options, + get_downscaler, ) diff --git a/unit_tests/test_file_accessor.py b/unit_tests/test_file_accessor.py index 6949a3f..c5d5ce2 100644 --- a/unit_tests/test_file_accessor.py +++ b/unit_tests/test_file_accessor.py @@ -6,11 +6,10 @@ import pathlib import pytest - -from neuroglancer_scripts.file_accessor import FileAccessor from neuroglancer_scripts.accessor import ( DataAccessError, ) +from neuroglancer_scripts.file_accessor import FileAccessor @pytest.mark.parametrize("compresslevel", [0, 1, 9]) diff --git a/unit_tests/test_http_accessor.py b/unit_tests/test_http_accessor.py index 0bc094f..43b5de5 100644 --- a/unit_tests/test_http_accessor.py +++ b/unit_tests/test_http_accessor.py @@ -7,11 +7,10 @@ import pytest import requests - -from neuroglancer_scripts.http_accessor import HttpAccessor from neuroglancer_scripts.accessor import ( DataAccessError, ) +from neuroglancer_scripts.http_accessor import HttpAccessor @pytest.mark.parametrize("base_url", [ diff --git a/unit_tests/test_mesh.py b/unit_tests/test_mesh.py index e6796f5..0a6f339 100644 --- a/unit_tests/test_mesh.py +++ b/unit_tests/test_mesh.py @@ -7,11 +7,10 @@ import io import numpy as np - from neuroglancer_scripts.mesh import ( - save_mesh_as_precomputed, read_precomputed_mesh, save_mesh_as_neuroglancer_vtk, + save_mesh_as_precomputed, ) diff --git a/unit_tests/test_precomputed_io.py b/unit_tests/test_precomputed_io.py index 6c5f1a4..e31b92b 100644 --- a/unit_tests/test_precomputed_io.py +++ b/unit_tests/test_precomputed_io.py @@ -5,7 +5,6 @@ import numpy as np import pytest - from neuroglancer_scripts.accessor import get_accessor_for_url from neuroglancer_scripts.chunk_encoding import InvalidInfoError from neuroglancer_scripts.precomputed_io import ( @@ -13,7 +12,6 @@ get_IO_for_new_dataset, ) - DUMMY_INFO = { "type": "image", "data_type": "uint16", diff --git a/unit_tests/test_sharded_base.py b/unit_tests/test_sharded_base.py index e2afb70..32a0d98 100644 --- a/unit_tests/test_sharded_base.py +++ b/unit_tests/test_sharded_base.py @@ -3,18 +3,23 @@ # # This software is made available under the MIT licence, see LICENCE.txt. -import pytest -from collections import namedtuple -import numpy as np import zlib -from unittest.mock import patch, MagicMock +from collections import namedtuple +from unittest.mock import MagicMock, patch +import numpy as np +import pytest from neuroglancer_scripts.sharded_base import ( - ShardVolumeSpec, ShardSpec, CMCReadWrite, ShardedIOError, - ReadableMiniShardCMC, ShardedScaleBase, ShardedAccessorBase, ShardCMC + CMCReadWrite, + ReadableMiniShardCMC, + ShardCMC, + ShardedAccessorBase, + ShardedIOError, + ShardedScaleBase, + ShardSpec, + ShardVolumeSpec, ) - ShardSpecArg = namedtuple("ShardSpecArg", ["minishard_bits", "shard_bits", "hash", "minishard_index_encoding", @@ -512,7 +517,6 @@ def test_store_cmc_chunk(mocked_get_shard_base): base, shard_spec, svs = mocked_get_shard_base shard_magic_mock = MagicMock() - get_shard_key_mock = MagicMock() with patch.object(base, "get_shard_key", return_value=shard_key) as get_shard_key_mock: with patch.object(base, "get_shard", @@ -540,7 +544,6 @@ def test_shard_fetch_cmc_chunk(mocked_get_shard_base): base, shard_spec, svs = mocked_get_shard_base shard_magic_mock = MagicMock() - get_shard_key_mock = MagicMock() with patch.object(base, "get_shard_key", return_value=shard_key) as get_shard_key_mock: with patch.object(base, "get_shard", diff --git a/unit_tests/test_sharded_file_accessor.py b/unit_tests/test_sharded_file_accessor.py index e068619..3a849b0 100644 --- a/unit_tests/test_sharded_file_accessor.py +++ b/unit_tests/test_sharded_file_accessor.py @@ -3,23 +3,24 @@ # # This software is made available under the MIT licence, see LICENCE.txt. +import json +import pathlib +from unittest.mock import MagicMock, PropertyMock, call, patch + +import numpy as np import pytest -from unittest.mock import patch, MagicMock, call, PropertyMock from neuroglancer_scripts.sharded_file_accessor import ( InMemByteArray, + MiniShard, OnDiskByteArray, OnDiskBytesDict, - ShardSpec, - MiniShard, - ShardedIOError, Shard, + ShardedFileAccessor, + ShardedIOError, ShardedScale, + ShardSpec, ShardVolumeSpec, - ShardedFileAccessor, ) -import numpy as np -import pathlib -import json def test_ondisk_bytes_dict(): diff --git a/unit_tests/test_sharded_http_accessor.py b/unit_tests/test_sharded_http_accessor.py index 77faf39..4d55e36 100644 --- a/unit_tests/test_sharded_http_accessor.py +++ b/unit_tests/test_sharded_http_accessor.py @@ -3,20 +3,19 @@ # # This software is made available under the MIT licence, see LICENCE.txt. -import pytest from unittest.mock import MagicMock, patch -import numpy as np - -from requests import Session, exceptions as re_exc +import numpy as np +import pytest from neuroglancer_scripts.sharded_http_accessor import ( HttpShard, - ShardSpec, HttpShardedScale, - ShardVolumeSpec, ShardedHttpAccessor, + ShardSpec, + ShardVolumeSpec, ) - +from requests import Session +from requests import exceptions as re_exc hdr_len = int(2 ** 2 * 16) diff --git a/unit_tests/test_transform.py b/unit_tests/test_transform.py index dc88dd5..dff3ade 100644 --- a/unit_tests/test_transform.py +++ b/unit_tests/test_transform.py @@ -4,10 +4,9 @@ # This software is made available under the MIT licence, see LICENCE.txt. import numpy as np - from neuroglancer_scripts.transform import ( - nifti_to_neuroglancer_transform, matrix_as_compact_urlsafe_json, + nifti_to_neuroglancer_transform, ) diff --git a/unit_tests/test_utils.py b/unit_tests/test_utils.py index 60eb530..87ecb72 100644 --- a/unit_tests/test_utils.py +++ b/unit_tests/test_utils.py @@ -5,11 +5,10 @@ import numpy as np import pytest - from neuroglancer_scripts.utils import ( ceil_div, - permute, invert_permutation, + permute, readable_count, ) diff --git a/unit_tests/test_volume_reader.py b/unit_tests/test_volume_reader.py index a37f593..06e1d2c 100644 --- a/unit_tests/test_volume_reader.py +++ b/unit_tests/test_volume_reader.py @@ -1,22 +1,23 @@ +import json +from unittest.mock import patch + import nibabel as nib import numpy as np import pytest -import json -from unittest.mock import patch -from neuroglancer_scripts.volume_reader import nibabel_image_to_info, \ - volume_file_to_precomputed +from neuroglancer_scripts.volume_reader import ( + nibabel_image_to_info, + volume_file_to_precomputed, +) def prepare_nifti_images(): - - random_rgb_val = np.random.rand(81).reshape((3, 3, 3, 3)) * 255 - random_rgb_val = random_rgb_val.astype(np.uint8) + rng = np.random.default_rng() + random_rgb_val = rng.integers(256, size=(3, 3, 3, 3), dtype=np.uint8) right_type = np.dtype([("R", "u1"), ("G", "u1"), ("B", "u1")]) new_data = random_rgb_val.copy().view(dtype=right_type).reshape((3, 3, 3)) rgb_img = nib.Nifti1Image(new_data, np.eye(4)) - random_uint8_val = np.random.rand(27).reshape((3, 3, 3)) * 255 - random_uint8_val = random_uint8_val.astype(np.uint8) + random_uint8_val = rng.integers(256, size=(3, 3, 3), dtype=np.uint8) uint8_img = nib.Nifti1Image(random_uint8_val, np.eye(4)) return [(rgb_img, 3), (uint8_img, 1)]