mirror of
https://github.com/quantum5/win2xcur.git
synced 2025-04-24 10:11:57 -04:00
More mypy annotations
This commit is contained in:
parent
764d0f6ca4
commit
0ecc367178
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -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 }}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
18
win2xcur/parser/base.py
Normal 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()
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue