Skip to content

Commit

Permalink
Fully annotate ExifRead (#9403)
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Waygood <[email protected]>
  • Loading branch information
srittau and AlexWaygood authored Dec 27, 2022
1 parent 27983aa commit 0cf685c
Show file tree
Hide file tree
Showing 16 changed files with 151 additions and 102 deletions.
1 change: 0 additions & 1 deletion pyrightconfig.stricter.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"stubs/cryptography",
"stubs/dateparser",
"stubs/docutils",
"stubs/ExifRead",
"stubs/Flask-Migrate",
"stubs/Flask-SQLAlchemy",
"stubs/fpdf2",
Expand Down
3 changes: 3 additions & 0 deletions stubs/ExifRead/@tests/stubtest_allowlist.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Stub-only module.
exifread._types

# The following constants are only re-exported at runtime as a side effect
# of Python's import system.
exifread.DEFAULT_STOP_TAG
Expand Down
18 changes: 14 additions & 4 deletions stubs/ExifRead/exifread/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
from _typeshed import Incomplete
from logging import Logger
from typing import Any

logger: Incomplete
from ._types import Reader

__version__: str
logger: Logger

def process_file(
fh, stop_tag=..., details: bool = ..., strict: bool = ..., debug: bool = ..., truncate_tags: bool = ..., auto_seek: bool = ...
): ...
fh: Reader,
stop_tag: str = ...,
details: bool = ...,
strict: bool = ...,
debug: bool = ...,
truncate_tags: bool = ...,
auto_seek: bool = ...,
) -> dict[str, Any]: ...
14 changes: 14 additions & 0 deletions stubs/ExifRead/exifread/_types.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Stubs-only module with type aliases for ExifRead.

from typing import Any, Protocol
from typing_extensions import Literal, TypeAlias

# The second item of the value tuple - if it exists - can be a variety of types,
# including a callable or another dict.
TagDict: TypeAlias = dict[int, tuple[str] | tuple[str, Any]]

class Reader(Protocol):
def __iter__(self) -> bytes: ...
def read(self, __size: int) -> bytes: ...
def tell(self) -> int: ...
def seek(self, __offset: int, __whence: Literal[0, 1] = ...) -> object: ...
60 changes: 33 additions & 27 deletions stubs/ExifRead/exifread/classes.pyi
Original file line number Diff line number Diff line change
@@ -1,42 +1,48 @@
from _typeshed import Incomplete
from logging import Logger
from typing import Any
from typing_extensions import Literal

logger: Incomplete
from ._types import Reader, TagDict

logger: Logger

class IfdTag:
printable: Incomplete
tag: Incomplete
field_type: Incomplete
field_offset: Incomplete
field_length: Incomplete
values: Incomplete
def __init__(self, printable: str, tag: int, field_type: int, values, field_offset: int, field_length: int) -> None: ...
printable: str
tag: int
field_type: int
field_offset: int
field_length: int
values: Any # either string, bytes or list of data items
def __init__(self, printable: str, tag: int, field_type: int, values: Any, field_offset: int, field_length: int) -> None: ...

class ExifHeader:
file_handle: Incomplete
endian: Incomplete
offset: Incomplete
fake_exif: Incomplete
strict: Incomplete
debug: Incomplete
detailed: Incomplete
truncate_tags: Incomplete
tags: Incomplete
file_handle: Reader
endian: Literal["I", "M"]
offset: int
fake_exif: bool
strict: bool
debug: bool
detailed: bool
truncate_tags: bool
tags: dict[str, Any]
def __init__(
self,
file_handle,
endian,
offset,
fake_exif,
file_handle: Reader,
endian: Literal["I", "M"],
offset: int,
fake_exif: bool,
strict: bool,
debug: bool = ...,
detailed: bool = ...,
truncate_tags: bool = ...,
) -> None: ...
def s2n(self, offset, length: int, signed: bool = ...) -> int: ...
def n2b(self, offset, length) -> bytes: ...
def list_ifd(self) -> list[Incomplete]: ...
def dump_ifd(self, ifd, ifd_name: str, tag_dict: Incomplete | None = ..., relative: int = ..., stop_tag=...) -> None: ...
def s2n(self, offset: int, length: int, signed: bool = ...) -> int: ...
def n2b(self, offset: int, length: int) -> bytes: ...
def list_ifd(self) -> list[int]: ...
def dump_ifd(
self, ifd: int, ifd_name: str, tag_dict: TagDict | None = ..., relative: int = ..., stop_tag: str = ...
) -> None: ...
def extract_tiff_thumbnail(self, thumb_ifd: int) -> None: ...
def extract_jpeg_thumbnail(self) -> None: ...
def decode_maker_note(self) -> None: ...
def parse_xmp(self, xmp_bytes: bytes): ...
def parse_xmp(self, xmp_bytes: bytes) -> None: ...
19 changes: 9 additions & 10 deletions stubs/ExifRead/exifread/exif_log.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from _typeshed import Incomplete
from typing import TextIO

