More mypy annotations

This commit is contained in:
Quantum 2020-09-27 00:53:23 -04:00
parent 764d0f6ca4
commit 0ecc367178
7 changed files with 43 additions and 17 deletions

View file

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: [ 3.5, 3.6, 3.7, 3.8 ] python-version: [ 3.6, 3.7, 3.8, 3.9.0-alpha - 3.9 ]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}

View file

@ -37,7 +37,7 @@ def main() -> None:
check_xcursorgen() check_xcursorgen()
def process(file): def process(file) -> None:
name = file.name name = file.name
blob = file.read() blob = file.read()
try: try:

View file

@ -1,10 +1,13 @@
from typing import List, Type
from win2xcur.parser.ani import ANIParser from win2xcur.parser.ani import ANIParser
from win2xcur.parser.base import BaseParser
from win2xcur.parser.cur import CURParser from win2xcur.parser.cur import CURParser
PARSERS = [CURParser, ANIParser] PARSERS: List[Type[BaseParser]] = [CURParser, ANIParser]
def open_blob(blob): def open_blob(blob: bytes) -> BaseParser:
for parser in PARSERS: for parser in PARSERS:
if parser.can_parse(blob): if parser.can_parse(blob):
return parser(blob) return parser(blob)

View file

@ -1,10 +1,13 @@
import struct import struct
from copy import copy from copy import copy
from typing import Any, Iterable, List, Tuple
from win2xcur.cursor import CursorFrame
from win2xcur.parser.base import BaseParser
from win2xcur.parser.cur import CURParser from win2xcur.parser.cur import CURParser
class ANIParser: class ANIParser(BaseParser):
SIGNATURE = b'RIFF' SIGNATURE = b'RIFF'
ANI_TYPE = b'ACON' ANI_TYPE = b'ACON'
FRAME_TYPE = b'fram' FRAME_TYPE = b'fram'
@ -16,26 +19,26 @@ class ANIParser:
ICON_FLAG = 0x1 ICON_FLAG = 0x1
@classmethod @classmethod
def can_parse(cls, blob): def can_parse(cls, blob: bytes) -> bool:
signature, size, subtype = cls.RIFF_HEADER.unpack(blob[:cls.RIFF_HEADER.size]) signature, size, subtype = cls.RIFF_HEADER.unpack(blob[:cls.RIFF_HEADER.size])
return signature == cls.SIGNATURE and size == len(blob) - 8 and subtype == cls.ANI_TYPE return signature == cls.SIGNATURE and size == len(blob) - 8 and subtype == cls.ANI_TYPE
def __init__(self, blob): def __init__(self, blob: bytes) -> None:
self.blob = blob super().__init__(blob)
if not self.can_parse(blob): if not self.can_parse(blob):
raise ValueError('Not a .ani file') raise ValueError('Not a .ani file')
self.frames = self._parse(self.RIFF_HEADER.size) self.frames = self._parse(self.RIFF_HEADER.size)
def _unpack(self, struct_cls, offset): def _unpack(self, struct_cls: struct.Struct, offset: int) -> Tuple[Any, ...]:
return struct_cls.unpack(self.blob[offset:offset + struct_cls.size]) return struct_cls.unpack(self.blob[offset:offset + struct_cls.size])
def _read_chunk(self, offset, expected): def _read_chunk(self, offset: int, expected: Iterable[bytes]) -> Tuple[int, int]:
name, size = self._unpack(self.CHUNK_HEADER, offset) name, size = self._unpack(self.CHUNK_HEADER, offset)
if name not in expected: if name not in expected:
raise ValueError('Expected chunk %r, found %r' % (expected, name)) raise ValueError('Expected chunk %r, found %r' % (expected, name))
return size, offset + self.CHUNK_HEADER.size return size, offset + self.CHUNK_HEADER.size
def _parse(self, offset): def _parse(self, offset: int) -> List[CursorFrame]:
size, offset = self._read_chunk(offset, expected=[b'anih']) size, offset = self._read_chunk(offset, expected=[b'anih'])
if size != self.ANIH_HEADER.size: if size != self.ANIH_HEADER.size:

18
win2xcur/parser/base.py Normal file
View file

@ -0,0 +1,18 @@
from abc import ABCMeta, abstractmethod
from typing import List
from win2xcur.cursor import CursorFrame
class BaseParser(metaclass=ABCMeta):
blob: bytes
frames: List[CursorFrame]
@abstractmethod
def __init__(self, blob: bytes) -> None:
self.blob = blob
@classmethod
@abstractmethod
def can_parse(cls, blob: bytes) -> bool:
raise NotImplementedError()

View file

@ -1,28 +1,30 @@
import struct import struct
from typing import List, Tuple
from wand.image import Image from wand.image import Image
from win2xcur.cursor import CursorFrame, CursorImage from win2xcur.cursor import CursorFrame, CursorImage
from win2xcur.parser.base import BaseParser
class CURParser: class CURParser(BaseParser):
MAGIC = b'\0\0\02\0' MAGIC = b'\0\0\02\0'
ICON_DIR = struct.Struct('<HHH') ICON_DIR = struct.Struct('<HHH')
ICON_DIR_ENTRY = struct.Struct('<BBBBHHII') ICON_DIR_ENTRY = struct.Struct('<BBBBHHII')
@classmethod @classmethod
def can_parse(cls, blob): def can_parse(cls, blob: bytes) -> bool:
return blob[:len(cls.MAGIC)] == cls.MAGIC return blob[:len(cls.MAGIC)] == cls.MAGIC
def __init__(self, blob): def __init__(self, blob: bytes) -> None:
self.blob = blob super().__init__(blob)
self._image = Image(blob=blob, format='cur') self._image = Image(blob=blob, format='cur')
self._hotspots = self._parse_header() self._hotspots = self._parse_header()
self.frames = [CursorFrame([ self.frames = [CursorFrame([
CursorImage(image, hotspot) for image, hotspot in zip(self._image.sequence, self._hotspots) CursorImage(image, hotspot) for image, hotspot in zip(self._image.sequence, self._hotspots)
])] ])]
def _parse_header(self): def _parse_header(self) -> List[Tuple[int, int]]:
reserved, ico_type, image_count = self.ICON_DIR.unpack(self.blob[:self.ICON_DIR.size]) reserved, ico_type, image_count = self.ICON_DIR.unpack(self.blob[:self.ICON_DIR.size])
assert reserved == 0 assert reserved == 0
assert ico_type == 2 assert ico_type == 2

View file

@ -22,7 +22,7 @@ def apply_to_image(image: BaseImage, *, color: str, radius: float, sigma: float,
return result return result
def apply_to_frames(frames: List[CursorFrame], **kwargs): def apply_to_frames(frames: List[CursorFrame], **kwargs) -> None:
for frame in frames: for frame in frames:
for cursor in frame: for cursor in frame:
cursor.image = apply_to_image(cursor.image, **kwargs) cursor.image = apply_to_image(cursor.image, **kwargs)