metadata-scrubber-tool:

implemented fix mypy type checking issues for piexif(it has no stub files) used basedpyright to generate a type stub, but damn that a lot more type related issues fixed most but a few remained so i modified the pyrightconfig with this: "typeCheckingMode": "standard" as it is in strict mode by default.
This commit is contained in:
HERITAGE-XION 2026-01-07 14:38:16 +01:00
parent fc1cf236c6
commit aabecd547f
22 changed files with 635 additions and 57 deletions

View File

@ -1,5 +0,0 @@
# Environment Variables Example
# Copy this file to .env and fill in your values
# Example:
# API_KEY=your_api_key_here

8
pyrightconfig.json Normal file
View File

@ -0,0 +1,8 @@
{
"stubPath": "./typings",
"reportMissingTypeStubs": false,
"venvPath": ".",
"venv": ".venv",
"pythonVersion": "3.12",
"typeCheckingMode": "standard",
}

View File

@ -36,7 +36,7 @@ def read(
raise typer.BadParameter("If you provide --recursive or -r, you must also provide --extension or -ext.")
if ext and not recursive:
raise typer.BadParameter("If you provide --extension or -ext, you must also provide --recursive or -r.")
for file in get_target_files(file_path, ext) if recursive else [file_path]:
try:
# Get the correct object from the factory

View File

@ -5,10 +5,23 @@ This module provides the JpegProcessor class which handles EXIF metadata
extraction and manipulation for JPEG images using the piexif library.
"""
import piexif # pyright: ignore[reportMissingTypeStubs]
from typing import TypedDict
import piexif
from PIL import Image
from src.utils.exceptions import MetadataNotFoundError, MetadataProcessingError
# Type alias for EXIF tag values (strings, bytes, ints, tuples, or lists)
ExifValue = str | bytes | int | tuple[object, ...] | list[object]
class JpegMetadataResult(TypedDict):
"""Return type for JpegProcessor.get_metadata()."""
data: dict[str, ExifValue]
tags_to_delete: list[int]
class JpegProcessor:
"""
@ -24,10 +37,10 @@ class JpegProcessor:
def __init__(self):
"""Initialize the JPEG processor with empty data structures."""
self.tags_to_delete = []
self.data = {}
self.tags_to_delete: list[int] = []
self.data: dict[str, ExifValue] = {}
def get_metadata(self, img):
def get_metadata(self, img: Image.Image) -> JpegMetadataResult:
"""
Extract EXIF metadata from a JPEG image.
@ -51,10 +64,11 @@ class JpegProcessor:
# Iterate through the IFD
for tag, tag_value in exif_dict[ifd].items():
tag_info = piexif.TAGS[ifd].get(tag, {})
ifd_tags = piexif.TAGS.get(ifd, {})
tag_info = ifd_tags.get(tag, {}) if isinstance(ifd_tags, dict) else {}
# Get the human-readable name for the tag
tag_name = (
tag_name = str(
tag_info.get("name", "Unknown Tag") if tag_info else "Unknown Tag"
)
@ -72,7 +86,7 @@ class JpegProcessor:
return {"data": self.data, "tags_to_delete": self.tags_to_delete}
def delete_metadata(self, img, tags_to_delete):
def delete_metadata(self, img: Image.Image, tags_to_delete: list[int]):
"""
Remove specified EXIF tags from a JPEG image.

View File

@ -6,7 +6,7 @@ extraction and manipulation for PNG images, including both EXIF data
and PNG textual metadata (PngInfo chunks).
"""
from typing import Any, Dict, List, Optional
from typing import Any
from PIL import ExifTags, PngImagePlugin
from PIL.Image import Exif, Image
@ -44,11 +44,11 @@ class PngProcessor:
def __init__(self):
"""Initialize the PNG processor with empty data structures."""
self.tags_to_delete: List[int] = []
self.text_keys_to_delete: List[str] = []
self.data: Dict[str, Any] = {}
self.tags_to_delete: list[int] = []
self.text_keys_to_delete: list[str] = []
self.data: dict[str, Any] = {}
def get_metadata(self, img: Image) -> Dict[str, Any]:
def get_metadata(self, img: Image) -> dict[str, Any]:
"""
Extract metadata from a PNG image.
@ -112,7 +112,7 @@ class PngProcessor:
"text_keys": self.text_keys_to_delete,
}
def delete_metadata(self, img: Image, tags_to_delete: List[int]) -> Exif:
def delete_metadata(self, img: Image, tags_to_delete: list[int]) -> Exif:
"""
Remove EXIF tags from a PNG image.
@ -143,8 +143,8 @@ class PngProcessor:
raise MetadataProcessingError(f"Error processing PNG EXIF: {str(e)}")
def get_clean_pnginfo(
self, img: Image, keys_to_remove: Optional[List[str]] = None
) -> Optional[PngImagePlugin.PngInfo]:
self, img: Image, keys_to_remove: list[str] | None = None
) -> PngImagePlugin.PngInfo | None:
"""
Create a new PngInfo with sensitive keys removed.

