Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-oiffile for openSUSE:Factory 
checked in at 2026-03-09 16:32:17
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-oiffile (Old)
 and      /work/SRC/openSUSE:Factory/.python-oiffile.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-oiffile"

Mon Mar  9 16:32:17 2026 rev:10 rq:1337647 version:2026.2.8

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-oiffile/python-oiffile.changes    
2025-12-29 15:17:25.960559761 +0100
+++ /work/SRC/openSUSE:Factory/.python-oiffile.new.8177/python-oiffile.changes  
2026-03-09 16:32:30.729151184 +0100
@@ -1,0 +2,7 @@
+Mon Mar  9 11:50:21 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 2026.2.8:
+  * Fix code review issues.
+  * Improve code quality.
+
+-------------------------------------------------------------------

Old:
----
  oiffile-2025.12.12.tar.gz

New:
----
  oiffile-2026.2.8.tar.gz

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

Other differences:
------------------
++++++ python-oiffile.spec ++++++
--- /var/tmp/diff_new_pack.Z7OU9X/_old  2026-03-09 16:32:31.885198561 +0100
+++ /var/tmp/diff_new_pack.Z7OU9X/_new  2026-03-09 16:32:31.889198725 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-oiffile
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           python-oiffile
-Version:        2025.12.12
+Version:        2026.2.8
 Release:        0
 Summary:        Read Olympus(r) image files (OIF and OIB)
 License:        BSD-3-Clause

++++++ oiffile-2025.12.12.tar.gz -> oiffile-2026.2.8.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oiffile-2025.12.12/CHANGES.rst 
new/oiffile-2026.2.8/CHANGES.rst
--- old/oiffile-2025.12.12/CHANGES.rst  1970-01-01 01:00:00.000000000 +0100
+++ new/oiffile-2026.2.8/CHANGES.rst    2026-02-09 05:42:29.000000000 +0100
@@ -0,0 +1,73 @@
+Revisions
+---------
+
+2026.2.8
+
+- Fix code review issues.
+
+2026.1.8
+
+- Improve code quality.
+
+2025.12.12
+
+- Derive OifFileError from ValueError.
+- Drop support for Python 3.10.
+
+2025.5.10
+
+- Remove doctest command line option.
+- Support Python 3.14.
+
+2025.1.1
+
+- Improve type hints.
+- Drop support for Python 3.9, support Python 3.13.
+
+2024.5.24
+
+- Support NumPy 2.
+- Fix docstring examples not correctly rendered on GitHub.
+
+2023.8.30
+
+- Fix linting issues.
+- Add py.typed marker.
+- Drop support for Python 3.8 and numpy < 1.22 (NEP29).
+
+2022.9.29
+
+- Switch to Google style docstrings.
+
+2022.2.2
+
+- Add type hints.
+- Add main function.
+- Add FileSystemAbc abstract base class.
+- Remove OifFile.tiffs (breaking).
+- Drop support for Python 3.7 and numpy < 1.19 (NEP29).
+
+2021.6.6
+
+- Fix unclosed file warnings.
+
+2020.9.18
+
+- Remove support for Python 3.6 (NEP 29).
+- Support os.PathLike file names.
+- Fix unclosed files.
+
+2020.1.18
+
+- Fix indentation error.
+
+2020.1.1
+
+- Support multiple image series.
+- Parse shape and dtype from settings file.
+- Remove support for Python 2.7 and 3.5.
+- Update copyright.
+
+2012.x.x
+
+- Initial release.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oiffile-2025.12.12/LICENSE 
new/oiffile-2026.2.8/LICENSE
--- old/oiffile-2025.12.12/LICENSE      2025-12-12 03:24:02.000000000 +0100
+++ new/oiffile-2026.2.8/LICENSE        2026-02-09 05:42:29.000000000 +0100
@@ -1,6 +1,6 @@
 BSD-3-Clause license
 
-Copyright (c) 2012-2025, Christoph Gohlke
+Copyright (c) 2012-2026, 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/oiffile-2025.12.12/MANIFEST.in 
new/oiffile-2026.2.8/MANIFEST.in
--- old/oiffile-2025.12.12/MANIFEST.in  2025-12-12 03:24:02.000000000 +0100
+++ new/oiffile-2026.2.8/MANIFEST.in    2026-02-09 05:42:29.000000000 +0100
@@ -1,5 +1,6 @@
 include LICENSE
 include README.rst
