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,
