Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-cmapfile for openSUSE:Factory 
checked in at 2023-01-28 18:43:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-cmapfile (Old)
 and      /work/SRC/openSUSE:Factory/.python-cmapfile.new.32243 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-cmapfile"

Sat Jan 28 18:43:03 2023 rev:3 rq:1061495 version:2022.9.29

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-cmapfile/python-cmapfile.changes  
2021-02-15 23:20:12.583746148 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-cmapfile.new.32243/python-cmapfile.changes   
    2023-01-28 19:00:56.763929500 +0100
@@ -1,0 +2,19 @@
+Thu Jan 26 22:55:59 UTC 2023 - Ben Greiner <[email protected]>
+
+- Clean specfile
+- Unify buildtime and runtime requirements: Use the setup.py
+  specification, not the loose recommendation from the README
+
+-------------------------------------------------------------------
+Fri Jan 13 09:42:39 UTC 2023 - Dirk Müller <[email protected]>
+
+- update to 2022.9.29:
+  * Make subsampling compatible with ChimeraX (breaking).
+  * Fix deprecated import of scipy.ndimage.interpolation.zoom.
+  * Switch to Google style docstrings.
+  * Add type hints.
+  * Drop support for Python 3.7 and numpy < 1.19 (NEP29).
+  * Fix LSM conversion with tifffile >= 2021.2.26.
+  * Remove support for Python 3.6 (NEP 29).
+
+-------------------------------------------------------------------

Old:
----
  cmapfile-2020.1.1.tar.gz

New:
----
  cmapfile-2022.9.29.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-cmapfile.spec ++++++
--- /var/tmp/diff_new_pack.ZMPQxp/_old  2023-01-28 19:00:57.163931706 +0100
+++ /var/tmp/diff_new_pack.ZMPQxp/_new  2023-01-28 19:00:57.167931728 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-cmapfile
 #
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,33 +16,33 @@
 #
 
 
-%define skip_python2 1
-# NEP 29: TW python36-numpy -scipy and -tifffile are no more
-%define skip_python36 1
-%define packagename cmapfile
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-cmapfile
-Version:        2020.1.1
+Version:        2022.9.29
 Release:        0
 Summary:        Write Chimera Map (CMAP) files
 License:        BSD-3-Clause
 Group:          Development/Languages/Python
 URL:            https://www.lfd.uci.edu/~gohlke/
-Source:         
https://github.com/cgohlke/cmapfile/archive/v%{version}.tar.gz#/%{packagename}-%{version}.tar.gz
-BuildRequires:  %{python_module h5py >= 2.9}
-BuildRequires:  %{python_module numpy >= 1.14.5}
-BuildRequires:  %{python_module scipy >= 1.2}
+# SourceRepository: https://github.com/cgohlke/cmapfile
+Source:         
https://github.com/cgohlke/cmapfile/archive/v%{version}.tar.gz#/cmapfile-%{version}.tar.gz
+BuildRequires:  %{python_module base >= 3.8}
+BuildRequires:  %{python_module h5py >= 3.1}
+BuildRequires:  %{python_module numpy >= 1.19.2}
+BuildRequires:  %{python_module oiffile >= 2021.6.6}
+BuildRequires:  %{python_module pip}
+BuildRequires:  %{python_module scipy >= 1.5}
 BuildRequires:  %{python_module setuptools}
-BuildRequires:  %{python_module tifffile >= 2019.1.1}
+BuildRequires:  %{python_module tifffile >= 2021.11.2}
+BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
-Requires:       python-h5py >= 2.9
-Requires:       python-numpy >= 1.14.5
-Requires:       python-oiffile >= 2020.1.1
-Requires:       python-scipy >= 1.2
-Requires:       python-tifffile >= 2019.1.1
+Requires:       python-h5py >= 3.1
+Requires:       python-numpy >= 1.19.2
+Requires:       python-oiffile >= 2021.6.6
+Requires:       python-scipy >= 1.5
+Requires:       python-tifffile >= 2021.11.2
 Requires(post): update-alternatives
-Requires(postun): update-alternatives
+Requires(postun):update-alternatives
 BuildArch:      noarch
 %python_subpackages
 
@@ -50,36 +50,32 @@
 Create Chimera MAP files from various file formats containing volume data.
 
 %prep
-%setup -q -n %{packagename}-%{version}
+%setup -q -n cmapfile-%{version}
 # Fix warning wrong-file-end-of-line-encoding
 sed -i 's/\r//' README.rst
 
 %build
-%python_build
+%pyproject_wheel
 
 %install
-%python_install
-for p in %{packagename} ; do
-    %python_clone -a %{buildroot}%{_bindir}/$p
-done
-
+%pyproject_install
+%python_clone -a %{buildroot}%{_bindir}/cmapfile
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
-%prepare_alternative %{packagename}
 
 %post
-%python_install_alternative %{packagename}
+%python_install_alternative cmapfile
 
 %postun
-%python_uninstall_alternative %{packagename}
+%python_uninstall_alternative cmapfile
 
 %check
-# Test not provided
+# No tests by upstream
 
 %files %{python_files}
 %doc README.rst
 %license LICENSE