TEXT_NORMAL: int
TEXT_BOLD: int
Expand All @@ -10,16 +10,15 @@ TEXT_BLUE: int
TEXT_MAGENTA: int
TEXT_CYAN: int

def get_logger(): ...
def setup_logger(debug, color) -> None: ...
def get_logger() -> logging.Logger: ...
def setup_logger(debug: bool, color: bool) -> None: ...

class Formatter(logging.Formatter):
color: Incomplete
debug: Incomplete
color: bool
debug: bool
def __init__(self, debug: bool = ..., color: bool = ...) -> None: ...
def format(self, record): ...

class Handler(logging.StreamHandler[Incomplete]):
color: Incomplete
debug: Incomplete
def __init__(self, log_level, debug: bool = ..., color: bool = ...) -> None: ...
class Handler(logging.StreamHandler[TextIO]):
color: bool
debug: bool
def __init__(self, log_level: logging._Level, debug: bool = ..., color: bool = ...) -> None: ...
30 changes: 16 additions & 14 deletions stubs/ExifRead/exifread/heic.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from _typeshed import Incomplete
from collections.abc import Callable
from logging import Logger

logger: Incomplete
from ._types import Reader

logger: Logger

class WrongBox(Exception): ...
class NoParser(Exception): ...
Expand All @@ -15,10 +17,10 @@ class Box:
size: int
after: int
pos: int
compat: list[Incomplete]
compat: list[bytes]
base_offset: int
subs: dict[str, Box]
locs: dict[Incomplete, Incomplete]
locs: dict[int, list[tuple[int, int]]]
exif_infe: Box | None
item_id: int
item_type: bytes
Expand All @@ -30,25 +32,25 @@ class Box:
base_offset_size: int
index_size: int
flags: int
name: Incomplete
name: str
def __init__(self, name: str) -> None: ...
def set_sizes(self, offset: int, length: int, base_offset: int, index: int): ...
def set_full(self, vflags: int): ...
def set_sizes(self, offset: int, length: int, base_offset: int, index: int) -> None: ...
def set_full(self, vflags: int) -> None: ...

class HEICExifFinder:
file_handle: Incomplete
def __init__(self, file_handle) -> None: ...
file_handle: Reader
def __init__(self, file_handle: Reader) -> None: ...
def get(self, nbytes: int) -> bytes: ...
def get16(self) -> int: ...
def get32(self) -> int: ...
def get64(self) -> int: ...
def get_int4x2(self) -> tuple[Incomplete, Incomplete]: ...
def get_int4x2(self) -> tuple[int, int]: ...
def get_int(self, size: int) -> int: ...
def get_string(self) -> bytes: ...
def next_box(self) -> Box: ...
def get_full(self, box: Box): ...
def skip(self, box: Box): ...
def get_full(self, box: Box) -> None: ...
def skip(self, box: Box) -> None: ...
def expect_parse(self, name: str) -> Box: ...
def get_parser(self, box: Box) -> Callable[..., Incomplete]: ...
def get_parser(self, box: Box) -> Callable[[Box], None]: ...
def parse_box(self, box: Box) -> Box: ...
def find_exif(self) -> tuple[Incomplete, Incomplete]: ...
def find_exif(self) -> tuple[int, bytes]: ...
8 changes: 5 additions & 3 deletions stubs/ExifRead/exifread/jpeg.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from _typeshed import Incomplete
from logging import Logger

logger: Incomplete
from ._types import Reader

def find_jpeg_exif(fh, data, fake_exif) -> tuple[Incomplete, Incomplete, Incomplete]: ...
logger: Logger

def find_jpeg_exif(fh: Reader, data: bytes, fake_exif: bool) -> tuple[int, bytes, bool]: ...
12 changes: 6 additions & 6 deletions stubs/ExifRead/exifread/tags/exif.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from _typeshed import Incomplete
from exifread._types import TagDict

INTEROP_TAGS: Incomplete
INTEROP_INFO: Incomplete
GPS_TAGS: Incomplete
GPS_INFO: Incomplete
EXIF_TAGS: Incomplete
INTEROP_TAGS: TagDict
INTEROP_INFO: tuple[str, TagDict]
GPS_TAGS: TagDict
GPS_INFO: tuple[str, TagDict]
EXIF_TAGS: TagDict
4 changes: 2 additions & 2 deletions stubs/ExifRead/exifread/tags/makernote/apple.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from _typeshed import Incomplete
from exifread._types import TagDict