+include CHANGES.rst
 include pyproject.toml
 include test.oib
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oiffile-2025.12.12/README.rst 
new/oiffile-2026.2.8/README.rst
--- old/oiffile-2025.12.12/README.rst   2025-12-12 03:24:02.000000000 +0100
+++ new/oiffile-2026.2.8/README.rst     2026-02-09 05:42:29.000000000 +0100
@@ -19,7 +19,7 @@
 
 :Author: `Christoph Gohlke <https://www.cgohlke.com>`_
 :License: BSD-3-Clause
-:Version: 2025.12.12
+:Version: 2026.2.8
 :DOI: `10.5281/zenodo.17905223 <https://doi.org/10.5281/zenodo.17905223>`_
 
 Quickstart
@@ -45,13 +45,21 @@
 This revision was tested with the following requirements and dependencies
 (other versions may work):
 
-- `CPython <https://www.python.org>`_ 3.11.9, 3.12.10, 3.13.11 3.14.2 64-bit
-- `NumPy <https://pypi.org/project/numpy>`_ 2.3.5
-- `Tifffile <https://pypi.org/project/tifffile/>`_ 2025.10.16
+- `CPython <https://www.python.org>`_ 3.11.9, 3.12.10, 3.13.12, 3.14.3 64-bit
+- `NumPy <https://pypi.org/project/numpy>`_ 2.4.2
+- `Tifffile <https://pypi.org/project/tifffile/>`_ 2026.1.28
 
 Revisions
 ---------
 
+2026.2.8
+
+- Fix code review issues.
+
+2026.1.8
+
+- Improve code quality.
+
 2025.12.12
 
 - Derive OifFileError from ValueError.
@@ -69,47 +77,9 @@
 
 2024.5.24
 
-- Support NumPy 2.
-- Fix docstring examples not correctly rendered on GitHub.
-
-2023.8.30
-
-- Fix linting issues.
-- Add py.typed marker.
-- Drop support for Python 3.8 and numpy < 1.22 (NEP29).
-
-2022.9.29
-
-- Switch to Google style docstrings.
-
-2022.2.2
-
-- Add type hints.
-- Add main function.
-- Add FileSystemAbc abstract base class.
-- Remove OifFile.tiffs (breaking).
-- Drop support for Python 3.7 and numpy < 1.19 (NEP29).
-
-2021.6.6
-
-- Fix unclosed file warnings.
-
-2020.9.18
-
-- Remove support for Python 3.6 (NEP 29).
-- Support os.PathLike file names.
-- Fix unclosed files.
-
-2020.1.18
-
-- Fix indentation error.
-
-2020.1.1
+- …
 
-- Support multiple image series.
-- Parse shape and dtype from settings file.
-- Remove support for Python 2.7 and 3.5.
-- Update copyright.
+Refer to the CHANGES file for older revisions.
 
 Notes
 -----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oiffile-2025.12.12/oiffile/oiffile.py 
new/oiffile-2026.2.8/oiffile/oiffile.py
--- old/oiffile-2025.12.12/oiffile/oiffile.py   2025-12-12 03:24:02.000000000 
+0100
+++ new/oiffile-2026.2.8/oiffile/oiffile.py     2026-02-09 05:42:29.000000000 
+0100
@@ -1,6 +1,6 @@
 # oiffile.py
 
-# Copyright (c) 2012-2025, Christoph Gohlke
+# Copyright (c) 2012-2026, Christoph Gohlke
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -46,7 +46,7 @@
 
 :Author: `Christoph Gohlke <https://www.cgohlke.com>`_
 :License: BSD-3-Clause
-:Version: 2025.12.12
+:Version: 2026.2.8
 :DOI: `10.5281/zenodo.17905223 <https://doi.org/10.5281/zenodo.17905223>`_
 
 Quickstart
@@ -72,13 +72,21 @@
 This revision was tested with the following requirements and dependencies
 (other versions may work):
 
