Skip to content

Commit d13768f

Browse files
committed
TYP: Annotated filename_parser, move typedefs from filebasedimages
1 parent ece10ac commit d13768f

File tree

4 files changed

+48
-37
lines changed

4 files changed

+48
-37
lines changed

nibabel/dataobj_images.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515

1616
from .arrayproxy import ArrayLike
1717
from .deprecated import deprecate_with_version
18-
from .filebasedimages import FileBasedHeader, FileBasedImage, FileMap, FileSpec
18+
from .filebasedimages import FileBasedHeader, FileBasedImage
19+
from .fileholders import FileMap
1920

2021
if ty.TYPE_CHECKING: # pragma: no cover
2122
import numpy.typing as npt
2223

24+
from .filename_parser import FileSpec
25+
2326
ArrayImgT = ty.TypeVar('ArrayImgT', bound='DataobjImage')
2427

2528

nibabel/filebasedimages.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@
1010
from __future__ import annotations
1111

1212
import io
13-
import os
1413
import typing as ty
1514
from copy import deepcopy
1615
from typing import Type
1716
from urllib import request
1817

1918
from .fileholders import FileHolder, FileMap
20-
from .filename_parser import TypesFilenamesError, splitext_addext, types_filenames
19+
from .filename_parser import TypesFilenamesError, _stringify_path, splitext_addext, types_filenames
2120
from .openers import ImageOpener
2221

23-
FileSpec = ty.Union[str, os.PathLike]
22+
if ty.TYPE_CHECKING: # pragma: no cover
23+
from .filename_parser import ExtensionSpec, FileSpec
24+
2425
FileSniff = ty.Tuple[bytes, str]
2526

2627
ImgT = ty.TypeVar('ImgT', bound='FileBasedImage')
@@ -159,7 +160,7 @@ class FileBasedImage:
159160
header_class: Type[FileBasedHeader] = FileBasedHeader
160161
_header: FileBasedHeader
161162
_meta_sniff_len: int = 0
162-
files_types: tuple[tuple[str, str | None], ...] = (('image', None),)
163+
files_types: tuple[ExtensionSpec, ...] = (('image', None),)
163164
valid_exts: tuple[str, ...] = ()
164165
_compressed_suffixes: tuple[str, ...] = ()
165166

