Skip to content

Commit

Permalink
Merge pull request #41 from source-foundry/dev
Browse files Browse the repository at this point in the history
Add type hints and static type checking with mypy
  • Loading branch information
chrissimpkins authored Sep 13, 2020
2 parents 748b459 + d40e624 commit 8429037
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ matrix:
dist: xenial
env: TOX_ENV=static-type-checks
install:
- pip install --upgrade pytype
- pip install --upgrade mypy
- pip install -r requirements.txt
script: make test-type-check
- python: 3.7
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test-lint:
flake8 --ignore=E501,W50 lib/dehinter

test-type-check:
pytype lib/dehinter
mypy lib/dehinter

test-unit:
tox
Expand Down
7 changes: 4 additions & 3 deletions lib/dehinter/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
import os
import pprint
import sys
from typing import List

from fontTools.ttLib import TTFont
from fontTools.ttLib import TTFont # type: ignore

from dehinter import __version__
from dehinter.font import is_truetype_font
Expand Down Expand Up @@ -46,11 +47,11 @@
from dehinter.system import get_filesize


def main(): # pragma: no cover
def main() -> None: # pragma: no cover
run(sys.argv[1:])


def run(argv):
def run(argv: List[str]) -> None:
# instantiate pretty printer
pp = pprint.PrettyPrinter(indent=4)

Expand Down
6 changes: 3 additions & 3 deletions lib/dehinter/bitops.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
# limitations under the License.


def is_bit_k_set(int_val, k):
def is_bit_k_set(int_val: int, k: int) -> bool:
"""Tests if the value of bit at offset k in an integer is set"""
return (int_val & (1 << k)) != 0


def clear_bit_k(int_val, k):
def clear_bit_k(int_val: int, k: int) -> int:
"""Clears the bit at offset k"""
return int_val & ~(1 << k)


def set_bit_k(int_val, k):
def set_bit_k(int_val: int, k: int) -> int:
"""Sets the bit at offset k"""
return int_val | (1 << k)
48 changes: 25 additions & 23 deletions lib/dehinter/font.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,67 +13,69 @@
# limitations under the License.

import array
import os
from typing import Union

from dehinter.bitops import is_bit_k_set, clear_bit_k


# ========================================================
# Utilities
# ========================================================
def has_cvt_table(tt):
def has_cvt_table(tt) -> bool:
"""Tests for the presence of a cvt table in a TrueType font."""
return "cvt " in tt


def has_fpgm_table(tt):
def has_fpgm_table(tt) -> bool:
"""Tests for the presence of a fpgm table in a TrueType font."""
return "fpgm" in tt


def has_gasp_table(tt):
def has_gasp_table(tt) -> bool:
"""Tests for the presence of a gasp table in a TrueType font."""
return "gasp" in tt


def has_hdmx_table(tt):
def has_hdmx_table(tt) -> bool:
"""Tests for the presence of a hdmx table in a TrueType font."""
return "hdmx" in tt


def has_ltsh_table(tt):
def has_ltsh_table(tt) -> bool:
"""Tests for the presence of a LTSH table in a TrueType font."""
return "LTSH" in tt


def has_prep_table(tt):
def has_prep_table(tt) -> bool:
"""Tests for the presence of a prep table in a TrueType font."""
return "prep" in tt


def has_ttfa_table(tt):
def has_ttfa_table(tt) -> bool:
"""Tests for the presence of a TTFA table in a TrueType font."""
return "TTFA" in tt


def has_vdmx_table(tt):
def has_vdmx_table(tt) -> bool:
"""Tests for the presence of a VDMX table in a TrueType font."""
return "VDMX" in tt


def is_truetype_font(filepath):
def is_truetype_font(filepath: Union[bytes, str, "os.PathLike[str]"]) -> bool:
"""Tests that a font has the TrueType file signature of either:
1) b'\x00\x01\x00\x00'
2) b'\x74\x72\x75\x65' == 'true'"""
with open(filepath, "rb") as f:
file_signature = f.read(4)
file_signature: bytes = f.read(4)

return file_signature in (b"\x00\x01\x00\x00", b"\x74\x72\x75\x65")


# ========================================================
# OpenType table removal
# ========================================================
def remove_cvt_table(tt):
def remove_cvt_table(tt) -> None:
"""Removes cvt table from a fontTools.ttLib.TTFont object"""
try:
del tt["cvt "]
Expand All @@ -82,7 +84,7 @@ def remove_cvt_table(tt):
pass


def remove_fpgm_table(tt):
def remove_fpgm_table(tt) -> None:
"""Removes fpgm table from a fontTools.ttLib.TTFont object"""
try:
del tt["fpgm"]
Expand All @@ -91,7 +93,7 @@ def remove_fpgm_table(tt):
pass


