Skip to content

Commit cb1bf5d

Browse files
committed
Revert to hasattr test, as it works with tempfile's dynamic class wrappers
1 parent 5906a20 commit cb1bf5d

2 files changed

Lines changed: 19 additions & 19 deletions

File tree

run_benchmarks.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,14 @@ def write_shapefile_with_PyShp(target: str | PathLike):
8585
for file_path in SHAPEFILES.values():
8686
file_path.read_bytes()
8787

88+
COLS_WIDTHS = (22, 10)
89+
8890
reader_benchmarks = [
8991
functools.partial(
9092
benchmark,
9193
name=f"Read {test_name}",
9294
func=functools.partial(open_shapefile_with_PyShp, target=target),
95+
col_widths=COLS_WIDTHS,
9396
)
9497
for test_name, target in SHAPEFILES.items()
9598
]
@@ -101,13 +104,17 @@ def write_shapefile_with_PyShp(target: str | PathLike):
101104
benchmark,
102105
name=f"Write {test_name}",
103106
func=functools.partial(write_shapefile_with_PyShp, target=target),
107+
col_widths=COLS_WIDTHS,
104108
)
105109
for test_name, target in SHAPEFILES.items()
106110
]
107111

108112

109-
def run(run_count: int, benchmarks: list[Callable[[], None]]) -> None:
110-
col_widths = (22, 10)
113+
def run(
114+
run_count: int,
115+
benchmarks: list[Callable[[], None]],
116+
col_widths: tuple[int, int] = COLS_WIDTHS,
117+
) -> None:
111118
col_head = ("parser", "exec time", "performance (more is better)")
112119
print(f"Running benchmarks {run_count} times:")
113120
print("-" * col_widths[0] + "---" + "-" * col_widths[1])
@@ -116,7 +123,6 @@ def run(run_count: int, benchmarks: list[Callable[[], None]]) -> None:
116123
for benchmark in benchmarks:
117124
benchmark( # type: ignore [call-arg]
118125
run_count=run_count,
119-
col_widths=col_widths,
120126
)
121127

122128

src/shapefile.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
Union,
4444
cast,
4545
overload,
46-
runtime_checkable,
4746
)
4847
from urllib.error import HTTPError
4948
from urllib.parse import ParseResult, urlparse, urlunparse
@@ -141,14 +140,12 @@ class ReadableBinStream(Protocol):
141140
def read(self, size: int = -1) -> bytes: ...
142141

143142

144-
@runtime_checkable
145143
class WriteSeekableBinStream(Protocol):
146144
def write(self, b: bytes) -> int: ...
147145
def seek(self, offset: int, whence: int = 0) -> int: ...
148146
def tell(self) -> int: ...
149147

150148

151-
@runtime_checkable
152149
class ReadSeekableBinStream(Protocol):
153150
def seek(self, offset: int, whence: int = 0) -> int: ...
154151
def tell(self) -> int: ...
@@ -2220,16 +2217,6 @@ def _save_to_named_tmp_file(
22202217
return tmp_file_obj
22212218

22222219

2223-
# Minor hack. Relies on Protocols being ABC subclasses.
2224-
# They are currently ABCs anyway, so why not use that
2225-
# for something useful like this?
2226-
#
2227-
# tempfile.NamedTemporaryFile is a dynamic wrapper
2228-
# https://github.com/python/cpython/blob/2dd91d2b92a6c74d78cd3385ede328190cd8eaa9/Lib/tempfile.py#L510
2229-
# so normal (naive) isinstance checks of tempfile.NamedTemporaryFiles
2230-
# against @runtime_checkable Protocols are not possible.
2231-
ReadSeekableBinStream.register(tempfile._TemporaryFileWrapper)
2232-
22332220
HTML_SIGNATURES_UC = (
22342221
b"<!DOCTYPE",
22352222
b"<HTML",
@@ -2398,7 +2385,6 @@ def close(self) -> None:
23982385
self.exit_stack.close()
23992386

24002387

2401-
# TODO: bound to @runtime_checkable Protocols
24022388
FileProtoT = TypeVar("FileProtoT")
24032389

24042390

@@ -2479,9 +2465,17 @@ def _ensure_file_obj(
24792465
exit_stack.enter_context(fp)
24802466
return cast(FileProtoT, fp)
24812467

2482-
if isinstance(f, FileProto):
2468+
# Ugly, but we need to apply this to TemporaryFiles, and tempfile
2469+
# is a platform-inconsistent stick mess of dynamic wrappers,
2470+
# leading to weird Python 3.14 specific bug in run_benchmarks.py
2471+
# on Windows
2472+
attrs = FileProto.__protocol_attrs__ # type: ignore[attr-defined]
2473+
2474+
if all(hasattr(f, attr) for attr in attrs):
24832475
return f
2484-
raise ExceptionClass(f"Unsupported file-like object: {f}")
2476+
raise ExceptionClass(
2477+
f"Unsupported file-like object: {f}. Must satisfy: {FileProto}"
2478+
)
24852479

24862480

24872481
class DbfReader(_FileChecker[ReadSeekableBinStream]):

0 commit comments

Comments
 (0)