Skip to content

Commit 1fd1a44

Browse files
committed
TYP: Annotated filename_parser, move typedefs from filebasedimages
1 parent a4ede1a commit 1fd1a44

File tree

4 files changed

+47
-37
lines changed

4 files changed

+47
-37
lines changed

nibabel/dataobj_images.py

Lines changed: 4 additions & 1 deletion
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

Lines changed: 6 additions & 5 deletions
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

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@
77
#
88
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
99
"""Create filename pairs, triplets etc, with expected extensions"""
10+
from __future__ import annotations
1011

1112
import os
12-
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]
1318

1419

1520
class TypesFilenamesError(Exception):
1621
pass
1722

1823

19-
def _stringify_path(filepath_or_buffer):
24+
def _stringify_path(filepath_or_buffer: FileSpec) -> str:
2025
"""Attempt to convert a path-like object to a string.
2126
2227
Parameters
@@ -29,30 +34,21 @@ def _stringify_path(filepath_or_buffer):
2934
3035
Notes
3136
-----
32-
Objects supporting the fspath protocol (python 3.6+) are coerced
33-
according to its __fspath__ method.
34-
For backwards compatibility with older pythons, pathlib.Path objects
35-
are specially coerced.
36-
Any other object is passed through unchanged, which includes bytes,
37-
strings, buffers, or anything else that's not even path-like.
38-
39-
Copied from:
40-
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
4139
"""
42-
if hasattr(filepath_or_buffer, '__fspath__'):
40+
if isinstance(filepath_or_buffer, os.PathLike):
4341
return filepath_or_buffer.__fspath__()
44-
elif isinstance(filepath_or_buffer, pathlib.Path):
45-
return str(filepath_or_buffer)
4642
return filepath_or_buffer
4743

4844

4945
def types_filenames(
50-
template_fname,
51-
types_exts,
52-
trailing_suffixes=('.gz', '.bz2'),
53-
enforce_extensions=True,
54-
match_case=False,
55-
):
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]:
5652
"""Return filenames with standard extensions from template name
5753
5854
The typical case is returning image and header filenames for an
@@ -153,12 +149,12 @@ def types_filenames(
153149
# we've found .IMG as the extension, we want .HDR as the matching
154150
# one. Let's only do this when the extension is all upper or all
155151
# lower case.
156-
proc_ext = lambda s: s
152+
proc_ext: ty.Callable[[str], str] = lambda s: s
157153
if found_ext:
158154
if found_ext == found_ext.upper():
159-
proc_ext = lambda s: s.upper()
155+
proc_ext = str.upper
160156
elif found_ext == found_ext.lower():
161-
proc_ext = lambda s: s.lower()
157+
proc_ext = str.lower
162158
for name, ext in types_exts:
163159
if name == direct_set_name:
164160
tfns[name] = template_fname
@@ -172,7 +168,12 @@ def types_filenames(
172168
return tfns
173169

174170

175-
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]:
176177
"""Split filename into fileroot, extension, trailing suffix; guess type.
177178
178179
Parameters
@@ -231,9 +232,9 @@ def parse_filename(filename, types_exts, trailing_suffixes, match_case=False):
231232
break
232233
guessed_name = None
233234
found_ext = None
234-
for name, ext in types_exts:
235-
if ext and endswith(filename, ext):
236-
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)
237238
found_ext = filename[extpos:]
238239
filename = filename[:extpos]
239240
guessed_name = name
@@ -243,15 +244,19 @@ def parse_filename(filename, types_exts, trailing_suffixes, match_case=False):
243244
return (filename, found_ext, ignored, guessed_name)
244245

245246

246-
def _endswith(whole, end):
247+
def _endswith(whole: str, end: str) -> bool:
247248
return whole.endswith(end)
248249

249250

250-
def _iendswith(whole, end):
251+
def _iendswith(whole: str, end: str) -> bool:
251252
return whole.lower().endswith(end.lower())
252253

253254

254-
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]:
255260
"""Split ``/pth/fname.ext.gz`` into ``/pth/fname, .ext, .gz``
256261
257262
where ``.gz`` may be any of passed `addext` trailing suffixes.

nibabel/spatialimages.py

Lines changed: 2 additions & 1 deletion
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)