Skip to content

Commit d81d75a

Browse files
committed
Typing
1 parent 369497c commit d81d75a

7 files changed

Lines changed: 261 additions & 31 deletions

File tree

.github/workflows/lint.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@ jobs:
5454
python -m black --check src/${PACKAGE} tests
5555
# python -m black --diff src/${PACKAGE} tests
5656
57-
# - name: Lint (mypy)
58-
# run: |
59-
# python -m mypy --version
60-
# python -m mypy ${PACKAGE}
57+
- name: Lint (mypy)
58+
end:
59+
PYTHONPATH: src
60+
run: |
61+
python -m mypy --version
62+
python -m mypy -p ${PACKAGE}
6163
6264
- name: Lint (ruff)
6365
run: |

Makefile

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ help:
3636
@echo " debug - generate debug build"
3737
@echo " ext-coverage - build the extension in-place with tracing enabled"
3838
@echo " coverage-report - generate the coverage report"
39+
@echo " stubs - generate Python typing stubs for cython modules"
3940

4041
dist:
4142
$(PYTHON) -m build
@@ -51,7 +52,7 @@ coverage: clean ext-coverage
5152
env PYTHONPATH=src $(PYTHON) -m pytest --doctest-modules --cov=$(TARGET) --cov-report=html --cov-report=term src/$(TARGET) tests
5253