View File

@ -50,10 +50,10 @@ def main(
help="Show detailed debug logs for every file processed.",
),
version: bool = typer.Option(
None,
"--version",
"-v",
callback=version_callback,
None,
"--version",
"-v",
callback=version_callback,
is_eager=True,
help="Show the application version and exit."
),

View File

@ -10,10 +10,10 @@ of large batches (1000+ files).
import logging
import threading
from collections.abc import Callable, Iterable
from concurrent.futures import ThreadPoolExecutor, as_completed
from dataclasses import dataclass, field
from pathlib import Path
from typing import Callable, Iterable, List, Optional
from rich.console import Console
@ -30,8 +30,8 @@ class FileResult:
filepath: Path
success: bool
action: str # "scrubbed", "skipped", "dry-run"
output_path: Optional[Path] = None
error: Optional[str] = None
output_path: Path | None = None
error: str | None = None
@dataclass
@ -43,8 +43,8 @@ class BatchSummary:
skipped: int = 0
failed: int = 0
dry_run: bool = False
output_dir: Optional[Path] = None
results: List[FileResult] = field(default_factory=list)
output_dir: Path | None = None
results: list[FileResult] = field(default_factory=list)
class BatchProcessor:
@ -58,7 +58,7 @@ class BatchProcessor:
def __init__(
self,
output_dir: Optional[str] = None,
output_dir: str | None = None,
dry_run: bool = False,
max_workers: int = 4,
):
@ -73,7 +73,7 @@ class BatchProcessor:
self.output_dir = Path(output_dir) if output_dir else Path("./scrubbed")
self.dry_run = dry_run
self.max_workers = max_workers
self.results: List[FileResult] = []
self.results: list[FileResult] = []
# Thread synchronization
self._path_lock = threading.Lock() # Protects unique path generation
@ -92,7 +92,7 @@ class BatchProcessor:
Returns:
FileResult with success status and details.
"""
output_path: Optional[Path] = None # Track reserved path for cleanup
output_path: Path | None = None # Track reserved path for cleanup
try:
# Dry-run mode: just report what would happen
@ -151,8 +151,8 @@ class BatchProcessor:
def process_batch(
self,
files: Iterable[Path],
progress_callback: Optional[Callable[[FileResult], None]] = None,
) -> List[FileResult]:
progress_callback: Callable[[FileResult], None] | None = None,
) -> list[FileResult]:
"""
Process multiple files concurrently using ThreadPoolExecutor.
@ -184,7 +184,7 @@ class BatchProcessor:
return self.results
def process_batch_sequential(self, files: Iterable[Path]) -> List[FileResult]:
def process_batch_sequential(self, files: Iterable[Path]) -> list[FileResult]:
"""
Process files sequentially (legacy behavior for debugging).
@ -228,7 +228,7 @@ class BatchProcessor:
with self._results_lock:
self.results.append(result)
def _cleanup_reserved_path(self, output_path: Optional[Path]) -> None:
def _cleanup_reserved_path(self, output_path: Path | None) -> None:
"""
Remove empty placeholder file created during path reservation.