@@ -410,7 +411,7 @@ def _sniff_meta_for(
410411
t_fnames = types_filenames(
411412
filename, klass.files_types, trailing_suffixes=klass._compressed_suffixes
412413
)
413-
meta_fname = t_fnames.get('header', filename)
414+
meta_fname = t_fnames.get('header', _stringify_path(filename))
414415

415416
# Do not re-sniff if it would be from the same file
416417
if sniff is not None and sniff[1] == meta_fname:

nibabel/filename_parser.py

+36-30
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,21 @@
77
#
88
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
99
"""Create filename pairs, triplets etc, with expected extensions"""
10+
from __future__ import annotations
11+
1012
import os
11-
import pathlib
13+
import typing as ty
14+
15+
if ty.TYPE_CHECKING: # pragma: no cover
16+
FileSpec = str | os.PathLike[str]
17+
ExtensionSpec = tuple[str, str | None]
1218

1319

1420
class TypesFilenamesError(Exception):
1521
pass
1622

1723

18-
def _stringify_path(filepath_or_buffer):
24+
def _stringify_path(filepath_or_buffer: FileSpec) -> str:
1925
"""Attempt to convert a path-like object to a string.
2026
2127
Parameters
@@ -28,30 +34,21 @@ def _stringify_path(filepath_or_buffer):
2834
2935
Notes
3036
-----
31-
Objects supporting the fspath protocol (python 3.6+) are coerced
32-
according to its __fspath__ method.
33-
For backwards compatibility with older pythons, pathlib.Path objects
34-
are specially coerced.
35-
Any other object is passed through unchanged, which includes bytes,
36-
strings, buffers, or anything else that's not even path-like.
37-
38-
Copied from:
39-
https://github.com/pandas-dev/pandas/blob/325dd686de1589c17731cf93b649ed5ccb5a99b4/pandas/io/common.py#L131-L160
37+
Adapted from:
38+
https://github.com/pandas-dev/pandas/blob/325dd68/pandas/io/common.py#L131-L160
4039
"""
41-
if hasattr(filepath_or_buffer, '__fspath__'):
40+
if isinstance(filepath_or_buffer, os.PathLike):
4241
return filepath_or_buffer.__fspath__()
43-
elif isinstance(filepath_or_buffer, pathlib.Path):
44-
return str(filepath_or_buffer)
4542
return filepath_or_buffer
4643

4744

4845
def types_filenames(
49-
template_fname,
50-
types_exts,
51-
trailing_suffixes=('.gz', '.bz2'),
52-
enforce_extensions=True,
53-
match_case=False,
54-
):
46+
template_fname: FileSpec,
47+
types_exts: ty.Sequence[ExtensionSpec],
48+
trailing_suffixes: ty.Sequence[str] = ('.gz', '.bz2'),
49+
enforce_extensions: bool = True,
50+
match_case: bool = False,
51+
) -> dict[str, str]:
5552
"""Return filenames with standard extensions from template name
5653
5754
The typical case is returning image and header filenames for an
@@ -152,12 +149,12 @@ def types_filenames(
152149
# we've found .IMG as the extension, we want .HDR as the matching
153150
# one. Let's only do this when the extension is all upper or all
154151
# lower case.
155-
proc_ext = lambda s: s
152+
proc_ext: ty.Callable[[str], str] = lambda s: s
156153
if found_ext:
157154
if found_ext == found_ext.upper():
158-
proc_ext = lambda s: s.upper()
155+
proc_ext = str.upper
159156
elif found_ext == found_ext.lower():
160-
proc_ext = lambda s: s.lower()
157+
proc_ext = str.lower
161158
for name, ext in types_exts:
162159
if name == direct_set_name:
163160
tfns[name] = template_fname
@@ -171,7 +168,12 @@ def types_filenames(
171168
return tfns
172169

173170

174-
def parse_filename(filename, types_exts, trailing_suffixes, match_case=False):
171+
def parse_filename(
172+
filename: FileSpec,
173+
types_exts: ty.Sequence[ExtensionSpec],
174+
trailing_suffixes: ty.Sequence[str],
175+
match_case: bool = False,
176+
) -> tuple[str, str, str | None, str | None]:
175177
"""Split filename into fileroot, extension, trailing suffix; guess type.
176178
177179
Parameters
@@ -230,9 +232,9 @@ def parse_filename(filename, types_exts, trailing_suffixes, match_case=False):
230232
break
231233
guessed_name = None
232234
found_ext = None
233-
for name, ext in types_exts:
234-
if ext and endswith(filename, ext):
235-
extpos = -len(ext)
235+
for name, type_ext in types_exts:
236+
if type_ext and endswith(filename, type_ext):
237+
extpos = -len(type_ext)
236238
found_ext = filename[extpos:]
237239
filename = filename[:extpos]
238240
guessed_name = name
@@ -242,15 +244,19 @@ def parse_filename(filename, types_exts, trailing_suffixes, match_case=False):
242244
return (filename, found_ext, ignored, guessed_name)
243245

244246

245-
def _endswith(whole, end):
247+
def _endswith(whole: str, end: str) -> bool:
246248
return whole.endswith(end)
247249

248250

249-
def _iendswith(whole, end):
251+
def _iendswith(whole: str, end: str) -> bool:
250252
return whole.lower().endswith(end.lower())
251253

252254

253-
def splitext_addext(filename, addexts=('.gz', '.bz2', '.zst'), match_case=False):
255+
def splitext_addext(
256+
filename: FileSpec,
257+
addexts: ty.Sequence[str] = ('.gz', '.bz2', '.zst'),
258+
match_case: bool = False,
259+
) -> tuple[str, str, str]:
254260
"""Split ``/pth/fname.ext.gz`` into ``/pth/fname, .ext, .gz``
255261
256262
where ``.gz`` may be any of passed `addext` trailing suffixes.

nibabel/spatialimages.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@
140140

141141
from .arrayproxy import ArrayLike
142142
from .dataobj_images import DataobjImage
143-
from .filebasedimages import FileBasedHeader, FileBasedImage, FileMap
143+
from .filebasedimages import FileBasedHeader, FileBasedImage
144+
from .fileholders import FileMap
144145
from .fileslice import canonical_slicers
145146
from .orientations import apply_orientation, inv_ornt_aff
146147
from .viewers import OrthoSlicer3D

0 commit comments

Comments
 (0)