Skip to content

Add type annotations for library and tests. #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions adafruit_pioasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
* Author(s): Scott Shawcroft
"""

try:
from typing import List, MutableSequence
except ImportError:
pass

import array
import re

Expand Down Expand Up @@ -40,14 +45,14 @@ class Program: # pylint: disable=too-few-public-methods

"""

def __init__(self, text_program: str, *, build_debuginfo=False) -> None:
def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None:
"""Converts pioasm text to encoded instruction bytes"""
# pylint: disable=too-many-branches,too-many-statements,too-many-locals
assembled = []
assembled: List[int] = []
program_name = None
labels = {}
linemap = []
instructions = []
instructions: List[str] = []
sideset_count = 0
sideset_enable = 0
wrap = None
Expand Down Expand Up @@ -86,9 +91,8 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None:

max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1
assembled = []
for instruction in instructions:
# print(instruction)
instruction = splitter(instruction.strip())
for line in instructions:
instruction = splitter(line.strip())
delay = 0
if instruction[-1].endswith("]"): # Delay
delay = int(instruction[-1].strip("[]"), 0)
Expand Down Expand Up @@ -253,16 +257,13 @@ def __init__(self, text_program: str, *, build_debuginfo=False) -> None:

self.assembled = array.array("H", assembled)

if build_debuginfo:
self.debuginfo = (linemap, text_program)
else:
self.debuginfo = None
self.debuginfo = (linemap, text_program) if build_debuginfo else None

def print_c_program(self, name, qualifier="const"):
def print_c_program(self, name: str, qualifier: str = "const") -> None:
"""Print the program into a C program snippet"""
if self.debuginfo is None:
linemap = None
program_lines = None
linemap = []
program_lines = []
else:
linemap = self.debuginfo[0][:] # Use a copy since we destroy it
program_lines = self.debuginfo[1].split("\n")
Expand Down Expand Up @@ -306,7 +307,7 @@ def print_c_program(self, name, qualifier="const"):
print()


def assemble(program_text: str) -> array.array:
def assemble(program_text: str) -> MutableSequence[int]:
"""Converts pioasm text to encoded instruction bytes

In new code, prefer to use the `Program` class so that the extra arguments
Expand Down
19 changes: 13 additions & 6 deletions tests/pytest_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@
Pytest helper functions
"""

try:
from typing import Any, List, Optional, Type
except ImportError:
pass

import pytest

import adafruit_pioasm


def nice_opcode(opcode):
opcode = f"{opcode:016b}"
return opcode[:3] + "_" + opcode[3:8] + "_" + opcode[8:]
def nice_opcode(opcode: int) -> str:
nice = f"{opcode:016b}"
return nice[:3] + "_" + nice[3:8] + "_" + nice[8:]


def assert_assembles_to(source, expected):
def assert_assembles_to(source: str, expected: List[int]) -> None:
actual = adafruit_pioasm.assemble(source)
expected_bin = [nice_opcode(x) for x in expected]
actual_bin = [nice_opcode(x) for x in actual]
Expand All @@ -25,7 +30,9 @@ def assert_assembles_to(source, expected):
), f"Assembling {source!r}: Expected {expected_bin}, got {actual_bin}"


def assert_assembly_fails(source, match=None, errtype=RuntimeError):
def assert_assembly_fails(
source: str, match: Optional[str] = None, errtype: Type[Exception] = RuntimeError
) -> None:
with pytest.raises(errtype, match=match):
adafruit_pioasm.assemble(source)
# if match:
Expand All @@ -36,6 +43,6 @@ def assert_assembly_fails(source, match=None, errtype=RuntimeError):
# adafruit_pioasm.assemble(source)


def assert_pio_kwargs(source, **kw):
def assert_pio_kwargs(source: str, **kw: Any) -> None:
program = adafruit_pioasm.Program(source)
assert kw == program.pio_kwargs
4 changes: 2 additions & 2 deletions tests/test_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ def test_in_delay_with_sideset() -> None:
assert_assembles_to("\n".join(source), [0b010_10_101_000_10001])


def test_in_bad_source():
def test_in_bad_source() -> None:
assert_assembly_fails(
"in bad, 17", match="Invalid in source 'bad'", errtype=ValueError
)


def test_in_bad_bitcount():
def test_in_bad_bitcount() -> None:
assert_assembly_fails(
"in pins, 0", match="Count out of range", errtype=RuntimeError
)
6 changes: 3 additions & 3 deletions tests/test_mov.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@
from pytest_helpers import assert_assembles_to, assert_assembly_fails


def test_mov_non_happy():
def test_mov_non_happy() -> None:
# non happy path
assert_assembly_fails(
"mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError
)


def test_mov_invert():
def test_mov_invert() -> None:
# test moving and inverting
assert_assembles_to("mov x, ~ x", [0b101_00000_001_01_001])
assert_assembles_to("mov x, ~x", [0b101_00000_001_01_001])
assert_assembles_to("mov x, !x", [0b101_00000_001_01_001])


