Skip to content

Commit

Permalink
Rework encode()'s error handling
Browse files Browse the repository at this point in the history
We no longer use a regular expression to validate the input string.
Instead, the logic within the per-character encoding loop has been
improved to catch all of the known error cases and raise appropriate
ValueError exceptions.

This results in slightly faster code with more useful errors. For
example, we now include the actual character that we've identified as
invalid, which aids in debugging.
  • Loading branch information
jparise committed Dec 30, 2021
1 parent a8b9671 commit c944022
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 32 deletions.
15 changes: 9 additions & 6 deletions tests/test_chars.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest

from vesta.chars import CHARCODES
from vesta.chars import CHARMAP
from vesta.chars import COLS
from vesta.chars import ROWS
from vesta.chars import Color
Expand All @@ -11,18 +12,20 @@

class EncodeTests(unittest.TestCase):
def test_printable_characters(self):
for c in CHARCODES:
for c in CHARMAP:
encode(c)

def test_character_codes(self):
for i in range(0, 70):
for i in CHARCODES:
encode(f"{{{i}}}")

def test_bad_characters(self):
self.assertRaisesRegex(ValueError, "unsupported characters", encode, "<>")
self.assertRaisesRegex(ValueError, "unsupported characters", encode, "{20")
self.assertRaisesRegex(ValueError, "unsupported characters", encode, "{999}")
self.assertRaisesRegex(ValueError, "bad character code", encode, "{99}")
self.assertRaisesRegex(ValueError, "unsupported character: <", encode, "<>")
self.assertRaisesRegex(
ValueError, "unsupported character code: 99", encode, "{99}"
)
self.assertRaisesRegex(ValueError, "missing }", encode, "{20")
self.assertRaisesRegex(ValueError, "missing }", encode, "{999}")


class EncodeRowTests(unittest.TestCase):
Expand Down
39 changes: 13 additions & 26 deletions vesta/chars.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import enum
import math
import re
import sys
from typing import Container
from typing import List
Expand All @@ -13,19 +12,7 @@
ROWS = 6
COLS = 22
PRINTABLE = " ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$() - +&=;: '\"%,. /? °"
CHARCODES = {c: idx for idx, c in enumerate(PRINTABLE) if idx == 0 or c != " "}

# Regular expression that matches supported characters.
VALID_RE = re.compile(
r"""
(?:
[ A-Za-z0-9!@#$()\-+&=;:'\"%,./?°] # Printable Characters
| # or
(?:\{\d{1,2}\}) # Character Codes ({5} or {65})
)*
""",
re.VERBOSE,
)
CHARMAP = {c: i for i, c in enumerate(PRINTABLE) if i == 0 or c != " "}


class Color(enum.IntEnum):
Expand All @@ -51,9 +38,8 @@ def __new__(cls, value: int, ansi: str):
# fmt: on


def iscode(n: int) -> bool:
"""Checks if an integer value is a valid character code."""
return 0 <= n <= 69
# The set of all supported character codes.
CHARCODES = frozenset(CHARMAP.values()).union(Color)


def encode(s: str) -> List[int]:
Expand All @@ -67,28 +53,29 @@ def encode(s: str) -> List[int]:
>>> encode("{67} Hello, World {68}")
[67, 0, 8, 5, 12, 12, 15, 55, 0, 23, 15, 18, 12, 4, 0, 68]
"""
if VALID_RE.fullmatch(s) is None:
raise ValueError(f"{s!r} contains unsupported characters or character codes")

out = []
lens = len(s)
skip_to = 0
for i, c in enumerate(s.upper()):
if i < skip_to:
continue

if c == "{":
if s[i + 2] == "}":
if lens > i + 2 and s[i + 2] == "}":
out.append(int(s[i + 1]))
skip_to = i + 3
elif s[i + 3] == "}":
elif lens > i + 3 and s[i + 3] == "}":
out.append(int(s[i + 1 : i + 3]))
skip_to = i + 4
else:
raise ValueError(f"{i+1}: unmatched '{{'") # pragma: no cover
if not iscode(out[-1]):
raise ValueError(f"{i+2}: bad character code: {out[-1]}")
raise ValueError(f"{i+1}: missing }} at index {i+2} or {i+3}")
if out[-1] not in CHARCODES:
raise ValueError(f"{i+2}: unsupported character code: {out[-1]}")
else:
out.append(CHARCODES[c])
try:
out.append(CHARMAP[c])
except KeyError:
raise ValueError(f"{i+1}: unsupported character: {c}")

return out

Expand Down

0 comments on commit c944022

Please sign in to comment.