Skip to content

Commit bb487f6

Browse files
authored
Merge pull request #339 from JamesParrott/pyshp-3.0.0-alpha-attype-of-the-clones
Pyshp 3.0.0 alpha attype of the clones
2 parents ce83f86 + e769958 commit bb487f6

17 files changed

+517
-343
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,11 +458,13 @@ shapeType Point do not have a bounding box 'bbox'.
458458
... if not name.startswith('_'):
459459
... name
460460
'bbox'
461+
'from_byte_stream'
461462
'oid'
462463
'parts'
463464
'points'
464465
'shapeType'
465466
'shapeTypeName'
467+
'write_to_byte_stream'
466468

467469
* `oid`: The shape's index position in the original shapefile.
468470

pyproject.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,11 @@ load-plugins=[
112112
"pylint_per_file_ignores",
113113
]
114114

115-
# Silence warning: shapefile.py:2076:20: W0212: Access to a protected
116-
# member _from_geojson of a client class (protected-access)
117-
# shapefile.py:950:16: W0201: Attribute 'm' defined outside __init__ (attribute-defined-outside-init)
115+
# Silence warnings: src/shapefile.py:2076:20: W0212: Access to a protected member _from_geojson of a client class (protected-access)
116+
# src/shapefile.py:950:16: W0201: Attribute 'm' defined outside __init__ (attribute-defined-outside-init)
117+
# src/shapefile.py:973:12: W0707: Consider explicitly re-raising using 'except error as exc' and
118+
# 'raise ShapefileException(f'Failed to write bounding box for record {i}.
119+
# Expected floats.') from exc' (raise-missing-from)
118120
# Silence remarks:
119121
# src\shapefile.py:338:0: R0914: Too many local variables (21/15) (too-many-locals)
120122
# src\shapefile.py:338:0: R0912: Too many branches (24/12) (too-many-branches)
@@ -134,6 +136,6 @@ load-plugins=[
134136
# https://github.com/christopherpickering/pylint-per-file-ignores/issues/160
135137
[tool.pylint.'messages control']
136138
per-file-ignores = [
137-
"/src/shapefile.py:W0212,W0201,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0917,R1732",
139+
"/src/shapefile.py:W0707,W0212,W0201,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0917,R1732",
138140
"test_shapefile.py:W0212,R1732",
139141
]

run_benchmarks.py

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
from __future__ import annotations
44

5+
import collections
56
import functools
67
import os
78
import timeit
89
from collections.abc import Callable
910
from pathlib import Path
11+
from tempfile import TemporaryFile as TempF
1012
from typing import Union
1113

12-
import shapefile as shp
14+
import shapefile
1315

1416
# For shapefiles from https://github.com/JamesParrott/PyShp_test_shapefile
1517
DEFAULT_PYSHP_TEST_REPO = (
@@ -31,26 +33,41 @@ def benchmark(
3133
name: str,
3234
run_count: int,
3335
func: Callable,
34-
col_width: tuple,
36+
col_widths: tuple,
3537
compare_to: float | None = None,
3638
) -> float:
3739
placeholder = "Running..."
38-
print(f"{name:>{col_width[0]}} | {placeholder}", end="", flush=True)
40+
print(f"{name:>{col_widths[0]}} | {placeholder}", end="", flush=True)
3941
time_taken = timeit.timeit(func, number=run_count)
4042
print("\b" * len(placeholder), end="")
4143
time_suffix = " s"
42-
print(f"{time_taken:{col_width[1]-len(time_suffix)}.3g}{time_suffix}", end="")
44+
print(f"{time_taken:{col_widths[1]-len(time_suffix)}.3g}{time_suffix}", end="")
4345
print()
4446
return time_taken
4547

4648

49+
fields = {}
50+
shapeRecords = collections.defaultdict(list)
51+
52+
4753
def open_shapefile_with_PyShp(target: Union[str, os.PathLike]):
48-
with shp.Reader(target) as r:
54+
with shapefile.Reader(target) as r:
55+
fields[target] = r.fields
4956
for shapeRecord in r.iterShapeRecords():
50-
pass
57+
shapeRecords[target].append(shapeRecord)
58+
59+
60+
def write_shapefile_with_PyShp(target: Union[str, os.PathLike]):
61+
with TempF("wb") as shp, TempF("wb") as dbf, TempF("wb") as shx:
62+
with shapefile.Writer(shp=shp, dbf=dbf, shx=shx) as w: # type: ignore [arg-type]
63+
for field_info_tuple in fields[target]:
64+
w.field(*field_info_tuple)
65+
for shapeRecord in shapeRecords[target]:
66+
w.shape(shapeRecord.shape)
67+
w.record(*shapeRecord.record)
5168

5269

53-
READER_TESTS = {
70+
SHAPEFILES = {
5471
"Blockgroups": blockgroups_file,
5572
"Edit": edit_file,
5673
"Merge": merge_file,
@@ -60,24 +77,47 @@ def open_shapefile_with_PyShp(target: Union[str, os.PathLike]):
6077
}
6178

6279

63-
def run(run_count: int) -> None:
64-
col_width = (21, 10)
80+
# Load files to avoid one off delays that only affect first disk seek
81+
for file_path in SHAPEFILES.values():
82+
file_path.read_bytes()
83+
84+
reader_benchmarks = [
85+
functools.partial(
86+
benchmark,
87+
name=f"Read {test_name}",
88+
func=functools.partial(open_shapefile_with_PyShp, target=target),
89+
)
90+
for test_name, target in SHAPEFILES.items()
91+
]
92+
93+
# Require fields and shapeRecords to first have been populated
94+
# from data from previouly running the reader_benchmarks
95+
writer_benchmarks = [
96+
functools.partial(
97+
benchmark,
98+
name=f"Write {test_name}",
99+
func=functools.partial(write_shapefile_with_PyShp, target=target),
100+
)
101+
for test_name, target in SHAPEFILES.items()
102+
]
103+
104+
105+
def run(run_count: int, benchmarks: list[Callable[[], None]]) -> None:
106+
col_widths = (22, 10)
65107
col_head = ("parser", "exec time", "performance (more is better)")
66-
# Load files to avoid one off delays that only affect first disk seek
67-
for file_path in READER_TESTS.values():
68-
file_path.read_bytes()
69108
print(f"Running benchmarks {run_count} times:")
70-
print("-" * col_width[0] + "---" + "-" * col_width[1])
71-
print(f"{col_head[0]:>{col_width[0]}} | {col_head[1]:>{col_width[1]}}")
72-
print("-" * col_width[0] + "-+-" + "-" * col_width[1])
73-
for test_name, target in READER_TESTS.items():
74-
benchmark(
75-
f"Read {test_name}",
76-
run_count,
77-
functools.partial(open_shapefile_with_PyShp, target=target),
78-
col_width,
109+
print("-" * col_widths[0] + "---" + "-" * col_widths[1])
110+
print(f"{col_head[0]:>{col_widths[0]}} | {col_head[1]:>{col_widths[1]}}")
111+
print("-" * col_widths[0] + "-+-" + "-" * col_widths[1])
112+
for benchmark in benchmarks:
113+
benchmark( # type: ignore [call-arg]
114+
run_count=run_count,
115+
col_widths=col_widths,
79116
)
80117

81118

82119
if __name__ == "__main__":
83-
run(1)
120+
print("Reader tests:")
121+
run(1, reader_benchmarks) # type: ignore [arg-type]
122+
print("\n\nWriter tests:")
123+
run(1, writer_benchmarks) # type: ignore [arg-type]

shapefiles/test/balancing.dbf

0 Bytes
Binary file not shown.

shapefiles/test/contextwriter.dbf

0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

shapefiles/test/dtype.dbf

0 Bytes
Binary file not shown.

shapefiles/test/line.dbf

0 Bytes
Binary file not shown.

shapefiles/test/multipoint.dbf

0 Bytes
Binary file not shown.

shapefiles/test/onlydbf.dbf

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)