5354
clean:
54-
$(PYTHON) setup.py clean --all
55+
# $(PYTHON) setup.py clean --all
5556
$(RM) -r src/*.*-info build
5657
find . -name __pycache__ -type d -exec $(RM) -r {} +
5758
# $(RM) -r __pycache__ */__pycache__ */*/__pycache__ */*/*/__pycache__
@@ -78,12 +79,12 @@ distclean: cleaner
7879
$(RM) $(TEST_DATSET)
7980

8081
lint:
81-
$(PYTHON) -m flake8 --count --statistics src/$(TARGET) tests
82+
# $(PYTHON) -m flake8 --count --statistics src/$(TARGET) tests
8283
$(PYTHON) -m pydocstyle --count src/$(TARGET)
8384
# $(PYTHON) -m isort --check src/$(TARGET) tests
8485
$(PYTHON) -m black --check src/$(TARGET) tests
85-
# $(PYTHON) -m mypy --check-untyped-defs --ignore-missing-imports src/$(TARGET)
86-
ruff check src/$(TARGET) tests
86+
$(PYTHON) -m mypy --check-untyped-defs src/$(TARGET)
87+
# ruff check src/$(TARGET) tests
8788
codespell
8889

8990
docs:
@@ -109,3 +110,9 @@ debug:
109110
wheels:
110111
# Requires docker
111112
python3 -m cibuildwheel --platform auto
113+
114+
stubs:
115+
env PYTHONPATH=src stubgen -v -m epr._epr -o src
116+
mv src/epr/_epr.pyi _epr.pyi
117+
grep -v _cython _epr.pyi | grep -v __pyx_ | grep -v _typeshed | grep -v Incomplete > src/epr/_epr.pyi
118+
$(RM) _epr.pyi

docs/NEWS.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ PyEPR 1.3.0 (UNRELEASED)
66
------------------------
77

88
* Update the bundled epr-api library source code to v2.3.
9+
* Improve typing support.
910

1011

1112
PyEPR 1.2.0 (29/03/2025)

pyproject.toml

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,14 @@ docs = [
7979
]
8080
test = ["pytest", "packaging"]
8181
coverage = ["pytest-cov", "coverage[toml]", {include-group = "test"}]
82-
lint = ["flake8", "pydocstyle", "isort", "black", "ruff", "codespell"]
82+
lint = ["flake8", "pydocstyle", "isort", "black", "ruff", "mypy", "codespell"]
8383
dev = [
8484
"python-build",
8585
"twine",
8686
"setuptools >=77",
8787
"packaging",
88-
"cython>=3",
89-
"numpy>=2",
88+
"cython >=3",
89+
"numpy >=2",
9090
{include-group = "docs"},
9191
{include-group = "test"},
9292
{include-group = "coverage"},
@@ -97,7 +97,10 @@ dev = [
9797
[tool.setuptools]
9898
packages = ["epr"]
9999
package-dir = {"" = "src"}
100-
include-package-data = false
100+
101+
102+
[tool.setuptools.package-data]
103+
epr = ["py.typed", "*.pyi"]
101104

102105

103106
[tool.setuptools.dynamic]
@@ -171,12 +174,10 @@ quiet-level = 3
171174
ignore-words="docs/spelling_wordlist.txt"
172175

173176

174-
# [tool.mypy]
175-
# exclude = [
176-
# "^docs/conf\\.py$",
177-
# # "two\.pyi$", # files ending with "two.pyi"
178-
# # "^three\.", # files starting with "three."
179-
# ]
177+
[tool.mypy]
178+
exclude = [
179+
"^docs/conf\\.py$",
180+
]
180181

181182

182183
[tool.ruff]
@@ -286,6 +287,7 @@ docstring-code-format = true
286287

287288
[tool.ruff.lint.isort]
288289
length-sort = true
290+
known-first-party = ["epr"]
289291

290292

291293
[tool.ruff.lint.flake8-import-conventions.extend-aliases]

src/epr/_epr.pyi

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import os
2+
import typing
3+
4+
import numpy as np
5+
6+
EPR_C_API_VERSION: str
7+
E_SMID_LIN: int
8+
E_SMID_LOG: int
9+
E_SMID_NON: int
10+
E_SMOD_1OF1: int
11+
E_SMOD_1OF2: int
12+
E_SMOD_2OF2: int
13+
E_SMOD_2TOF: int
14+
E_SMOD_3TOI: int
15+
E_TID_CHAR: int
16+
E_TID_DOUBLE: int
17+
E_TID_FLOAT: int
18+
E_TID_INT: int
19+
E_TID_SHORT: int
20+
E_TID_SPARE: int
21+
E_TID_STRING: int
22+
E_TID_TIME: int
23+
E_TID_UCHAR: int
24+
E_TID_UINT: int
25+
E_TID_UNKNOWN: int
26+
E_TID_USHORT: int
27+
28+
class EPRTime(typing.NamedTuple):
29+
days: int
30+
seconds: int
31+
microseconds: int
32+
33+
MJD: np.dtype
34+
35+
_EPR_MAGIC_FIELD: int
36+
_EPR_MAGIC_RASTER: int
37+
_EPR_MAGIC_RECORD: int
38+
_EPR_MAGIC_BAND_ID: int
39+
_EPR_MAGIC_PRODUCT_ID: int
40+
41+
class EPRError(Exception):
42+
def __init__(
43+
self, message: str = ..., code: int = ..., *args, **kargs
44+
) -> None: ...
45+
46+
class EPRValueError(EPRError, ValueError): ...
47+
class EprObject: ...
48+
49+
def get_numpy_dtype(type_id: int) -> type | np.dtype | None: ...
50+
def get_data_type_size(type_id: int) -> int: ...
51+
def data_type_id_to_str(type_id: int) -> str: ...
52+
def get_scaling_method_name(method: int) -> str: ...
53+
def get_sample_model_name(model: int) -> str: ...
54+
55+
class DSD(EprObject):
56+
def __eq__(self, other: object) -> bool: ...
57+
def __ge__(self, other: object) -> bool: ...
58+
def __gt__(self, other: object) -> bool: ...
59+
def __le__(self, other: object) -> bool: ...
60+
def __lt__(self, other: object) -> bool: ...
61+
def __ne__(self, other: object) -> bool: ...
62+
63+
class Field(EprObject):
64+
tot_size: int
65+
66+
def get_description(self) -> str: ...
67+
def get_elem(self, index: int = ...): ...
68+
def get_elems(self) -> np.ndarray: ...
69+
def get_name(self) -> str: ...
70+
def get_num_elems(self) -> int: ...
71+
def get_offset(self) -> int: ...
72+
def get_type(self) -> int: ...
73+
def get_unit(self) -> str: ...
74+
def print(self, ostream=...) -> None: ...
75+
def set_elem(self, elem, index=...) -> None: ...
76+
def set_elems(self, elems) -> None: ...
77+
def __eq__(self, other: object) -> bool: ...
78+
def __ge__(self, other: object) -> bool: ...
79+
def __gt__(self, other: object) -> bool: ...
80+
def __le__(self, other: object) -> bool: ...
81+
def __len__(self) -> int: ...
82+
def __lt__(self, other: object) -> bool: ...
83+
def __ne__(self, other: object) -> bool: ...
84+
85+
class Record(EprObject):
86+
dataset_name: str
87+
tot_size: int
88+
index: int
89+
90+
def fields(self) -> list[Field]: ...
91+
def get_field(self, name: str) -> Field: ...
92+
def get_field_at(self, index: str) -> Field: ...
93+
def get_field_names(self) -> list[str]: ...
94+
def get_num_fields(self) -> int: ...
95+
def get_offset(self) -> int | None: ...
96+
def print(self, ostream=...) -> None: ...
97+
def print_element(
98+
self, field_index: int, element_index: int, ostream=...
99+
) -> None: ...
100+
def __iter__(self) -> typing.Generator[Field]: ...
101+
102+
class Raster(EprObject):
103+
data_type: int
104+
source_width: int
105+
source_height: int
106+
source_step_x: int
107+
source_step_y: int
108+
data: np.ndarray
109+
110+
def get_elem_size(self) -> int: ...
111+
def get_height(self) -> int: ...
112+
def get_pixel(self, x: int, y: int): ...
113+
def get_width(self) -> int: ...
114+
def toarray(self) -> np.ndarray: ...
115+
116+
def create_raster(
117+
data_type: int,
118+
src_width: int,
119+
src_height: int,
120+
xstep: int = ...,
121+
ystep: int = ...,
122+
) -> Raster: ...
123+
def create_bitmask_raster(
124+
src_width: int, src_height: int, xstep: int = ..., ystep: int = ...
125+
) -> Raster: ...
126+
127+
class Band(EprObject):
128+
product: Product
129+
spectr_band_index: int
130+
sample_model: int
131+
data_type: int
132+
scaling_method: int
133+
scaling_offset: float
134+
scaling_factor: float
135+
bm_expr: str | None
136+
unit: str | None
137+
description: str
138+
lines_mirrored: bool
139+
dataset: Dataset
140+
141+
def create_compatible_raster(
142+
self,
143+
src_width: int = ...,
144+
src_height: int = ...,
145+
xstep: int = ...,
146+
ystep: int = ...,
147+
) -> Raster: ...
148+
def get_name(self) -> str: ...
149+
def read_as_array(
150+
self,
151+
width: int = ...,
152+
height: int = ...,
153+
xoffset: int = ...,
154+
yoffset: int = ...,
155+
xstep: int = ...,
156+
ystep: int = ...,
157+
) -> np.ndarray: ...
158+
def read_raster(
159+
self,
160+
xoffset: int = ...,
161+
yoffset: int = ...,
162+
raster: Raster | None = ...,
163+
) -> Raster: ...
164+
165+
class Dataset(EprObject):
166+
product: Product
167+
description: str
168+
169+
def create_record(self) -> Record: ...
170+
def get_dsd(self) -> DSD: ...
171+
def get_dsd_name(self) -> str: ...
172+
def get_name(self) -> str: ...
173+
def get_num_records(self) -> int: ...
174+
def read_record(
175+
self, index: int, record: Record | None = ...
176+
) -> Record: ...
177+
def records(self) -> list[Record]: ...
178+
def __iter__(self) -> typing.Generator[Record]: ...
179+
180+
class Product(EprObject):
181+
file_path: str
182+
mode: str
183+
tot_size: int
184+
id_string: str
185+
meris_iodd_version: int
186+
closed: bool
187+
188+
def __init__(
189+
self, filename: str | os.PathLike[str], mode: str = ...
190+
) -> None: ...
191+
def bands(self) -> list[Band]: ...
192+
def close(self) -> None: ...
193+
def datasets(self) -> list[Dataset]: ...
194+
def flush(self) -> None: ...
195+
def get_band(self, name: str) -> Band: ...
196+
def get_band_at(self, index: int) -> Band: ...
197+
def get_band_names(self) -> list[str]: ...
198+
def get_dataset(self, name: str) -> Dataset: ...
199+
def get_dataset_at(self, index) -> Dataset: ...
200+
def get_dataset_names(self) -> list[str]: ...
201+
def get_dsd_at(self, index: int) -> DSD: ...
202+
def get_mph(self) -> Record: ...
203+
def get_num_bands(self) -> int: ...
204+
def get_num_datasets(self) -> int: ...
205+
def get_num_dsds(self) -> int: ...
206+
def get_scene_height(self) -> int: ...
207+
def get_scene_width(self) -> int: ...
208+
def get_sph(self) -> Record: ...
209+
def read_bitmask_raster(
210+
self, bm_expr: str, xoffset: int, yoffset: int, raster: Raster
211+
) -> Raster: ...
212+
213+
def open( # noqa: A001
214+
filename: str | os.PathLike[str], mode: str = ...
215+
) -> Product: ...

src/epr/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)