def test_mov_reverse():
def test_mov_reverse() -> None:
# test moving and reversing bits
assert_assembles_to("mov x, :: x", [0b101_00000_001_10_001])
assert_assembles_to("mov x, ::x", [0b101_00000_001_10_001])
16 changes: 8 additions & 8 deletions tests/test_nop.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
from pytest_helpers import assert_assembles_to, assert_assembly_fails, assert_pio_kwargs


def test_nonsense():
def test_nonsense() -> None:
assert_assembly_fails("nope")


def test_nop():
def test_nop() -> None:
assert_assembles_to("nop", [0b101_00000_010_00_010])
assert_assembles_to("nop\nnop", [0b101_00000_010_00_010, 0b101_00000_010_00_010])
assert_assembles_to("nop [1]", [0b101_00001_010_00_010])
Expand All @@ -22,7 +22,7 @@ def test_nop():
assert_assembles_to(".side_set 1\nnop side 1 [15]", [0b101_11111_010_00_010])


def test_sideset_opt():
def test_sideset_opt() -> None:
assert_assembles_to(".side_set 1 opt\nnop side 1", [0b101_11000_010_00_010])
assert_assembles_to(".side_set 1 opt\nnop side 0", [0b101_10000_010_00_010])
assert_assembles_to(".side_set 1 opt\nnop side 0 [1]", [0b101_10001_010_00_010])
Expand All @@ -32,14 +32,14 @@ def test_sideset_opt():
assert_assembles_to(".side_set 1 opt\nnop side 0 [7]", [0b101_10111_010_00_010])


def test_set():
def test_set() -> None:
# non happy path
assert_assembly_fails(
"set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError
)


def test_jmp():
def test_jmp() -> None:
assert_assembles_to("l:\njmp l", [0b000_00000_000_00000])
assert_assembles_to("l:\njmp 7", [0b000_00000_000_00111])
assert_assembles_to("jmp l\nl:", [0b000_00000_000_00001])
Expand All @@ -56,7 +56,7 @@ def test_jmp():
)


def test_wait():
def test_wait() -> None:
assert_assembles_to("wait 0 gpio 0", [0b001_00000_0_00_00000])
assert_assembles_to("wait 0 gpio 1", [0b001_00000_0_00_00001])
assert_assembles_to("wait 1 gpio 2", [0b001_00000_1_00_00010])
Expand All @@ -69,15 +69,15 @@ def test_wait():
assert_assembles_to("wait 0 irq 1 rel", [0b001_00000_0_10_10001])


def test_limits():
def test_limits() -> None:
assert_assembly_fails(".side_set 1\nnop side 2")
assert_assembly_fails(".side_set 1\nnop side 2 [1]")
assert_assembly_fails("nop [32]")
assert_assembly_fails(".side_set 1\nnop side 0 [16]")
assert_assembly_fails(".side_set 1 opt\nnop side 0 [8]")


def test_cls():
def test_cls() -> None:
assert_pio_kwargs("", sideset_enable=False)
assert_pio_kwargs(".side_set 1", sideset_pin_count=1, sideset_enable=False)
assert_pio_kwargs(".side_set 3 opt", sideset_pin_count=3, sideset_enable=True)
4 changes: 2 additions & 2 deletions tests/test_out.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ def test_out_delay_with_sideset() -> None:
assert_assembles_to("\n".join(source), [0b011_10_101_000_10001])


def test_out_bad_destination():
def test_out_bad_destination() -> None:
assert_assembly_fails(
"out bad, 17", match="Invalid out destination 'bad'", errtype=ValueError
)


def test_out_bad_bitcount():
def test_out_bad_bitcount() -> None:
assert_assembly_fails(
"out pins, 0", match="Count out of range", errtype=RuntimeError
)
2 changes: 1 addition & 1 deletion tests/test_pseudo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
from pytest_helpers import assert_pio_kwargs


def test_offset():
def test_offset() -> None:
assert_pio_kwargs(".origin 7", offset=7, sideset_enable=False)
6 changes: 3 additions & 3 deletions tests/test_radix.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
from pytest_helpers import assert_assembles_to


def test_octal():
def test_octal() -> None:
assert_assembles_to(".side_set 0o1\nset x, 0o11", [0b111_00000_001_01001])


def test_binary():
def test_binary() -> None:
assert_assembles_to(".side_set 0b101\nnop side 0b10001", [0b101_10001_010_00_010])


def test_hex():
def test_hex() -> None:
assert_assembles_to(".side_set 0x0\nnop [0x10]", [0b101_10000_010_00_010])
2 changes: 1 addition & 1 deletion tests/test_wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pytest_helpers import assert_assembly_fails, assert_pio_kwargs


def test_wrap():
def test_wrap() -> None:
assert_assembly_fails(".wrap")
assert_pio_kwargs(
"nop\n.wrap_target\nnop\nnop\n.wrap",
Expand Down
Loading