-- `CPython <https://www.python.org>`_ 3.11.9, 3.12.10, 3.13.11 3.14.2 64-bit
-- `NumPy <https://pypi.org/project/numpy>`_ 2.3.5
-- `Tifffile <https://pypi.org/project/tifffile/>`_ 2025.10.16
+- `CPython <https://www.python.org>`_ 3.11.9, 3.12.10, 3.13.12, 3.14.3 64-bit
+- `NumPy <https://pypi.org/project/numpy>`_ 2.4.2
+- `Tifffile <https://pypi.org/project/tifffile/>`_ 2026.1.28
 
 Revisions
 ---------
 
+2026.2.8
+
+- Fix code review issues.
+
+2026.1.8
+
+- Improve code quality.
+
 2025.12.12
 
 - Derive OifFileError from ValueError.
@@ -96,47 +104,9 @@
 
 2024.5.24
 
-- Support NumPy 2.
-- Fix docstring examples not correctly rendered on GitHub.
-
-2023.8.30
-
-- Fix linting issues.
-- Add py.typed marker.
-- Drop support for Python 3.8 and numpy < 1.22 (NEP29).
-
-2022.9.29
-
-- Switch to Google style docstrings.
-
-2022.2.2
-
-- Add type hints.
-- Add main function.
-- Add FileSystemAbc abstract base class.
-- Remove OifFile.tiffs (breaking).
-- Drop support for Python 3.7 and numpy < 1.19 (NEP29).
-
-2021.6.6
-
-- Fix unclosed file warnings.
-
-2020.9.18
+- …
 
-- Remove support for Python 3.6 (NEP 29).
-- Support os.PathLike file names.
-- Fix unclosed files.
-
-2020.1.18
-
-- Fix indentation error.
-
-2020.1.1
-
-- Support multiple image series.
-- Parse shape and dtype from settings file.
-- Remove support for Python 2.7 and 3.5.
-- Update copyright.
+Refer to the CHANGES file for older revisions.
 
 Notes
 -----
@@ -210,7 +180,7 @@
 
 from __future__ import annotations
 
