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
strategy:
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:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}

View file

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

View file

@ -1,10 +1,13 @@
from typing import List, Type
from win2xcur.parser.ani import ANIParser
from win2xcur.parser.base import BaseParser
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:
if parser.can_parse(blob):
return parser(blob)

View file

@ -1,10 +1,13 @@
import struct
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
class ANIParser:
class ANIParser(BaseParser):
SIGNATURE = b'RIFF'
ANI_TYPE = b'ACON'
FRAME_TYPE = b'fram'
@ -16,26 +19,26 @@ class ANIParser:
ICON_FLAG = 0x1
@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])
return signature == cls.SIGNATURE and size == len(blob) - 8 and subtype == cls.ANI_TYPE
def __init__(self, blob):
self.blob = blob
def __init__(self, blob: bytes) -> None:
super().__init__(blob)
if not self.can_parse(blob):
raise ValueError('Not a .ani file')
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])
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)
if name not in expected:
raise ValueError('Expected chunk %r, found %r' % (expected, name))
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'])
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
from typing import List, Tuple
from wand.image import Image
from win2xcur.cursor import CursorFrame, CursorImage
from win2xcur.parser.base import BaseParser
class CURParser:
class CURParser(BaseParser):
MAGIC = b'\0\0\02\0'
ICON_DIR = struct.Struct('<HHH')
ICON_DIR_ENTRY = struct.Struct('<BBBBHHII')
@classmethod
def can_parse(cls, blob):
def can_parse(cls, blob: bytes) -> bool:
return blob[:len(cls.MAGIC)] == cls.MAGIC
def __init__(self, blob):
self.blob = blob
def __init__(self, blob: bytes) -> None:
super().__init__(blob)
self._image = Image(blob=blob, format='cur')
self._hotspots = self._parse_header()
self.frames = [CursorFrame([
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])
assert reserved == 0
assert ico_type == 2

View file

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