def remove_hdmx_table(tt):
def remove_hdmx_table(tt) -> None:
"""Removes hdmx table from a fontTools.ttLib.TTFont object"""
try:
del tt["hdmx"]
Expand All @@ -100,7 +102,7 @@ def remove_hdmx_table(tt):
pass


def remove_ltsh_table(tt):
def remove_ltsh_table(tt) -> None:
"""Removes LTSH table from a fontTools.ttLib.TTFont object."""
try:
del tt["LTSH"]
Expand All @@ -109,7 +111,7 @@ def remove_ltsh_table(tt):
pass


def remove_prep_table(tt):
def remove_prep_table(tt) -> None:
"""Removes prep table from a fontTools.ttLib.TTFont object"""
try:
del tt["prep"]
Expand All @@ -118,7 +120,7 @@ def remove_prep_table(tt):
pass


def remove_ttfa_table(tt):
def remove_ttfa_table(tt) -> None:
"""Removes TTFA table from a fontTools.ttLib.TTFont object"""
try:
del tt["TTFA"]
Expand All @@ -127,7 +129,7 @@ def remove_ttfa_table(tt):
pass


def remove_vdmx_table(tt):
def remove_vdmx_table(tt) -> None:
"""Removes TTFA table from a fontTools.ttLib.TTFont object"""
try:
del tt["VDMX"]
Expand All @@ -139,9 +141,9 @@ def remove_vdmx_table(tt):
# ========================================================
# glyf table instruction set bytecode removal
# ========================================================
def remove_glyf_instructions(tt):
def remove_glyf_instructions(tt) -> int:
"""Removes instruction set bytecode from glyph definitions in the glyf table."""
glyph_number = 0
glyph_number: int = 0
for glyph in tt["glyf"].glyphs.values():
glyph.expand(tt["glyf"])
if hasattr(glyph, "program") and glyph.program.bytecode != array.array("B", []):
Expand All @@ -157,7 +159,7 @@ def remove_glyf_instructions(tt):
# ========================================================
# gasp table edit
# ========================================================
def update_gasp_table(tt):
def update_gasp_table(tt) -> bool:
"""Modifies the following gasp table fields:
1) rangeMaxPPEM changed to 65535
2) rangeGaspBehavior changed to 0x000a (symmetric grayscale, no gridfit)"""
Expand All @@ -171,9 +173,9 @@ def update_gasp_table(tt):
# =========================================
# maxp table edits
# =========================================
def update_maxp_table(tt):
def update_maxp_table(tt) -> bool:
"""Update the maxp table with new values based on elimination of instruction sets."""
changed = False
changed: bool = False
if tt["maxp"].maxZones != 0:
tt["maxp"].maxZones = 0
changed = True
Expand All @@ -198,7 +200,7 @@ def update_maxp_table(tt):
# =========================================
# head table edits
# =========================================
def update_head_table_flags(tt):
def update_head_table_flags(tt) -> bool:
if is_bit_k_set(tt["head"].flags, 4):
# confirm that there is no LTSH or hdmx table
# bit 4 should be set if either of these tables are present in font
Expand Down
7 changes: 5 additions & 2 deletions lib/dehinter/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@
# limitations under the License.

import os
from typing import Union


def filepath_exists(filepath):
def filepath_exists(filepath: Union[bytes, str, "os.PathLike[str]"]) -> bool:
"""Tests a file path string to confirm that the file exists on the file system"""
return os.path.isfile(filepath)


def get_default_out_path(filepath):
def get_default_out_path(
filepath: os.PathLike,
) -> Union[str, bytes]:
"""Returns an updated file path that is used as dehinted file default when user
does not specify an out file path."""
dir_path, file_path = os.path.split(filepath)
Expand Down
3 changes: 2 additions & 1 deletion lib/dehinter/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
# limitations under the License.

import os
from typing import Tuple, Union


def get_filesize(filepath):
def get_filesize(filepath: Union[str, bytes, "os.PathLike[str]"]) -> Tuple[str, str]:
"""Returns formatted file size tuple fit for printing to end user."""
filesize = os.path.getsize(filepath)
kb_factor = 1 << 10
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# Optional packages
EXTRAS_REQUIRES = {
# for developer installs
"dev": ["coverage", "pytest", "tox", "flake8", "pytype"],
"dev": ["coverage", "pytest", "tox", "flake8", "mypy"],
# for maintainer installs
"maintain": ["wheel", "setuptools", "twine"],
}
Expand Down

0 comments on commit 8429037

Please sign in to comment.