-__version__ = '2025.12.12'
+__version__ = '2026.2.8'
 
 __all__ = [
     'CompoundFile',
@@ -331,8 +301,9 @@
             return self.filesystem.open_file(
                 self._files_flat.get(filename, filename)
             )
-        except (KeyError, OSError) as exc:
-            raise FileNotFoundError(f'No such file: {filename}') from exc
+        except (KeyError, OSError):
+            msg = f'no such file: {filename}'
+            raise FileNotFoundError(msg) from None
 
     def glob(self, pattern: str = '*', /) -> Iterator[str]:
         """Return iterator over unsorted file names matching pattern.
@@ -393,14 +364,14 @@
         if self._series is not None:
             return self._series
         tiffiles: dict[str, list[str]] = {}
-        for fname in self.glob('*.tif'):
+        for filename in self.glob('*.tif'):
             key = ''.join(
-                c for c in os.path.split(fname)[-1][:-4] if c.isalpha()
+                c for c in os.path.split(filename)[-1][:-4] if c.isalpha()
             )
             if key in tiffiles:
-                tiffiles[key].append(fname)
+                tiffiles[key].append(filename)
             else:
-                tiffiles[key] = [fname]
+                tiffiles[key] = [filename]
         series = tuple(
             TiffSequence(
                 natural_sorted(files), imread=self.asarray, pattern='axes'
@@ -408,7 +379,7 @@
             for files in tiffiles.values()
         )
         if len(series) > 1:
-            series = tuple(sorted(series, key=lambda x: len(x), reverse=True))
+            series = tuple(sorted(series, key=len, reverse=True))
         self._series = series
         return series
 
@@ -468,7 +439,7 @@
 
 
 class FileSystemAbc(metaclass=abc.ABCMeta):
-    """Abstract base class for structures with key."""
+    """Abstract base class for OIF and OIB file systems."""
 
     filename: str
     """Name of OIB or OIF file."""
@@ -538,9 +509,8 @@
         # check that storage directory exists
         storage = os.path.join(self._path, self.mainfile + storage_ext)
         if not os.path.exists(storage) or not os.path.isdir(storage):
-            raise OSError(
-                f'OIF storage path not found: {self.mainfile}{storage_ext}'
-            )
+            msg = f'OIF storage path not found: {self.mainfile}{storage_ext}'
+            raise OSError(msg)
         # list all files
         pathlen = len(self._path + os.path.sep)
         self._files = [self.mainfile]
@@ -637,8 +607,9 @@
         """
         try:
             return self.com.open_file(self._files[filename])
-        except KeyError as exc:
-            raise FileNotFoundError(f'No such file: {filename}') from exc
+        except KeyError:
+            msg = f'no such file: {filename}'
+            raise FileNotFoundError(msg) from None
 
     def files(self) -> Iterator[str]:
         """Return iterator over unsorted files in OIB."""
@@ -647,7 +618,7 @@
     def saveas_oif(self, location: str = '', *, verbose: int = 0) -> None:
         """Save all streams in OIB file as separate files.
 
-        Raise OSError if target files or directories already exist.
+        Raise FileExistsError if target files or directories already exist.
 
         The main .oif file name and storage names are determined from the
         OibInfo.txt settings.
@@ -656,7 +627,7 @@
             location:
                 Directory, where files are written.
             verbose:
-                Level of printed status messages.
+                Level of printed status messages (0: none, 1: dots, >1: paths).
 
         """
         if location and not os.path.exists(location):
@@ -679,8 +650,8 @@
                 print(end='.')  # noqa: T201
             elif verbose > 1:
                 print(path)  # noqa: T201
-            with open(path, 'w+b') as fh:
-                fh.write(self.open_file(f).read())
+            with self.open_file(f) as src, open(path, 'w+b') as fh:
+                fh.write(src.read())
         if verbose == 1:
             print(' done.')  # noqa: T201
 
@@ -712,15 +683,15 @@
 class SettingsFile(dict):  # type: ignore[type-arg]
     """Olympus settings file (oif, txt, pty, roi, lut).
 
-    Settings files contain little endian utf-16 encoded strings, except for
-    [ColorLUTData] sections, which contain uint8 binary arrays.
+    Settings files contain little endian UTF-16 or UTF-8 encoded strings,
+    except for [ColorLUTData] sections, which contain uint8 binary arrays.
 
     Settings can be accessed as a nested dictionary {section: {key: value}},
     except for {'ColorLUTData': numpy array}.
 
     Parameters:
         file:
-            Name of file or open file containing little endian UTF-16 string.
+            Name of file or open file containing UTF-16 or UTF-8 string.
             File objects are closed.
         name:
             Human readable label of stream.
@@ -765,7 +736,7 @@
                 self['ColorLUTData'] = (
                     numpy.frombuffer(content_list[1], dtype=numpy.uint8)
                     .copy()
-                    .reshape(-1, 4)
+                    .reshape((-1, 4))
                 )
             contents = content_list[0].decode('utf-16')
         elif content[:1] == b'[':
@@ -775,23 +746,26 @@
                 self['ColorLUTData'] = (
                     numpy.frombuffer(content_list[1], dtype=numpy.uint8)
                     .copy()
-                    .reshape(-1, 4)
+                    .reshape((-1, 4))
                 )
             try:
                 contents = content_list[0].decode()
             except Exception as exc:
-                raise ValueError('not a valid settings file') from exc
+                msg = 'not a valid settings file'
+                raise ValueError(msg) from exc
         else:
-            raise ValueError('not a valid settings file')
+            msg = 'not a valid settings file'
+            raise ValueError(msg)
 
+        properties: dict[str, Any] = {}
         for line in contents.splitlines():
             line = line.strip()  # noqa: PLW2901
-            if line.startswith(';'):
+            if not line or line.startswith(';'):
                 continue
             if line.startswith('[') and line.endswith(']'):
                 self[line[1:-1]] = properties = {}
-            else:
-                key, value = line.split('=')
+            elif '=' in line:
+                key, value = line.split('=', 1)
                 properties[key] = astype(value)
 
     def __repr__(self) -> str:
@@ -822,7 +796,7 @@
     dir_len: int
     fat_len: int
     dir_start: int
-    mini_stream_cutof_size: int
+    mini_stream_cutoff_size: int
     minifat_start: int
     minifat_len: int
     difat_start: int
@@ -855,7 +829,8 @@
     def _fromfile(self) -> None:
         """Initialize instance from file."""
         if self._fh.read(8) != b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1':
-            raise ValueError('not a compound document file')
+            msg = 'not a compound document file'
+            raise ValueError(msg)
         (
             self.clsid,
             self.version_minor,
@@ -869,7 +844,7 @@
             self.fat_len,
             self.dir_start,
             _,
-            self.mini_stream_cutof_size,
+            self.mini_stream_cutoff_size,
             self.minifat_start,
             self.minifat_len,
             self.difat_start,
@@ -881,17 +856,21 @@
         else:
             # 0xFEFF
             # self.byteorder = '>'
-            raise NotImplementedError('big-endian byte order not supported')
+            msg = 'big-endian byte order not supported'
+            raise NotImplementedError(msg)
 
         if self.clsid == b'\x00' * 16:
             self.clsid = None
         if self.clsid is not None:
-            raise OifFileError(f'cannot handle {self.clsid=!r}')
+            msg = f'cannot handle {self.clsid=!r}'
+            raise OifFileError(msg)
 
         if self.version_minor != 0x3E:
-            raise OifFileError(f'cannot handle {self.version_minor=}')
+            msg = f'cannot handle {self.version_minor=}'
+            raise OifFileError(msg)
         if mini_sector_shift != 0x0006:
-            raise OifFileError(f'cannot handle {mini_sector_shift=}')
+            msg = f'cannot handle {mini_sector_shift=}'
+            raise OifFileError(msg)
         if not (
             (self.version_major == 0x4 and sector_shift == 0x000C)
             or (
@@ -900,9 +879,8 @@
                 and self.dir_len == 0
             )
         ):
-            raise OifFileError(
-                f'cannot handle {self.version_major=} and {sector_shift=}'
-            )
+            msg = f'cannot handle {self.version_major=} and {sector_shift=}'
+            raise OifFileError(msg)
 
         self.sec_size = 2**sector_shift
         self.short_sec_size = 2**mini_sector_shift
@@ -915,7 +893,8 @@
         nextsec = self.difat_start
         for _i in range(self.difat_len):
             if nextsec >= CompoundFile.MAXREGSID:
-                raise OifFileError(f'{nextsec=} >= {CompoundFile.MAXREGSID=}')
+                msg = f'{nextsec=} >= {CompoundFile.MAXREGSID=}'
+                raise OifFileError(msg)
             sec = struct.unpack(secfmt, self._sec_read(nextsec))
             self._difat.extend(sec[:-1])
             nextsec = sec[-1]
@@ -940,16 +919,18 @@
                 )
         # read root storage
         if len(self._dirs) <= 0:
-            raise OifFileError('no directories found')
+            msg = 'no directories found'
+            raise OifFileError(msg)
         root = self._dirs[0]
         if root.name != 'Root Entry':
-            raise OifFileError(f'no root directory found, got {root.name!r}')
+            msg = f'no root directory found, got {root.name!r}'
+            raise OifFileError(msg)
         if root.create_time is not None:  # and root.modify_time is None
-            raise OifFileError(f'invalid {root.create_time=}')
+            msg = f'invalid {root.create_time=}'
+            raise OifFileError(msg)
         if root.stream_size % self.short_sec_size != 0:
-            raise OifFileError(
-                f'{root.stream_size=} does not match {self.short_sec_size=}'
-            )
+            msg = f'{root.stream_size=} does not match {self.short_sec_size=}'
+            raise OifFileError(msg)
         # read mini stream
         self._ministream = b''.join(self._sec_chain(root.sector_start))
         self._ministream = self._ministream[: root.stream_size]
@@ -979,7 +960,7 @@
 
     def _read_stream(self, direntry: DirectoryEntry, /) -> bytes:
         """Return content of stream."""
-        if direntry.stream_size < self.mini_stream_cutof_size:
+        if direntry.stream_size < self.mini_stream_cutoff_size:
             result = b''.join(self._mini_sec_chain(direntry.sector_start))
         else:
             result = b''.join(self._sec_chain(direntry.sector_start))
@@ -1067,7 +1048,7 @@
                     'dir_len',
                     'fat_len',
                     'dir_start',
-                    'mini_stream_cutof_size',
+                    'mini_stream_cutoff_size',
                     'minifat_start',
                     'minifat_len',
                     'difat_start',
@@ -1143,9 +1124,11 @@
             self.clsid = None
 
         if name_len % 2 != 0 or name_len > 64:
-            raise OifFileError(f'invalid {name_len=}')
+            msg = f'invalid {name_len=}'
+            raise OifFileError(msg)
         if self.color not in (0, 1):
-            raise OifFileError(f'invalid {self.color=}')
+            msg = f'invalid {self.color=}'
+            raise OifFileError(msg)
 
         self.name = name[: name_len - 2].decode('utf-16')
         self.create_time = filetime(create_time)
@@ -1193,7 +1176,7 @@
             result.append(f'{prefix}{bullets[1]}{k}: {v}')
         else:
             result.append((f'{prefix}{bullets[0]}{k}: {v}')[:linelen].rstrip())
-    if trim > 0:
+    if trim > 0 and result:
         result[0] = result[0][trim:]
     return '\n'.join(result)
 
@@ -1208,7 +1191,9 @@
             Possible types of value. By default, int, float, and str.
 
     """
-    if arg[0] in '\'"':
+    if not arg:
+        return arg
+    if len(arg) >= 2 and arg[0] in '\'"' and arg[-1] == arg[0]:
         return arg[1:-1]
     if types is None:
         types = int, float, str
@@ -1223,35 +1208,40 @@
 def filetime(ft: int, /) -> datetime | None:
     """Return Python datetime from Microsoft FILETIME number.
 
+    Return None if `ft` is 0 or cannot be converted.
+
     Parameters:
         ft: Microsoft FILETIME number.
 
     """
     if not ft:
         return None
-    sec, nsec = divmod(ft - 116444736000000000, 10000000)
-    return datetime.fromtimestamp(sec, timezone.utc).replace(
-        microsecond=nsec // 10
-    )
+    try:
+        sec, nsec = divmod(ft - 116444736000000000, 10000000)
+        return datetime.fromtimestamp(sec, timezone.utc).replace(
+            microsecond=nsec // 10
+        )
+    except (OSError, OverflowError, ValueError):
+        return None
 
 
 def main(argv: list[str] | None = None) -> int:
     """Oiffile command line usage main function.
 
-    ``python -m oiffile file_or_directory``
+    ``python -m oiffile oif_or_oib_file``
 
     """
     if argv is None:
         argv = sys.argv
 
     if len(argv) != 2:
-        print('Usage: python -m oiffile file_or_directory')  # noqa: T201
+        print('Usage: python -m oiffile oif_or_oib_file')  # noqa: T201
         return 0
 
     from matplotlib import pyplot
     from tifffile import imshow
 
-    with OifFile(sys.argv[1]) as oif:
+    with OifFile(argv[1]) as oif:
         print(oif)  # noqa: T201
         print(oif.mainfile)  # noqa: T201
         for series in oif.series:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oiffile-2025.12.12/setup.py 
new/oiffile-2026.2.8/setup.py
--- old/oiffile-2025.12.12/setup.py     2025-12-12 03:24:02.000000000 +0100
+++ new/oiffile-2026.2.8/setup.py       2026-02-09 05:42:29.000000000 +0100
@@ -12,7 +12,8 @@
     """Return first match of pattern in string."""
     match = re.search(pattern, string, flags)
     if match is None:
-        raise ValueError(f'{pattern!r} not found')
+        msg = f'{pattern=!r} not found'
+        raise ValueError(msg)
     return match.groups()[0]
 
 
@@ -67,6 +68,20 @@
         fh.write('BSD-3-Clause license\n\n')
         fh.write(license)
 
+    revisions = search(
+        r'(?:\r\n|\r|\n){2}(Revisions.*)- …',
+        readme,
+        re.MULTILINE | re.DOTALL,
+    ).strip()
+
+    with open('CHANGES.rst', encoding='utf-8') as fh:
+        old = fh.read()
+
+    old = old.split(revisions.splitlines()[-1])[-1]
+    with open('CHANGES.rst', 'w', encoding='utf-8') as fh:
+        fh.write(revisions.strip())
+        fh.write(old)
+
 setup(
     name='oiffile',
     version=version,

Reply via email to