-%python_alternative %{_bindir}/%{packagename}
-%{python_sitelib}/*egg-info/
-%{python_sitelib}/%{packagename}/
+%python_alternative %{_bindir}/cmapfile
+%{python_sitelib}/cmapfile
+%{python_sitelib}/cmapfile-%{version}.dist-info
 
 %changelog

++++++ cmapfile-2020.1.1.tar.gz -> cmapfile-2022.9.29.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cmapfile-2020.1.1/LICENSE 
new/cmapfile-2022.9.29/LICENSE
--- old/cmapfile-2020.1.1/LICENSE       2020-01-18 10:05:10.000000000 +0100
+++ new/cmapfile-2022.9.29/LICENSE      2022-09-30 06:20:32.000000000 +0200
@@ -1,6 +1,6 @@
 BSD 3-Clause License
 
-Copyright (c) 2014-2020, Christoph Gohlke
+Copyright (c) 2014-2022, Christoph Gohlke
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cmapfile-2020.1.1/README.rst 
new/cmapfile-2022.9.29/README.rst
--- old/cmapfile-2020.1.1/README.rst    2020-01-18 10:05:10.000000000 +0100
+++ new/cmapfile-2022.9.29/README.rst   2022-09-30 06:20:32.000000000 +0200
@@ -5,35 +5,32 @@
 files, HDF5 files containing series of 3D XYZ datasets.
 
 CMAP files can be created from numpy arrays and various file formats
-containing volume data, e.g. BIN, TIFF, LSM, OIF, and OIB.
+containing volume data, e.g., BIN, TIFF, LSM, OIF, and OIB.
 
-CMAP files can be visualized using UCSF Chimera [2], a highly extensible
-program for interactive visualization and analysis of molecular structures
-and related data.
-
-For command line usage run ``python -m cmapfile --help``
-
-:Author:
-  `Christoph Gohlke <https://www.lfd.uci.edu/~gohlke/>`_
-
-:Organization:
-  Laboratory for Fluorescence Dynamics. University of California, Irvine
+CMAP files can be visualized using UCSF Chimera [2], a program for interactive
+visualization and analysis of molecular structures and related data.
 
+:Author: `Christoph Gohlke <https://www.cgohlke.com>`_
 :License: BSD 3-Clause
-
-:Version: 2020.1.1
+:Version: 2022.9.29
 
 Requirements
 ------------
-* `CPython >= 3.6 <https://www.python.org>`_
-* `Numpy 1.14 <https://www.numpy.org>`_
-* `Scipy 1.1 <https://www.scipy.org>`_
-* `H5py 2.10 <https://www.h5py.org/>`_
-* `Tifffile 2019.1.1 <https://pypi.org/project/tifffile/>`_
-* `Oiffile 2020.1.1 <https://pypi.org/project/oiffile/>`_
+
+This release has been tested with the following requirements and dependencies
+(other versions may work):
+
+- `CPython 3.8.10, 3.9.13, 3.10.7, 3.11.0rc2 <https://www.python.org>`_
+  (32-bit platforms are deprecated)
+- `Numpy 1.21.5 <https://pypi.org/project/numpy/>`_
+- `Scipy 1.8.1 <https://pypi.org/project/scipy/>`_
+- `H5py 3.7.0 <https://pypi.org/project/h5py/>`_
+- `Tifffile 2022.8.12 <https://pypi.org/project/tifffile/>`_  (optional)
+- `Oiffile 2022.2.2 <https://pypi.org/project/oiffile />`_ (optional)
 
 References
 ----------
+
 1. Thomas Goddard. [Chimera-users] reading in hdf5 files in chimera.
    https://www.cgl.ucsf.edu/pipermail/chimera-users/2008-September/003052.html
 2. UCSF Chimera, an extensible molecular modeling system.
@@ -42,12 +39,17 @@
 
 Examples
 --------
+
+Print the command line usage::
+
+    python -m cmapfile --help
+
 Convert a 5D LSM file to CMAP file::
 
     python -m cmapfile "/my data directory/large.lsm"
 
 Convert all BIN files in the current directory to test.cmap. The BIN files
-are known to contain 128x128x64 samples of 16 bit integers. The CMAP file
+are known to contain 128x128x64 samples of 16-bit integers. The CMAP file
 will store float32 maps using subsampling up to 16::
 
     python -m cmapfile --shape 128,128,64 --step 1,1,2 --dtype i2
@@ -59,6 +61,7 @@
 
 Notes
 -----
+
 The CMAP file format according to [1]::
 
     Example of HDF format written by Chimera (Chimera map format) follows.
@@ -72,20 +75,44 @@
      cell_angles (90.0, 90.0, 90.0) (attribute)
      rotation_axis (0.0, 0.0, 1.0) (attribute)
      rotation_angle 45.0 (attribute, degrees)
+     color (1.0, 1.0, 0, 1.0) (attribute, rgba 0-1 float)
+     time 5 (attribute, time series frame number)
+     channel 0 (attribute, integer for multichannel data)
      /data (3d array of uint8 (123,542,82)) (dataset, any name allowed)
      /data_x (3d array of uint8 (123,542,82), alternate chunk shape) (dataset)
      /data_2 (3d array of uint8 (61,271,41)) (dataset, any name allowed)
         subsample_spacing (2, 2, 2) (attribute)
      (more subsampled or alternate chunkshape versions of same data)
 
-
 Revisions
 ---------
+
+2022.9.29
+
+- Make subsampling compatible with ChimeraX (breaking).
+- Fix deprecated import of scipy.ndimage.interpolation.zoom.
+- Switch to Google style docstrings.
+
+2022.2.2
+
+- Add type hints.
+- Drop support for Python 3.7 and numpy < 1.19 (NEP29).
+
+2021.2.26
+
+- Fix LSM conversion with tifffile >= 2021.2.26.
+- Remove support for Python 3.6 (NEP 29).
+
 2020.1.1
-    Do not write name attribute.
-    Remove support for Python 2.7 and 3.5.
-    Update copyright.
+
+- Do not write name attribute.
+- Remove support for Python 2.7 and 3.5.
+- Update copyright.
+
 2018.8.30
-    Move cmapfile.py into cmapfile package.
+
+- Move cmapfile.py into cmapfile package.
+
 2014.10.10
-    Initial release.
+
+- Initial release.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cmapfile-2020.1.1/cmapfile/cmapfile.py 
new/cmapfile-2022.9.29/cmapfile/cmapfile.py
--- old/cmapfile-2020.1.1/cmapfile/cmapfile.py  2020-01-18 10:05:10.000000000 
+0100
+++ new/cmapfile-2022.9.29/cmapfile/cmapfile.py 2022-09-30 06:20:32.000000000 
+0200
@@ -1,6 +1,6 @@
 # cmapfile.py
 
-# Copyright (c) 2014-2020, Christoph Gohlke
+# Copyright (c) 2014-2022, Christoph Gohlke
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -35,35 +35,32 @@
 files, HDF5 files containing series of 3D XYZ datasets.
 
 CMAP files can be created from numpy arrays and various file formats
-containing volume data, e.g. BIN, TIFF, LSM, OIF, and OIB.
+containing volume data, e.g., BIN, TIFF, LSM, OIF, and OIB.
 
-CMAP files can be visualized using UCSF Chimera [2], a highly extensible
-program for interactive visualization and analysis of molecular structures
-and related data.
-
-For command line usage run ``python -m cmapfile --help``
-
-:Author:
-  `Christoph Gohlke <https://www.lfd.uci.edu/~gohlke/>`_
-
-:Organization:
-  Laboratory for Fluorescence Dynamics. University of California, Irvine
+CMAP files can be visualized using UCSF Chimera [2], a program for interactive
+visualization and analysis of molecular structures and related data.
 
+:Author: `Christoph Gohlke <https://www.cgohlke.com>`_
 :License: BSD 3-Clause
-
-:Version: 2020.1.1
+:Version: 2022.9.29
 
 Requirements
 ------------
-* `CPython >= 3.6 <https://www.python.org>`_
-* `Numpy 1.14 <https://www.numpy.org>`_
-* `Scipy 1.1 <https://www.scipy.org>`_
-* `H5py 2.10 <https://www.h5py.org/>`_
-* `Tifffile 2019.1.1 <https://pypi.org/project/tifffile/>`_
-* `Oiffile 2020.1.1 <https://pypi.org/project/oiffile/>`_
+
+This release has been tested with the following requirements and dependencies
+(other versions may work):
+
+- `CPython 3.8.10, 3.9.13, 3.10.7, 3.11.0rc2 <https://www.python.org>`_
+  (32-bit platforms are deprecated)
+- `Numpy 1.21.5 <https://pypi.org/project/numpy/>`_
+- `Scipy 1.8.1 <https://pypi.org/project/scipy/>`_
+- `H5py 3.7.0 <https://pypi.org/project/h5py/>`_
+- `Tifffile 2022.8.12 <https://pypi.org/project/tifffile/>`_  (optional)
+- `Oiffile 2022.2.2 <https://pypi.org/project/oiffile />`_ (optional)
 
 References
 ----------
+
 1. Thomas Goddard. [Chimera-users] reading in hdf5 files in chimera.
    https://www.cgl.ucsf.edu/pipermail/chimera-users/2008-September/003052.html
 2. UCSF Chimera, an extensible molecular modeling system.
@@ -72,12 +69,17 @@
 
 Examples
 --------
+
+Print the command line usage::
+
+    python -m cmapfile --help
+
 Convert a 5D LSM file to CMAP file::
 
     python -m cmapfile "/my data directory/large.lsm"
 
 Convert all BIN files in the current directory to test.cmap. The BIN files
-are known to contain 128x128x64 samples of 16 bit integers. The CMAP file
+are known to contain 128x128x64 samples of 16-bit integers. The CMAP file
 will store float32 maps using subsampling up to 16::
 
     python -m cmapfile --shape 128,128,64 --step 1,1,2 --dtype i2
@@ -89,6 +91,7 @@
 
 Notes
 -----
+
 The CMAP file format according to [1]::
 
     Example of HDF format written by Chimera (Chimera map format) follows.
@@ -102,31 +105,62 @@
      cell_angles (90.0, 90.0, 90.0) (attribute)
      rotation_axis (0.0, 0.0, 1.0) (attribute)
      rotation_angle 45.0 (attribute, degrees)
+     color (1.0, 1.0, 0, 1.0) (attribute, rgba 0-1 float)
+     time 5 (attribute, time series frame number)
+     channel 0 (attribute, integer for multichannel data)
      /data (3d array of uint8 (123,542,82)) (dataset, any name allowed)
      /data_x (3d array of uint8 (123,542,82), alternate chunk shape) (dataset)
      /data_2 (3d array of uint8 (61,271,41)) (dataset, any name allowed)
         subsample_spacing (2, 2, 2) (attribute)
      (more subsampled or alternate chunkshape versions of same data)
 
-
 Revisions
 ---------
+
+2022.9.29
+
+- Make subsampling compatible with ChimeraX (breaking).
+- Fix deprecated import of scipy.ndimage.interpolation.zoom.
+- Switch to Google style docstrings.
+
+2022.2.2
+
+- Add type hints.
+- Drop support for Python 3.7 and numpy < 1.19 (NEP29).
+
+2021.2.26
+
+- Fix LSM conversion with tifffile >= 2021.2.26.
+- Remove support for Python 3.6 (NEP 29).
+
 2020.1.1
-    Do not write name attribute.
-    Remove support for Python 2.7 and 3.5.
-    Update copyright.
+
+- Do not write name attribute.
+- Remove support for Python 2.7 and 3.5.
+- Update copyright.
+
 2018.8.30
-    Move cmapfile.py into cmapfile package.
+
+- Move cmapfile.py into cmapfile package.
+
 2014.10.10
-    Initial release.
+
+- Initial release.
 
 """
 
-__version__ = '2020.1.1'
+from __future__ import annotations
+
+__version__ = '2022.9.29'
 
-__all__ = (
-    'CmapFile', 'bin2cmap', 'tif2cmap', 'lsm2cmap', 'oif2cmap', 'array2cmap'
-)
+__all__ = [
+    'CmapFile',
+    'bin2cmap',
+    'tif2cmap',
+    'lsm2cmap',
+    'oif2cmap',
+    'array2cmap',
+]
 
 import sys
 import os
@@ -137,75 +171,120 @@
 import h5py
 
 try:
-    from ndimage.interpolation import zoom
+    from scipy.ndimage import zoom
 except ImportError:
-    from scipy.ndimage.interpolation import zoom
+    from ndimage import zoom
 
 from tifffile import TiffFile, transpose_axes, natural_sorted, product
 from oiffile import OifFile
 
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from typing import Any, Union, Sequence, Iterator
+
+    try:
+        from numpy.typing import ArrayLike
+    except ImportError:
+        # numpy < 1.20
+        from numpy import ndarray as ArrayLike
+
+    PathLike = Union[str, os.PathLike]
+
 
 class CmapFile(h5py.File):
-    """Write Chimera MAP formatted HDF5 file."""
+    """Write Chimera MAP formatted HDF5 file.
 
-    def __init__(self, filename, mode='w', **kwargs):
-        """Create new HDF5 file object.
+    Parameters:
+        filename:
+            Name of file to write.
+        mode:
+            File open mode.
+        **kwargs:
+            Arguments passed to `h5py.File`.
 
-        See h5py.File for parameters.
+    """
 
-        """
+    mapcounter: int
+
+    def __init__(
+        self, filename: PathLike, /, mode: str = 'w', **kwargs
+    ) -> None:
         h5py.File.__init__(self, name=filename, mode=mode, **kwargs)
         self.mapcounter = 0
 
-    def addmap(self, data, name=None, step=None, origin=None,
-               cell_angles=None, rotation_axis=None, rotation_angle=None,
-               symmetries=None, astype=None, subsample=16, chunks=True,
-               compression=None, verbose=False):
+    def addmap(
+        self,
+        data: ArrayLike,
+        /,
+        *,
+        name: str | None = None,
+        step: Sequence[float] | None = None,
+        origin: Sequence[float] | None = None,
+        cell_angles: Sequence[float] | None = None,
+        rotation_axis: Sequence[float] | None = None,
+        rotation_angle: float | None = None,
+        color: Sequence[float] | None = None,
+        time: int | None = None,
+        channel: int | None = None,
+        symmetries=None,
+        astype: numpy.dtype = None,
+        subsample: int = 16,
+        chunks: bool = True,
+        compression: str | None = None,
+        verbose: bool = False,
+    ) -> None:
         """Create HDF5 group and datasets according to CMAP format.
 
-        The order of axes is XYZ for 'step', 'origin', 'cell_angles', and
-        'rotation_axis'. Data 'shape' and 'chunks' sizes are in ZYX order.
+        The order of axes is XYZ for `step`, `origin`, `cell_angles`, and
+        `rotation_axis`. Data shape and `chunks` sizes are in ZYX order.
 
-        Parameters
-        ----------
-        data : array_like
-            Map data to store. Must be three dimensional.
-        name : str, optional
-            Name of map.
-        step : sequence of 3 float, optional
-            Spacing between samples in XYZ dimensions.
-            Chimera defaults to (1.0, 1.0, 1.0)
-        origin : sequence of 3 float, optional
-            Chimera defaults to (0.0, 0.0, 0.0)
-        cell_angles : sequence of 3 float, optional
-            Chimera defaults to (90.0, 90.0, 90.0)
-        rotation_axis : sequence of 3 float, optional
-            Axis to rotate around. Chimera defaults to (0.0, 0.0, 1.0)
-        rotation_angle : float, optional
-            Extent to rotate around rotation_axis. Chimera defaults to 0.0.
-        symmetries : None, optional
-            Undocumented.
-        astype : numpy dtype, optional
-            Datatype of HDF dataset, e.g. 'float32'.
-            By default this is data.dtype.
-        subsample : int, optional
-            Store subsampled datasets up to the specified number (default: 16).
-        chunks : bool or sequence of 3 int, optional
-            Size of chunks to store in datasets in ZYX order.
-            By default HDF5 determines this.
-        compression : str, optional
-            Type of HDF5 data compression, e.g. None (default) or 'gzip'.
-        verbose : bool, optional
-            If False (default), do not print messages to stdout.
+        Parameters:
+            data:
+                Map data to store. Must be three dimensional.
+            name:
+                Name of map.
+            step:
+                Spacing between samples in XYZ dimensions.
+                Chimera defaults to (1.0, 1.0, 1.0).
+            origin:
+                Origin in XYZ dimensions.
+                Chimera defaults to (0.0, 0.0, 0.0).
+            cell_angles:
+                Chimera defaults to (90.0, 90.0, 90.0).
+            rotation_axis:
+                Axis to rotate around. Chimera defaults to (0.0, 0.0, 1.0).
+            rotation_angle:
+                Extent to rotate around rotation_axis. Chimera defaults to 0.0.
+            color:
+                RGBA color values, e.g. (1.0, 1.0, 0, 1.0).
+            time:
+                Time series frame number.
+            channel:
+                Multi-channel index.
+            symmetries:
+                Undocumented.
+            astype:
+                Datatype of HDF dataset, e.g. 'float32'.
+                By default, this is data.dtype.
+            subsample:
+                Store subsampled datasets up to specified number.
+            chunks:
+                Size of chunks to store in datasets in ZYX order.
+                By default, the chunk size is determined by HDF5.
+            compression:
+                Type of HDF5 data compression, e.g. None or 'gzip'.
+            verbose:
+                Print messages to stdout.
 
         """
-        data = numpy.atleast_3d(data)
-        if data.ndim != 3:
-            raise ValueError('map data must be 3 dimensional')
         if astype:
             data = numpy.ascontiguousarray(data, astype)
         else:
             data = numpy.ascontiguousarray(data)
+        if data.ndim != 3:
+            raise ValueError('map data must be 3 dimensional')
+
         # create group and write attributes
         group = self.create_group(f'map{self.mapcounter:05d}')
 
@@ -226,32 +305,42 @@
             group.attrs['rotation_axis'] = rotation_axis
         if rotation_angle:
             group.attrs['rotation_angle'] = rotation_angle
+        if color:
+            group.attrs['color'] = color
+        if time:
+            group.attrs['time'] = time
+        if channel:
+            group.attrs['channel'] = channel
         if symmetries:
             group.attrs['symmetries'] = symmetries
         # create main dataset
         if verbose:
             print('1 ', end='', flush=True)
-        dset = group.create_dataset(f'data{self.mapcounter:05d}',
-                                    data=data, chunks=chunks,
-                                    compression=compression)
+        dset = group.create_dataset(
+            f'data{self.mapcounter:05d}',
+            data=data,
+            chunks=chunks,
+            compression=compression,
+        )
         # create subsampled datasets
         for i, data in enumerate(subsamples(data, int(subsample))):
-            sample = 2**(i + 1)
+            sample = 2 ** (i + 1)
             if verbose:
                 print(f'{sample} ', end='', flush=True)
             dset = group.create_dataset(
                 f'data{self.mapcounter:05d}_{i + 2}',
-                data=data, chunks=chunks, compression=compression)
+                data=data,
+                chunks=chunks,
+                compression=compression,
+            )
             dset.attrs['subsample_spacing'] = sample, sample, sample
         self.mapcounter += 1
 
-    def setstep(self, step):
-        """Set 'step' attribute on all datasets.
+    def setstep(self, step: Sequence[float], /) -> None:
+        """Set `step` attribute on all datasets.
 
-        Parameters
-        ----------
-        step : sequence of 3 float
-            Spacing between samples in XYZ dimensions.
+        Parameters:
+            step: Spacing between samples in XYZ dimensions.
 
         """
         for name, group in self.items():
@@ -259,38 +348,43 @@
                 group.attrs.modify('step', step)
 
 
-def bin2cmap(binfiles, shape, dtype, offset=0, cmapfile=None, fail=True,
-             **kwargs):
+def bin2cmap(
+    binfiles: Sequence[PathLike] | str,
+    /,
+    shape: tuple[int, ...],
+    dtype: numpy.dtype,
+    offset: int = 0,
+    cmapfile: PathLike | None = None,
+    fail: bool = True,
+    **kwargs,
+) -> None:
     r"""Convert series of SimFCS BIN files to Chimera MAP file.
 
     SimFCS BIN files contain homogeneous data of any type and shape,
     stored C-contiguously in little endian order.
-    A common format is: shape=(-1, 256, 256), dtype='uint16'.
+    A common format is `shape=(-1, 256, 256), dtype='uint16'`.
 
     TODO: Support generic strides, storage order, and byteorder.
 
-    Parameters
-    ----------
-    binfiles : str or sequence of str
-        List of BIN file names or file pattern, e.g. '\*.bin'
-    shape : tuple of 3 int
-        Shape of data in BIN files in ZYX order, e.g. (32, 256, 256).
-    dtype : numpy dtype
-        Type of data in the BIN files, e.g. 'uint16'.
-    offset : int, optional
-        Number of bytes to skip at beginning of BIN file (default: 0).
-    cmapfile : str, optional
-        Name of the output CMAP file. If None (default), the name is
-        derived from the first BIN file.
-    fail : bool, optional
-        If True (default), raise error when reading invalid BIN files.
-    kwargs : dict, optional
-        Additional parameters passed to the CmapFile.addmap function,
-        e.g. verbose, step, origin, cell_angles, rotation_axis,
-        rotation_angle, subsample, chunks, and compression.
+    Parameters:
+        binfiles:
+            List of BIN file names or file pattern, e.g. '\*.bin'
+        shape:
+            Shape of data in BIN files in ZYX order, e.g. (32, 256, 256).
+        dtype:
+            Type of data in BIN files, e.g. 'uint16'.
+        offset:
+            Number of bytes to skip at beginning of BIN file.
+        cmapfile:
+            Name of output CMAP file.
+            If None, the name is derived from the first BIN file.
+        fail:
+            Raise error when reading invalid BIN files.
+        **kwargs:
+            Arguments passed to :py:meth:`CmapFile.addmap`.
 
     """
-    binfiles = parse_files(binfiles)
+    binfiles_list = parse_files(binfiles)
     validate_shape(shape, 3)
     shape = tuple(shape)
     dtype = numpy.dtype(dtype)
@@ -298,12 +392,12 @@
     if count < 0:
         count = -1
     if not cmapfile:
-        cmapfile = binfiles[0] + '.cmap'
+        cmapfile = os.fspath(binfiles_list[0]) + '.cmap'
     verbose = kwargs.get('verbose', False)
     if verbose:
         print(f"Creating '{cmapfile}'", flush=True)
     with CmapFile(cmapfile, 'w') as cmap:
-        for binfile in binfiles:
+        for binfile in binfiles_list:
             if verbose:
                 print('+', os.path.basename(binfile), end=' ', flush=True)
             try:
@@ -323,34 +417,37 @@
                 print(flush=True)
 
 
-def tif2cmap(tiffiles, cmapfile=None, fail=True, **kwargs):
+def tif2cmap(
+    tiffiles: Sequence[PathLike],
+    /,
+    cmapfile: PathLike | None = None,
+    fail: bool = True,
+    **kwargs,
+) -> None:
     r"""Convert series of 3D TIFF files to Chimera MAP file.
 
-    Parameters
-    ----------
-    tiffiles : str or sequence of str
-        List of TIFF file names or file pattern, e.g. '\*.tif'.
-        Files must contain 3D data of matching shape and dtype.
-    cmapfile : str, optional
-        Name of the output CMAP file. If None (default), the name is
-        derived from the first TIFF file.
-    fail : bool, optional
-        If True (default), raise error when processing incompatible TIFF files.
-    kwargs : dict, optional
-        Additional parameters passed to the CmapFile.addmap function,
-        e.g. verbose, step, origin, cell_angles, rotation_axis,
-        rotation_angle, subsample, chunks, and compression.
+    Parameters:
+        tiffiles:
+            TIFF file names or file pattern, e.g. '\*.tif'.
+            Files must contain 3D data of matching shape and dtype.
+        cmapfile:
+            Name of output CMAP file.
+            By default, the name is derived from the first TIFF file.
+        fail:
+            Raise error when processing incompatible TIFF files.
+        **kwargs:
+            Arguments passed to :py:meth:`CmapFile.addmap`.
 
     """
-    tiffiles = parse_files(tiffiles)
+    tiffiles_list = parse_files(tiffiles)
     if not cmapfile:
-        cmapfile = tiffiles[0] + '.cmap'
+        cmapfile = os.fspath(tiffiles_list[0]) + '.cmap'
     verbose = kwargs.get('verbose', False)
     if verbose:
         print(f"Creating '{cmapfile}'", flush=True)
     shape = dtype = None
     with CmapFile(cmapfile, 'w') as cmap:
-        for tiffile in tiffiles:
+        for tiffile in tiffiles_list:
             if verbose:
                 print('+', os.path.basename(tiffile), end=' ', flush=True)
             try:
@@ -375,20 +472,19 @@
                 print(flush=True)
 
 
-def lsm2cmap(lsmfile, cmapfile=None, **kwargs):
+def lsm2cmap(
+    lsmfile: PathLike, /, cmapfile: PathLike | None = None, **kwargs
+) -> None:
     """Convert 5D TZCYX LSM file to Chimera MAP files, one per channel.
 
-    Parameters
-    ----------
-    lsmfile : str
-        Name of the LSM file to convert.
-    cmapfile : str, optional
-        Name of the output CMAP file. If None (default), the name is
-        derived from lsmfile.
-    kwargs : dict, optional
-        Additional parameters passed to the CmapFile.addmap function,
-        e.g. verbose, step, origin, cell_angles, rotation_axis,
-        rotation_angle, subsample, chunks, and compression.
+    Parameters:
+        lsmfile:
+            Name of LSM file to convert.
+        cmapfile:
+            Name of output CMAP file.
+            If None, the name is derived from `lsmfile`.
+        **kwargs:
+            Arguments passed to :py:meth:`CmapFile.addmap`.
 
     """
     verbose = kwargs.get('verbose', False)
@@ -398,19 +494,27 @@
         # open LSM file
         lsm = TiffFile(lsmfile)
         series = lsm.series[0]  # first series contains the image data
-        if series.axes != 'TZCYX':
-            raise ValueError(
-                f'not a 5D LSM file (expected TZCYX, got {series.axes})'
-            )
+        if hasattr(series, 'get_shape'):
+            # tifffile > 2020.2.25 return squeezed shape and axes
+            shape = series.get_shape(False)
+            axes = series.get_axes(False)
+            if axes[:2] == 'MP' and shape[:2] == (1, 1):
+                axes = axes[2:]
+                shape = shape[2:]
+        else:
+            shape = series.shape
+            axes = series.axes
+        if axes != 'TZCYX':
+            raise ValueError(f'not a 5D LSM file (expected TZCYX, got {axes})')
         if verbose:
             print(lsm)
-            print(series.shape, series.axes, flush=True)
+            print(shape, axes, flush=True)
         # create one CMAP file per channel
         if cmapfile:
             cmapfile = '{}.ch%04d{}'.format(*os.path.splitext(cmapfile))
         else:
             cmapfile = f'{lsmfile}.ch%04d.cmap'
-        cmaps = [CmapFile(cmapfile % i) for i in range(series.shape[2])]
+        cmaps = [CmapFile(cmapfile % i) for i in range(shape[2])]
         # voxel/step sizes
         if not kwargs.get('step', None):
             try:
@@ -418,19 +522,20 @@
                 kwargs['step'] = (
                     attrs['voxel_size_x'] / attrs['voxel_size_x'],
                     attrs['voxel_size_y'] / attrs['voxel_size_x'],
-                    attrs['voxel_size_z'] / attrs['voxel_size_x'])
+                    attrs['voxel_size_z'] / attrs['voxel_size_x'],
+                )
             except Exception:
                 pass
         # iterate over Tiff pages containing data
         pages = iter(series.pages)
-        for _ in range(series.shape[0]):  # iterate over time axis
-            data = []
-            for _ in range(series.shape[1]):  # iterate over z slices
-                data.append(next(pages).asarray())
-            data = numpy.vstack(data).reshape(series.shape[1:])
-            for c in range(series.shape[2]):  # iterate over channels
+        for t in range(shape[0]):  # iterate over time axis
+            datalist = []
+            for _ in range(shape[1]):  # iterate over z slices
+                datalist.append(next(pages).asarray())
+            data = numpy.vstack(datalist).reshape(shape[1:])
+            for c in range(shape[2]):  # iterate over channels
                 # write datasets and attributes
-                cmaps[c].addmap(data=data[:, c], **kwargs)
+                cmaps[c].addmap(data[:, c], time=t, **kwargs)
     finally:
         if lsm:
             lsm.close()
@@ -438,22 +543,21 @@
             f.close()
 
 
-def array2cmap(data, axes, cmapfile, **kwargs):
+def array2cmap(
+    data: numpy.ndarray, /, axes: str, cmapfile: PathLike, **kwargs
+) -> None:
     """Save numpy ndarray to Chimera MAP files, one per channel.
 
-    Parameters
-    ----------
-    data : ndarray
-        Three to 5 dimensional array.
-    axes : str
-        Specifies type and order of axes in data array.
-        May contain only 'CTZYX'.
-    cmapfile : str
-        Name of the output CMAP file.
-    kwargs : dict, optional
-        Additional parameters passed to the CmapFile.addmap function,
-        e.g. verbose, step, origin, cell_angles, rotation_axis,
-        rotation_angle, subsample, chunks, and compression.
+    Parameters:
+        data:
+            Three to 5 dimensional array.
+        axes:
+            Specifies type and order of axes in data array.
+            May contain only 'CTZYX'.
+        cmapfile:
+            Name of output CMAP file.
+        **kwargs:
+            Arguments passed to :py:meth:`CmapFile.addmap`.
 
     """
     if len(data.shape) != len(axes):
@@ -462,47 +566,45 @@
     try:
         # create one CMAP file per channel
         cmaps = []
+        cmapfile = os.fspath(cmapfile)
         if cmapfile.lower().endswith('.cmap'):
             cmapfile = cmapfile[:-5]
         if data.shape[0] > 1:
-            cmaps = [CmapFile(f'{cmapfile}.ch{i:04d}.cmap')
-                     for i in range(data.shape[0])]
+            cmaps = [
+                CmapFile(f'{cmapfile}.ch{i:04d}.cmap')
+                for i in range(data.shape[0])
+            ]
         else:
             cmaps = [CmapFile(f'{cmapfile}.cmap')]
         # iterate over data and write cmaps
         for c in range(data.shape[0]):  # channels
             for t in range(data.shape[1]):  # times
-                cmaps[c].addmap(data=data[c, t], **kwargs)
+                cmaps[c].addmap(data[c, t], time=t, **kwargs)
     finally:
         for f in cmaps:
             f.close()
 
 
-def oif2cmap(oiffile, cmapfile=None, **kwargs):
+def oif2cmap(
+    oiffile: PathLike, /, cmapfile: PathLike = None, **kwargs
+) -> None:
     """Convert OIF or OIB files to Chimera MAP files, one per channel.
 
-    Parameters
-    ----------
-    oiffile : str
-        Name of the OIF or OIB file to convert.
-    cmapfile : str, optional
-        Name of the output CMAP file. If None (default), the name is
-        derived from oiffile.
-    kwargs : dict, optional
-        Additional parameters passed to the CmapFile.addmap function,
-        e.g. verbose, step, origin, cell_angles, rotation_axis,
-        rotation_angle, subsample, chunks, and compression.
+    Parameters:
+        oiffile:
+            Name of OIF or OIB file to convert.
+        cmapfile:
+            Name of output CMAP file.
+            By default, the name is derived from `oiffile`.
+        **kwargs:
+            Arguments passed to :py:meth:`CmapFile.addmap`.
 
     """
     verbose = kwargs.get('verbose', False)
     with OifFile(oiffile) as oif:
         if verbose:
             print(oif)
-        try:
-            tiffs = oif.series[0]
-        except Exception:
-            # oiffile < 2020.1.1
-            tiffs = oif.tiffs
+        tiffs = oif.series[0]
         data = tiffs.asarray()
         axes = tiffs.axes + 'YX'
         if verbose:
@@ -516,7 +618,8 @@
                 kwargs['step'] = (
                     1.0,
                     (size['Y'] / (shape[-2] - 1)) / xsize,
-                    (size['Z'] / (shape[axes.index('Z')] - 1)) / xsize)
+                    (size['Z'] / (shape[axes.index('Z')] - 1)) / xsize,
+                )
             except Exception:
                 pass
     if cmapfile is None:
@@ -524,8 +627,13 @@
     array2cmap(data, axes, cmapfile, **kwargs)
 
 
-def oif_axis_size(oifsettings):
-    """Return dict of axes sizes from OIF main settings."""
+def oif_axis_size(oifsettings: dict[str, Any], /) -> dict[str, Any]:
+    """Return mapping of axes sizes from OIF main settings.
+
+    Parameters:
+        oifsettings: OIF main settings.
+
+    """
     scale = {'nm': 1000.0, 'ms': 1000.0}
     result = {}
     i = 0
@@ -541,18 +649,44 @@
     return result
 
 
-def subsamples(data, maxsample=16, minshape=4):
-    """Return iterator over data zoomed by 0.5."""
+def subsamples(
+    data: numpy.ndarray, /, maxsample: int = 16, minshape: int = 4
+) -> Iterator[numpy.ndarray]:
+    """Return iterator over data zoomed by ~0.5.
+
+    Parameters:
+        data:
+            Data to be resampled.
+        maxsample:
+            Inverse of maximum zoom factor.
+        minshape:
+            Minimum size of any dimension.
+
+    """
     # TODO: use faster mipmap or gaussian pyramid generator
+    zoomed = data
+    zooms = [1.0 for size in data.shape]
     sample = 2
-    while sample <= maxsample and all(i >= minshape for i in data.shape):
-        data = zoom(data, 0.5, prefilter=False)
+    while sample <= maxsample and all(i >= minshape for i in zoomed.shape):
+        # this formula is used by ChimeraX to calculate subsample zoom factors
+        zooms = [((i + sample - 1) // sample) / i for i in data.shape]
+        zoomed = zoom(data, zooms, prefilter=False)
+        yield zoomed
         sample *= 2
-        yield data
 
 
-def validate_shape(shape, length=None):
-    """Raise ValueError if shape is not a sequence of positive integers."""
+def validate_shape(
+    shape: tuple[int, ...], /, length: int | None = None
+) -> None:
+    """Raise ValueError if shape is not a sequence of positive integers.
+
+    Parameters:
+        shape:
+            Shape to validate.
+        length:
+            Expected length of `shape`.
+
+    """
     try:
         if length is not None and len(shape) != length:
             raise ValueError()
@@ -562,8 +696,20 @@
         raise ValueError('invalid shape') from exc
 
 
-def parse_numbers(numbers, dtype=float, sep=','):
-    """Return list of numbers from string of separated numbers."""
+def parse_numbers(
+    numbers: str, /, dtype: type = float, sep: str = ','
+) -> list[Any]:
+    """Return list of numbers from string of separated numbers.
+
+    Parameters:
+        numbers:
+            Numbers of type `dtype` separated by `sep.`
+        dtype:
+            Type of numbers.
+        sep:
+            Separator used to split numbers.
+
+    """
     if not numbers:
         return []
     try:
@@ -572,10 +718,14 @@
         raise ValueError(f"not a '{sep}' separated list of numbers") from exc
 
 
-def parse_files(files):
+def parse_files(files: Sequence[PathLike], /) -> Sequence[PathLike]:
     """Return list of file names from pattern or list of file names.
 
-    Raise ValueError if no files are found.
+    Parameters:
+        files: Sequence of file names.
+
+    Raises:
+        ValueError: No files are found.
 
     """
     #    # list of files as string
@@ -583,13 +733,15 @@
     #        files = natural_sorted(
     #            match.group(1) or match.group(2)
     #            for match in re.finditer(r'(?:"([^"\t\n\r\f\v]+))"|(\S+)',
-    #                                     files))
-    try:  # list of files
-        if os.path.isfile(files[0]):
+    try:
+        # list of files
+        if isinstance(files[0], os.PathLike) or os.path.isfile(files[0]):
             return files
     except Exception:
         pass
-    try:  # glob pattern
+    try:
+        # glob pattern
+        assert isinstance(files[0], str)
         files = natural_sorted(glob.glob(files[0]))
         files[0]  # noqa: validation
         return files
@@ -597,7 +749,7 @@
         raise ValueError('no files found') from exc
 
 
-def main(argv=None):
+def main(argv: list[str] | None = None) -> int:
     """Command line usage main function."""
     if argv is None:
         argv = sys.argv
@@ -607,35 +759,68 @@
     parser = optparse.OptionParser(
         usage='usage: %prog [options] files',
         description='Convert volume data files to Chimera MAP files.',
-        version=f'%prog {__version__}', prog='cmapfile')
+        version=f'%prog {__version__}',
+        prog='cmapfile',
+    )
 
     opt = parser.add_option
     opt('-q', '--quiet', dest='verbose', action='store_false', default=True)
-    opt('--filetype', dest='filetype', default=None,
-        help='type of input file(s), e.g. BIN, LSM, OIF, TIF')
-    opt('--dtype', dest='dtype', default=None,
-        help='type of data in BIN files. e.g. uint16')
-    opt('--shape', dest='shape', default=None,
-        help='shape of data in BIN files in F order, e.g. 256,256,32')
-    opt('--offset', dest='offset', type='int', default=0,
-        help='number of bytes to skip at beginning of BIN files')
-    opt('--step', dest='step', default=None,
-        help='stepsize of data in files in F order, e.g. 1.0,1.0,8.0')
-    opt('--cmap', dest='cmap', default=None,
-        help='name of output CMAP file')
-    opt('--astype', dest='astype', default=None,
-        help='type of data in CMAP file. e.g. float32')
-    opt('--subsample', dest='subsample', type='int', default=16,
-        help='write subsampled datasets to CMAP file')
+    opt(
+        '--filetype',
+        dest='filetype',
+        default=None,
+        help='type of input file(s), e.g. BIN, LSM, OIF, TIF',
+    )
+    opt(
+        '--dtype',
+        dest='dtype',
+        default=None,
+        help='type of data in BIN files. e.g. uint16',
+    )
+    opt(
+        '--shape',
+        dest='shape',
+        default=None,
+        help='shape of data in BIN files in F order, e.g. 256,256,32',
+    )
+    opt(
+        '--offset',
+        dest='offset',
+        type='int',
+        default=0,
+        help='number of bytes to skip at beginning of BIN files',
+    )
+    opt(
+        '--step',
+        dest='step',
+        default=None,
+        help='stepsize of data in files in F order, e.g. 1.0,1.0,8.0',
+    )
+    opt('--cmap', dest='cmap', default=None, help='name of output CMAP file')
+    opt(
+        '--astype',
+        dest='astype',
+        default=None,
+        help='type of data in CMAP file. e.g. float32',
+    )
+    opt(
+        '--subsample',
+        dest='subsample',
+        type='int',
+        default=16,
+        help='write subsampled datasets to CMAP file',
+    )
 
-    options, files = parser.parse_args()
-    if not files:
+    options, filesarg = parser.parse_args()
+    if not filesarg:
         parser.error('no input files specified')
     try:
-        files = parse_files(files)
+        files = parse_files(filesarg)
+        if len(files) == 0:
+            raise ValueError
     except ValueError:
         parser.error('input file not found')
-    shape = parse_numbers(options.shape, int)
+    shape = tuple(parse_numbers(options.shape, int))
     if shape and len(shape) != 3:
         parser.error('invalid shape: expected 3 integers')
     shape = tuple(reversed(shape))  # C order
@@ -656,7 +841,8 @@
             cmapfile=options.cmap,
             astype=options.astype,
             subsample=options.subsample,
-            verbose=options.verbose)
+            verbose=options.verbose,
+        )
     elif filetype in ('OIB', 'OIF'):
         if len(files) > 1:
             warnings.warn('too many input files')
@@ -666,7 +852,8 @@
             cmapfile=options.cmap,
             astype=options.astype,
             subsample=options.subsample,
-            verbose=options.verbose)
+            verbose=options.verbose,
+        )
     elif filetype in ('TIF', 'TIFF'):
         tif2cmap(
             files,
@@ -674,13 +861,16 @@
             cmapfile=options.cmap,
             astype=options.astype,
             subsample=options.subsample,
-            verbose=options.verbose)
+            verbose=options.verbose,
+        )
     elif filetype == 'CMAP':
         if not step:
             parser.error('no step size specified for CMAP file')
         if options.verbose:
-            print(f"Changing step size in '{os.path.basename(files[0])}'",
-                  flush=True)
+            print(
+                f"Changing step size in '{os.path.basename(files[0])}'",
+                flush=True,
+            )
         with CmapFile(files[0], mode='r+') as cmap:
             cmap.setstep(step)
     elif options.dtype and options.shape:
@@ -693,7 +883,8 @@
             cmapfile=options.cmap,
             astype=options.astype,
             subsample=options.subsample,
-            verbose=options.verbose)
+            verbose=options.verbose,
+        )
     else:
         if not options.shape:
             parser.error('no data shape specified')
@@ -702,6 +893,7 @@
         parser.error(f'do not know how to convert {filetype} to CMAP')
     if options.verbose:
         print('Done.', flush=True)
+    return 0
 
 
 if __name__ == '__main__':
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cmapfile-2020.1.1/setup.py 
new/cmapfile-2022.9.29/setup.py
--- old/cmapfile-2020.1.1/setup.py      2020-01-18 10:05:10.000000000 +0100
+++ new/cmapfile-2022.9.29/setup.py     2022-09-30 06:20:32.000000000 +0200
@@ -7,21 +7,37 @@
 
 from setuptools import setup
 
+
+def search(pattern, code, flags=0):
+    # return first match for pattern in code
+    match = re.search(pattern, code, flags)
+    if match is None:
+        raise ValueError(f'{pattern!r} not found')
+    return match.groups()[0]
+
+
 with open('cmapfile/cmapfile.py') as fh:
     code = fh.read()
 
-version = re.search(r"__version__ = '(.*?)'", code).groups()[0]
+version = search(r"__version__ = '(.*?)'", code)
 
-description = re.search(r'"""(.*)\.(?:\r\n|\r|\n)', code).groups()[0]
+description = search(r'"""(.*)\.(?:\r\n|\r|\n)', code)
 
-readme = re.search(r'(?:\r\n|\r|\n){2}"""(.*)"""(?:\r\n|\r|\n){2}__version__',
-                   code, re.MULTILINE | re.DOTALL).groups()[0]
+readme = search(
+    r'(?:\r\n|\r|\n){2}"""(.*)"""(?:\r\n|\r|\n){2}[__version__|from]',
+    code,
+    re.MULTILINE | re.DOTALL,
+)
 
-readme = '\n'.join([description, '=' * len(description)] +
-                   readme.splitlines()[1:])
+readme = '\n'.join(
+    [description, '=' * len(description)] + readme.splitlines()[1:]
+)
 
-license = re.search(r'(# Copyright.*?(?:\r\n|\r|\n))(?:\r\n|\r|\n)+""', code,
-                    re.MULTILINE | re.DOTALL).groups()[0]
+license = search(
+    r'(# Copyright.*?(?:\r\n|\r|\n))(?:\r\n|\r|\n)+""',
+    code,
+    re.MULTILINE | re.DOTALL,
+)
 
 license = license.replace('# ', '').replace('#', '')
 
@@ -35,20 +51,26 @@
 setup(
     name='cmapfile',
     version=version,
+    license='BSD',
     description=description,
     long_description=readme,
     author='Christoph Gohlke',
-    author_email='[email protected]',
-    url='https://www.lfd.uci.edu/~gohlke/',
-    license='BSD',
+    author_email='[email protected]',
+    url='https://www.cgohlke.com',
+    project_urls={
+        'Bug Tracker': 'https://github.com/cgohlke/cmapfile/issues',
+        'Source Code': 'https://github.com/cgohlke/cmapfile',
+        # 'Documentation': 'https://',
+    },
     packages=['cmapfile'],
-    python_requires='>=3.6',
+    python_requires='>=3.8',
     install_requires=[
-        'numpy>=1.14.5',
-        'scipy>=1.2',
-        'h5py>=2.9',
-        'tifffile>=2019.1.1',
-        'oiffile>=2020.1.1'],
+        'numpy>=1.19.2',
+        'scipy>=1.5',
+        'h5py>=3.1',
+        'tifffile>=2021.11.2',
+        'oiffile>=2021.6.6',
+    ],
     entry_points={'console_scripts': ['cmapfile = cmapfile:main']},
     platforms=['any'],
     classifiers=[
@@ -58,8 +80,9 @@
         'Intended Audience :: Developers',
         'Operating System :: OS Independent',
         'Programming Language :: Python :: 3 :: Only',
-        'Programming Language :: Python :: 3.6',
-        'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
+        'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11',
     ],
 )

Reply via email to