TAGS: Incomplete
TAGS: TagDict
37 changes: 22 additions & 15 deletions stubs/ExifRead/exifread/tags/makernote/canon.pyi
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
from _typeshed import Incomplete
from collections.abc import Callable
from typing import Any
from typing_extensions import TypeAlias

TAGS: Incomplete
CAMERA_SETTINGS: Incomplete
FOCAL_LENGTH: Incomplete
SHOT_INFO: Incomplete
AF_INFO_2: Incomplete
FILE_INFO: Incomplete
from exifread._types import TagDict

def add_one(value): ...
def subtract_one(value): ...
def convert_temp(value): ...
TAGS: TagDict

CAMERA_SETTINGS: TagDict
FOCAL_LENGTH: TagDict
SHOT_INFO: TagDict
AF_INFO_2: TagDict
FILE_INFO: TagDict

def add_one(value: int) -> int: ...
def subtract_one(value: int) -> int: ...
def convert_temp(value: int) -> str: ...

_CameraInfo: TypeAlias = dict[int, tuple[str, str, Callable[[int], Any]]]

CAMERA_INFO_TAG_NAME: str
CAMERA_INFO_5D: Incomplete
CAMERA_INFO_5DMKII: Incomplete
CAMERA_INFO_5DMKIII: Incomplete
CAMERA_INFO_600D: Incomplete
CAMERA_INFO_MODEL_MAP: Incomplete
CAMERA_INFO_5D: _CameraInfo
CAMERA_INFO_5DMKII: _CameraInfo
CAMERA_INFO_5DMKIII: _CameraInfo
CAMERA_INFO_600D: _CameraInfo
CAMERA_INFO_MODEL_MAP: dict[str, _CameraInfo]
4 changes: 2 additions & 2 deletions stubs/ExifRead/exifread/tags/makernote/casio.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from _typeshed import Incomplete
from exifread._types import TagDict

TAGS: Incomplete
TAGS: TagDict
4 changes: 2 additions & 2 deletions stubs/ExifRead/exifread/tags/makernote/fujifilm.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from _typeshed import Incomplete
from exifread._types import TagDict

TAGS: Incomplete
TAGS: TagDict
8 changes: 4 additions & 4 deletions stubs/ExifRead/exifread/tags/makernote/nikon.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from _typeshed import Incomplete
from exifread._types import TagDict

def ev_bias(seq) -> str: ...
def ev_bias(seq: list[int]) -> str: ...

TAGS_NEW: Incomplete
TAGS_OLD: Incomplete
TAGS_NEW: TagDict
TAGS_OLD: TagDict
8 changes: 4 additions & 4 deletions stubs/ExifRead/exifread/tags/makernote/olympus.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from _typeshed import Incomplete
from exifread._types import TagDict

def special_mode(val): ...
def special_mode(val: bytes) -> str: ...

TAGS: Incomplete
TAG_0x2020: Incomplete
TAGS: TagDict
TAG_0x2020: TagDict
23 changes: 15 additions & 8 deletions stubs/ExifRead/exifread/utils.pyi
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
from _typeshed import Incomplete
from _typeshed import Self
from collections.abc import Mapping
from fractions import Fraction
from typing import Any, TypeVar, overload

def ord_(dta): ...
def make_string(seq: bytes | list[Incomplete]) -> str: ...
def make_string_uc(seq) -> str: ...
def get_gps_coords(tags: dict[Incomplete, Incomplete]) -> tuple[Incomplete, Incomplete]: ...
_T = TypeVar("_T")

@overload
def ord_(dta: str) -> int: ... # type: ignore[misc]
@overload
def ord_(dta: _T) -> _T: ...
def make_string(seq: str | list[int]) -> str: ...
def make_string_uc(seq: str | list[int]) -> str: ...
def get_gps_coords(tags: Mapping[str, Any]) -> tuple[float, float]: ...

class Ratio(Fraction):
def __new__(cls, numerator: int = ..., denominator: Incomplete | None = ...): ...
def __new__(cls: type[Self], numerator: int = ..., denominator: int | None = ...) -> Self: ...
@property
def num(self): ...
def num(self) -> int: ...
@property
def den(self): ...
def den(self) -> int: ...
def decimal(self) -> float: ...

0 comments on commit 0cf685c

Please sign in to comment.