View File

@ -8,7 +8,7 @@ format-specific processors (JpegProcessor, PngProcessor).
import shutil
from pathlib import Path
from typing import Optional
from typing import Any, cast
import piexif # pyright: ignore[reportMissingTypeStubs]
from PIL import Image
@ -48,13 +48,13 @@ class ImageHandler(MetadataHandler):
filepath: Path to the image file to process.
"""
super().__init__(filepath)
self.processors = {
self.processors: dict[str, JpegProcessor | PngProcessor] = {
"jpeg": JpegProcessor(),
"png": PngProcessor(),
}
self.tags_to_delete = []
self.detected_format: Optional[str] = None
self.text_keys_to_delete = []
self.tags_to_delete: list[int] = []
self.detected_format: str | None = None
self.text_keys_to_delete: list[str] = []
def _detect_format(self) -> str:
"""
@ -101,7 +101,8 @@ class ImageHandler(MetadataHandler):
self.metadata = result["data"]
self.tags_to_delete = result["tags_to_delete"]
# Store text keys for PNG processing
self.text_keys_to_delete = result.get("text_keys", [])
if isinstance(result, PngProcessor):
self.text_keys_to_delete = result.get("text_keys", [])
return self.metadata
def wipe(self) -> None:
@ -120,18 +121,16 @@ class ImageHandler(MetadataHandler):
raise UnsupportedFormatError(f"Unsupported format: {self.detected_format}")
with Image.open(Path(self.filepath)) as img:
self.processed_metadata = processor.delete_metadata(
img, self.tags_to_delete
self.processed_metadata = cast(
dict[str, Any], processor.delete_metadata(img, self.tags_to_delete)
)
# For PNG, also get clean PngInfo
if self.detected_format == "png" and hasattr(
processor, "get_clean_pnginfo"
):
if isinstance(processor, PngProcessor):
self.clean_pnginfo = processor.get_clean_pnginfo(
img, self.text_keys_to_delete
)
def save(self, output_path: Optional[str | Path] = None) -> None:
def save(self, output_path: str | Path | None = None) -> None:
"""
Writes the changes to a copy of the original file.

View File

@ -7,7 +7,7 @@ must implement the read, wipe, and save methods.
"""
from abc import ABC, abstractmethod
from typing import Any, Dict, Optional
from typing import Any
class MetadataHandler(ABC):
@ -32,11 +32,11 @@ class MetadataHandler(ABC):
filepath: Path to the file to process.
"""
self.filepath = filepath
self.metadata: Dict[str, Any] = {}
self.processed_metadata: Dict[str, Any] = {}
self.metadata: dict[str, Any] = {}
self.processed_metadata: dict[str, Any] = {}
@abstractmethod
def read(self) -> Dict[str, Any]:
def read(self) -> dict[str, Any]:
"""
Extract metadata from the file.
@ -61,7 +61,7 @@ class MetadataHandler(ABC):
pass
@abstractmethod
def save(self, output_path: Optional[str] = None) -> None:
def save(self, output_path: str | None = None) -> None:
"""
Save the processed file with cleaned metadata.

View File

@ -5,7 +5,7 @@ This module provides functions for displaying metadata and batch processing
results in beautifully formatted tables and panels using the Rich library.
"""
from typing import Any, Dict
from typing import Any
from rich import box
from rich.console import Console
@ -18,7 +18,7 @@ from src.utils.formatter import clean_value
console = Console()
def print_metadata_table(metadata: Dict[str, Any]):
def print_metadata_table(metadata: dict[str, Any]):
"""
Display metadata in a formatted table organized by logical groups.

View File

@ -5,8 +5,8 @@ This module provides functions to find and yield files for processing,
supporting recursive directory traversal with extension filtering.
"""
from collections.abc import Generator
from pathlib import Path
from typing import Generator
def get_target_files(input_path_str: Path, ext: str) -> Generator[Path, None, None]:

View File

@ -0,0 +1,15 @@
"""
This type stub file was generated by pyright.
"""
from ._dump import dump as dump
from ._exceptions import *
from ._exif import *
from ._insert import insert as insert
from ._load import load as load
from ._remove import remove as remove
from ._transplant import transplant as transplant
__all__ = ["remove", "load", "dump", "transplant", "insert", "VERSION"]
VERSION: str

View File

@ -0,0 +1,24 @@
"""
This type stub file was generated by pyright.
"""
def split_into_segments(data): # -> list[bytes]:
"""Slices JPEG meta data into a list from JPEG binary data.
"""
...
def read_exif_from_file(filename): # -> bytes | None:
"""Slices JPEG meta data into a list from JPEG binary data.
"""
...
def get_exif_seg(segments): # -> None:
"""Returns Exif from JPEG meta data list
"""
...
def merge_segments(segments, exif=...): # -> bytes:
"""Merges Exif with APP0 and APP1 manipulations.
"""
...

20
typings/piexif/_dump.pyi Normal file
View File

@ -0,0 +1,20 @@
"""
This type stub file was generated by pyright.
"""
from ._common import *
from ._exif import *
TIFF_HEADER_LENGTH = ...
def dump(exif_dict_original): # -> bytes:
"""
py:function:: piexif.load(data)
Return exif as bytes.
:param dict exif: Exif data({"0th":dict, "Exif":dict, "GPS":dict, "Interop":dict, "1st":dict, "thumbnail":bytes})
:return: Exif
:rtype: bytes
"""
...

View File

@ -0,0 +1,8 @@
"""
This type stub file was generated by pyright.
"""
class InvalidImageDataError(ValueError):
...

341
typings/piexif/_exif.pyi Normal file
View File

@ -0,0 +1,341 @@
"""
Type stubs for piexif EXIF tag definitions.
"""
from typing import Final
class TYPES:
"""EXIF data type identifiers."""
Byte: Final[int] = ...
Ascii: Final[int] = ...
Short: Final[int] = ...
Long: Final[int] = ...
Rational: Final[int] = ...
SByte: Final[int] = ...
Undefined: Final[int] = ...
SShort: Final[int] = ...
SLong: Final[int] = ...
SRational: Final[int] = ...
Float: Final[int] = ...
DFloat: Final[int] = ...
TAGS: dict[str, dict[int, dict[str, str | int]]]
class ImageIFD:
"""Exif tag number reference - 0th IFD"""
ProcessingSoftware: Final[int] = ...
NewSubfileType: Final[int] = ...
SubfileType: Final[int] = ...
ImageWidth: Final[int] = ...
ImageLength: Final[int] = ...
BitsPerSample: Final[int] = ...
Compression: Final[int] = ...
PhotometricInterpretation: Final[int] = ...
Threshholding: Final[int] = ...
CellWidth: Final[int] = ...
CellLength: Final[int] = ...
FillOrder: Final[int] = ...
DocumentName: Final[int] = ...
ImageDescription: Final[int] = ...
Make: Final[int] = ...
Model: Final[int] = ...
StripOffsets: Final[int] = ...
Orientation: Final[int] = ...
SamplesPerPixel: Final[int] = ...
RowsPerStrip: Final[int] = ...
StripByteCounts: Final[int] = ...
XResolution: Final[int] = ...
YResolution: Final[int] = ...
PlanarConfiguration: Final[int] = ...
GrayResponseUnit: Final[int] = ...
GrayResponseCurve: Final[int] = ...
T4Options: Final[int] = ...
T6Options: Final[int] = ...
ResolutionUnit: Final[int] = ...
TransferFunction: Final[int] = ...
Software: Final[int] = ...
DateTime: Final[int] = ...
Artist: Final[int] = ...
HostComputer: Final[int] = ...
Predictor: Final[int] = ...
WhitePoint: Final[int] = ...
PrimaryChromaticities: Final[int] = ...
ColorMap: Final[int] = ...
HalftoneHints: Final[int] = ...
TileWidth: Final[int] = ...
TileLength: Final[int] = ...
TileOffsets: Final[int] = ...
TileByteCounts: Final[int] = ...
SubIFDs: Final[int] = ...
InkSet: Final[int] = ...
InkNames: Final[int] = ...
NumberOfInks: Final[int] = ...
DotRange: Final[int] = ...
TargetPrinter: Final[int] = ...
ExtraSamples: Final[int] = ...
SampleFormat: Final[int] = ...
SMinSampleValue: Final[int] = ...
SMaxSampleValue: Final[int] = ...
TransferRange: Final[int] = ...
ClipPath: Final[int] = ...
XClipPathUnits: Final[int] = ...
YClipPathUnits: Final[int] = ...
Indexed: Final[int] = ...
JPEGTables: Final[int] = ...
OPIProxy: Final[int] = ...
JPEGProc: Final[int] = ...
JPEGInterchangeFormat: Final[int] = ...
JPEGInterchangeFormatLength: Final[int] = ...
JPEGRestartInterval: Final[int] = ...
JPEGLosslessPredictors: Final[int] = ...
JPEGPointTransforms: Final[int] = ...
JPEGQTables: Final[int] = ...
JPEGDCTables: Final[int] = ...
JPEGACTables: Final[int] = ...
YCbCrCoefficients: Final[int] = ...
YCbCrSubSampling: Final[int] = ...
YCbCrPositioning: Final[int] = ...
ReferenceBlackWhite: Final[int] = ...
XMLPacket: Final[int] = ...
Rating: Final[int] = ...
RatingPercent: Final[int] = ...
ImageID: Final[int] = ...
CFARepeatPatternDim: Final[int] = ...
CFAPattern: Final[int] = ...
BatteryLevel: Final[int] = ...
Copyright: Final[int] = ...
ExposureTime: Final[int] = ...
ImageResources: Final[int] = ...
ExifTag: Final[int] = ...
InterColorProfile: Final[int] = ...
GPSTag: Final[int] = ...
Interlace: Final[int] = ...
TimeZoneOffset: Final[int] = ...
SelfTimerMode: Final[int] = ...
FlashEnergy: Final[int] = ...
SpatialFrequencyResponse: Final[int] = ...
Noise: Final[int] = ...
FocalPlaneXResolution: Final[int] = ...
FocalPlaneYResolution: Final[int] = ...
FocalPlaneResolutionUnit: Final[int] = ...
ImageNumber: Final[int] = ...
SecurityClassification: Final[int] = ...
ImageHistory: Final[int] = ...
ExposureIndex: Final[int] = ...
TIFFEPStandardID: Final[int] = ...
SensingMethod: Final[int] = ...
XPTitle: Final[int] = ...
XPComment: Final[int] = ...
XPAuthor: Final[int] = ...
XPKeywords: Final[int] = ...
XPSubject: Final[int] = ...
PrintImageMatching: Final[int] = ...
DNGVersion: Final[int] = ...
DNGBackwardVersion: Final[int] = ...
UniqueCameraModel: Final[int] = ...
LocalizedCameraModel: Final[int] = ...
CFAPlaneColor: Final[int] = ...
CFALayout: Final[int] = ...
LinearizationTable: Final[int] = ...
BlackLevelRepeatDim: Final[int] = ...
BlackLevel: Final[int] = ...
BlackLevelDeltaH: Final[int] = ...
BlackLevelDeltaV: Final[int] = ...
WhiteLevel: Final[int] = ...
DefaultScale: Final[int] = ...
DefaultCropOrigin: Final[int] = ...
DefaultCropSize: Final[int] = ...
ColorMatrix1: Final[int] = ...
ColorMatrix2: Final[int] = ...
CameraCalibration1: Final[int] = ...
CameraCalibration2: Final[int] = ...
ReductionMatrix1: Final[int] = ...
ReductionMatrix2: Final[int] = ...
AnalogBalance: Final[int] = ...
AsShotNeutral: Final[int] = ...
AsShotWhiteXY: Final[int] = ...
BaselineExposure: Final[int] = ...
BaselineNoise: Final[int] = ...
BaselineSharpness: Final[int] = ...
BayerGreenSplit: Final[int] = ...
LinearResponseLimit: Final[int] = ...
CameraSerialNumber: Final[int] = ...
LensInfo: Final[int] = ...
ChromaBlurRadius: Final[int] = ...
AntiAliasStrength: Final[int] = ...
ShadowScale: Final[int] = ...
DNGPrivateData: Final[int] = ...
MakerNoteSafety: Final[int] = ...
CalibrationIlluminant1: Final[int] = ...
CalibrationIlluminant2: Final[int] = ...
BestQualityScale: Final[int] = ...
RawDataUniqueID: Final[int] = ...
OriginalRawFileName: Final[int] = ...
OriginalRawFileData: Final[int] = ...
ActiveArea: Final[int] = ...
MaskedAreas: Final[int] = ...
AsShotICCProfile: Final[int] = ...
AsShotPreProfileMatrix: Final[int] = ...
CurrentICCProfile: Final[int] = ...
CurrentPreProfileMatrix: Final[int] = ...
ColorimetricReference: Final[int] = ...
CameraCalibrationSignature: Final[int] = ...
ProfileCalibrationSignature: Final[int] = ...
AsShotProfileName: Final[int] = ...
NoiseReductionApplied: Final[int] = ...
ProfileName: Final[int] = ...
ProfileHueSatMapDims: Final[int] = ...
ProfileHueSatMapData1: Final[int] = ...
ProfileHueSatMapData2: Final[int] = ...
ProfileToneCurve: Final[int] = ...
ProfileEmbedPolicy: Final[int] = ...
ProfileCopyright: Final[int] = ...
ForwardMatrix1: Final[int] = ...
ForwardMatrix2: Final[int] = ...
PreviewApplicationName: Final[int] = ...
PreviewApplicationVersion: Final[int] = ...
PreviewSettingsName: Final[int] = ...
PreviewSettingsDigest: Final[int] = ...
PreviewColorSpace: Final[int] = ...
PreviewDateTime: Final[int] = ...
RawImageDigest: Final[int] = ...
OriginalRawFileDigest: Final[int] = ...
SubTileBlockSize: Final[int] = ...
RowInterleaveFactor: Final[int] = ...
ProfileLookTableDims: Final[int] = ...
ProfileLookTableData: Final[int] = ...
OpcodeList1: Final[int] = ...
OpcodeList2: Final[int] = ...
OpcodeList3: Final[int] = ...
NoiseProfile: Final[int] = ...
ZZZTestSlong1: Final[int] = ...
ZZZTestSlong2: Final[int] = ...
ZZZTestSByte: Final[int] = ...
ZZZTestSShort: Final[int] = ...
ZZZTestDFloat: Final[int] = ...
class ExifIFD:
"""Exif tag number reference - Exif IFD"""
ExposureTime: Final[int] = ...
FNumber: Final[int] = ...
ExposureProgram: Final[int] = ...
SpectralSensitivity: Final[int] = ...
ISOSpeedRatings: Final[int] = ...
OECF: Final[int] = ...
SensitivityType: Final[int] = ...
StandardOutputSensitivity: Final[int] = ...
RecommendedExposureIndex: Final[int] = ...
ISOSpeed: Final[int] = ...
ISOSpeedLatitudeyyy: Final[int] = ...
ISOSpeedLatitudezzz: Final[int] = ...
ExifVersion: Final[int] = ...
DateTimeOriginal: Final[int] = ...
DateTimeDigitized: Final[int] = ...
OffsetTime: Final[int] = ...
OffsetTimeOriginal: Final[int] = ...
OffsetTimeDigitized: Final[int] = ...
ComponentsConfiguration: Final[int] = ...
CompressedBitsPerPixel: Final[int] = ...
ShutterSpeedValue: Final[int] = ...
ApertureValue: Final[int] = ...
BrightnessValue: Final[int] = ...
ExposureBiasValue: Final[int] = ...
MaxApertureValue: Final[int] = ...
SubjectDistance: Final[int] = ...
MeteringMode: Final[int] = ...
LightSource: Final[int] = ...
Flash: Final[int] = ...
FocalLength: Final[int] = ...
Temperature: Final[int] = ...
Humidity: Final[int] = ...
Pressure: Final[int] = ...
WaterDepth: Final[int] = ...
Acceleration: Final[int] = ...
CameraElevationAngle: Final[int] = ...
SubjectArea: Final[int] = ...
MakerNote: Final[int] = ...
UserComment: Final[int] = ...
SubSecTime: Final[int] = ...
SubSecTimeOriginal: Final[int] = ...
SubSecTimeDigitized: Final[int] = ...
FlashpixVersion: Final[int] = ...
ColorSpace: Final[int] = ...
PixelXDimension: Final[int] = ...
PixelYDimension: Final[int] = ...
RelatedSoundFile: Final[int] = ...
InteroperabilityTag: Final[int] = ...
FlashEnergy: Final[int] = ...
SpatialFrequencyResponse: Final[int] = ...
FocalPlaneXResolution: Final[int] = ...
FocalPlaneYResolution: Final[int] = ...
FocalPlaneResolutionUnit: Final[int] = ...
SubjectLocation: Final[int] = ...
ExposureIndex: Final[int] = ...
SensingMethod: Final[int] = ...
FileSource: Final[int] = ...
SceneType: Final[int] = ...
CFAPattern: Final[int] = ...
CustomRendered: Final[int] = ...
ExposureMode: Final[int] = ...
WhiteBalance: Final[int] = ...
DigitalZoomRatio: Final[int] = ...
FocalLengthIn35mmFilm: Final[int] = ...
SceneCaptureType: Final[int] = ...
GainControl: Final[int] = ...
Contrast: Final[int] = ...
Saturation: Final[int] = ...
Sharpness: Final[int] = ...
DeviceSettingDescription: Final[int] = ...
SubjectDistanceRange: Final[int] = ...
ImageUniqueID: Final[int] = ...
CameraOwnerName: Final[int] = ...
BodySerialNumber: Final[int] = ...
LensSpecification: Final[int] = ...
LensMake: Final[int] = ...
LensModel: Final[int] = ...
LensSerialNumber: Final[int] = ...
Gamma: Final[int] = ...
class GPSIFD:
"""Exif tag number reference - GPS IFD"""
GPSVersionID: Final[int] = ...
GPSLatitudeRef: Final[int] = ...
GPSLatitude: Final[int] = ...
GPSLongitudeRef: Final[int] = ...
GPSLongitude: Final[int] = ...
GPSAltitudeRef: Final[int] = ...
GPSAltitude: Final[int] = ...
GPSTimeStamp: Final[int] = ...
GPSSatellites: Final[int] = ...
GPSStatus: Final[int] = ...
GPSMeasureMode: Final[int] = ...
GPSDOP: Final[int] = ...
GPSSpeedRef: Final[int] = ...
GPSSpeed: Final[int] = ...
GPSTrackRef: Final[int] = ...
GPSTrack: Final[int] = ...
GPSImgDirectionRef: Final[int] = ...
GPSImgDirection: Final[int] = ...
GPSMapDatum: Final[int] = ...
GPSDestLatitudeRef: Final[int] = ...
GPSDestLatitude: Final[int] = ...
GPSDestLongitudeRef: Final[int] = ...
GPSDestLongitude: Final[int] = ...
GPSDestBearingRef: Final[int] = ...
GPSDestBearing: Final[int] = ...
GPSDestDistanceRef: Final[int] = ...
GPSDestDistance: Final[int] = ...
GPSProcessingMethod: Final[int] = ...
GPSAreaInformation: Final[int] = ...
GPSDateStamp: Final[int] = ...
GPSDifferential: Final[int] = ...
GPSHPositioningError: Final[int] = ...
class InteropIFD:
"""Exif tag number reference - Interoperability IFD"""
InteroperabilityIndex: Final[int] = ...

View File

@ -0,0 +1,17 @@
"""
This type stub file was generated by pyright.
"""
from ._common import *
def insert(exif, image, new_file=...): # -> None:
"""
py:function:: piexif.insert(exif_bytes, filename)
Insert exif into JPEG.
:param bytes exif_bytes: Exif as bytes
:param str filename: JPEG
"""
...

32
typings/piexif/_load.pyi Normal file
View File

@ -0,0 +1,32 @@
"""
This type stub file was generated by pyright.
"""
from ._common import *
from ._exif import *
LITTLE_ENDIAN = ...
def load(input_data, key_is_name=...): # -> dict[str, Any]:
"""
py:function:: piexif.load(filename)
Return exif data as dict. Keys(IFD name), be contained, are "0th", "Exif", "GPS", "Interop", "1st", and "thumbnail". Without "thumbnail", the value is dict(tag name/tag value). "thumbnail" value is JPEG as bytes.
:param str filename: JPEG or TIFF
:return: Exif data({"0th":dict, "Exif":dict, "GPS":dict, "Interop":dict, "1st":dict, "thumbnail":bytes})
:rtype: dict
"""
...
class _ExifReader:
def __init__(self, data) -> None:
...
def get_ifd_dict(self, pointer, ifd_name, read_unknown=...): # -> dict[Any, Any]:
...
def convert_value(self, val): # -> Any | tuple[Any, Any] | bytes | tuple[Any, ...] | tuple[tuple[Any, Any], ...]:
...

View File

@ -0,0 +1,16 @@
"""
This type stub file was generated by pyright.
"""
from ._common import *
def remove(src, new_file=...): # -> None:
"""
py:function:: piexif.remove(filename)
Remove exif from JPEG.
:param str filename: JPEG
"""
...

View File

@ -0,0 +1,17 @@
"""
This type stub file was generated by pyright.
"""
from ._common import *
def transplant(exif_src, image, new_file=...): # -> None:
"""
py:function:: piexif.transplant(filename1, filename2)
Transplant exif from filename1 to filename2.
:param str filename1: JPEG
:param str filename2: JPEG
"""
...

28
typings/piexif/_webp.pyi Normal file
View File

@ -0,0 +1,28 @@
"""
This type stub file was generated by pyright.
"""
def split(data): # -> list[Any]:
...
def merge_chunks(chunks): # -> bytes:
...
def set_vp8x(chunks):
...
def get_file_header(chunks): # -> bytes:
...
def get_exif(data): # -> None:
...
def insert_exif_into_chunks(chunks, exif_bytes):
...
def insert(webp_bytes, exif_bytes): # -> bytes:
...
def remove(webp_bytes): # -> bytes:
...

44
typings/piexif/helper.pyi Normal file
View File

@ -0,0 +1,44 @@
"""
This type stub file was generated by pyright.
"""
class UserComment:
ASCII = ...
JIS = ...
UNICODE = ...
ENCODINGS = ...
_JIS = ...
_UNICODE = ...
_PREFIX_SIZE = ...
_ASCII_PREFIX = ...
_JIS_PREFIX = ...
_UNICODE_PREFIX = ...
_UNDEFINED_PREFIX = ...
@classmethod
def load(cls, data):
"""
Convert "UserComment" value in exif format to str.
:param bytes data: "UserComment" value from exif
:return: u"foobar"
:rtype: str(Unicode)
:raises: ValueError if the data does not conform to the EXIF specification,
or the encoding is unsupported.
"""
...
@classmethod
def dump(cls, data, encoding=...):
"""
Convert str to appropriate format for "UserComment".
:param data: Like u"foobar"
:param str encoding: "ascii", "jis", or "unicode"
:return: b"ASCII\x00\x00\x00foobar"
:rtype: bytes
:raises: ValueError if the encoding is unsupported.
"""
...