Skip to content

Commit

Permalink
Added type validation
Browse files Browse the repository at this point in the history
  • Loading branch information
sg495 committed Dec 13, 2021
1 parent 026b9d5 commit b5d871c
Show file tree
Hide file tree
Showing 28 changed files with 293 additions and 36 deletions.
2 changes: 1 addition & 1 deletion bases/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"""

__version__ = "0.1.1"
__version__ = "0.2.0"

from . import encoding as encoding
from . import alphabet as alphabet
Expand Down
11 changes: 11 additions & 0 deletions bases/alphabet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import re
from typing import Collection, Dict, Iterator, Optional, overload, Tuple, Union
from typing_extensions import Literal
from typing_validation import validate

from .abstract import Alphabet as Alphabet
from .string_alphabet import StringAlphabet as StringAlphabet
Expand All @@ -63,6 +64,7 @@ def get(name: str) -> Alphabet:
StringAlphabet('0123456789ABCDEF')
```
"""
validate(name, str)
if name not in _alphabets:
raise KeyError(f"Alphabet named {repr(name)} does not exist.")
return _alphabets[name]
Expand All @@ -79,6 +81,7 @@ def has(name: str) -> bool:
True
```
"""
validate(name, str)
return name in _alphabets

def register(**kwargs: Alphabet) -> None:
Expand All @@ -101,6 +104,8 @@ def register(**kwargs: Alphabet) -> None:
re.match(r"^base[0-9][a-zA-Z0-9_]*$", name)
```
"""
for arg in kwargs.values():
validate(arg, Alphabet)
for name, alphabet in kwargs.items():
if not re.match(r"^base[0-9][a-zA-Z0-9_]*$", name):
raise ValueError(f"Invalid alphabet name {repr(name)}")
Expand Down Expand Up @@ -133,6 +138,8 @@ def unregister(*names: str) -> None:
StringAlphabet('0123456789ABCDEF')
```
"""
for name in names:
validate(name, str)
for name in names:
if name not in _alphabets:
raise KeyError(f"Alphabet named {repr(name)} does not exist.")
Expand All @@ -153,6 +160,7 @@ def table(*, prefix: str = "") -> Iterator[Tuple[str, Alphabet]]:
```
"""
validate(prefix, str)
alphabets = [(name, alphabet) for name, alphabet in _alphabets.items()
if name.startswith(prefix)]
alphabets = sorted(alphabets, key=lambda pair: pair[0])
Expand Down Expand Up @@ -188,6 +196,9 @@ def make(chars: Union[str, range], *, case_sensitive: bool = True, name: Optiona
If the optional keyword argument `name` is specified, the alphabet is automatically registered using `register`.
"""
validate(chars, Union[str, range])
validate(case_sensitive, bool)
validate(name, Optional[str])
if isinstance(chars, str):
string_alphabet = StringAlphabet(chars, case_sensitive=case_sensitive)
if name is not None:
Expand Down
6 changes: 6 additions & 0 deletions bases/alphabet/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from abc import ABC, abstractmethod
from typing import Any, Mapping, Optional, overload, Sequence, TypeVar, Union
from typing_validation import validate

Self = TypeVar("Self", bound="Alphabet")

Expand All @@ -26,6 +27,7 @@ class Alphabet(ABC, Sequence[str]):
_case_sensitive: bool

def __init__(self, case_sensitive: bool = True):
validate(case_sensitive, bool)
self._case_sensitive = case_sensitive

@property
Expand Down Expand Up @@ -68,6 +70,9 @@ def index(self, char: str, start: int = 0, stop: Optional[int] = None) -> int:
```
"""
# pylint: disable = arguments-renamed
validate(char, str)
validate(start, int)
validate(stop, Optional[int])
if start < 0:
start = max(len(self) + start, 0)
if stop is None:
Expand All @@ -87,6 +92,7 @@ def count(self, char: str) -> int:
Returns 1 if `char` is in the alphabet and 0 otherwise.
"""
# pylint: disable = arguments-renamed
validate(char, str)
return 1 if char in self else 0

def __contains__(self, char: Any) -> bool:
Expand Down
5 changes: 5 additions & 0 deletions bases/alphabet/range_alphabet.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

from typing import Any, Iterator, Mapping, overload, Union
from typing_validation import validate

from .abstract import Alphabet
from .string_alphabet import StringAlphabet
Expand All @@ -28,6 +29,7 @@ class RangeAlphabet(Alphabet):
def __init__(self, codepoints: range, *,
case_sensitive: bool = True):
super().__init__(case_sensitive)
validate(codepoints, range)
self._codepoints = codepoints
self._revdir = _RangeAlphabetRevdir(self)
self.__validate_init()
Expand Down Expand Up @@ -75,12 +77,14 @@ def __getitem__(self, idx: slice) -> "RangeAlphabet":
...

def __getitem__(self, idx: Union[int, slice]) -> Union[str, "RangeAlphabet"]:
validate(idx, Union[int, slice])
if isinstance(idx, slice):
new_codepoints = self._codepoints[idx]
return RangeAlphabet(new_codepoints, case_sensitive=self.case_sensitive)
return chr(self._codepoints[idx])

def with_case_sensitivity(self, case_sensitive: bool) -> "RangeAlphabet":
validate(case_sensitive, bool)
if case_sensitive == self.case_sensitive:
return self
return RangeAlphabet(self.codepoints, case_sensitive=case_sensitive)
Expand Down Expand Up @@ -146,6 +150,7 @@ def __contains__(self, char: Any) -> bool:
return ord(char.upper()) in alphabet.codepoints or ord(char.lower()) in alphabet.codepoints

def __getitem__(self, char: str) -> int:
validate(char, str)
alphabet = self._alphabet
if ord(char) in alphabet.codepoints:
return ord(char)-alphabet.codepoints.start
Expand Down
4 changes: 4 additions & 0 deletions bases/alphabet/string_alphabet.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from types import MappingProxyType
from typing import Any, Mapping, overload, Union
from typing_validation import validate

from .abstract import Alphabet

Expand All @@ -30,6 +31,7 @@ class StringAlphabet(Alphabet):
def __init__(self, chars: str, *,
case_sensitive: bool = True):
super().__init__(case_sensitive)
validate(chars, str)
self._chars = chars
revdir = {
c: idx for idx, c in enumerate(chars)
Expand Down Expand Up @@ -88,12 +90,14 @@ def __getitem__(self, idx: slice) -> "StringAlphabet":
...

def __getitem__(self, idx: Union[int, slice]) -> Union[str, "StringAlphabet"]:
validate(idx, Union[int, slice])
if isinstance(idx, slice):
new_chars = self._chars[idx]
return StringAlphabet(new_chars, case_sensitive=self.case_sensitive)
return self._chars[idx]

def with_case_sensitivity(self, case_sensitive: bool) -> "StringAlphabet":
validate(case_sensitive, bool)
if case_sensitive == self.case_sensitive:
return self
return StringAlphabet(self.chars, case_sensitive=case_sensitive)
Expand Down
12 changes: 12 additions & 0 deletions bases/encoding/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import re
from typing import Any, cast, Collection, Dict, Iterator, Mapping, Optional, overload, Tuple, Type, Union
from typing_extensions import Literal
from typing_validation import validate

from bases import alphabet
from bases.alphabet import Alphabet
Expand All @@ -89,6 +90,7 @@ def get(name: str) -> BaseEncoding:
block_nchars=2)
```
"""
validate(name, str)
if name not in _base_encodings:
raise KeyError(f"Encoding named {repr(name)} does not exist.")
return _base_encodings[name]
Expand All @@ -101,6 +103,7 @@ def has(name: str) -> bool:
True
```
"""
validate(name, str)
return name in _base_encodings

def register(**kwargs: BaseEncoding) -> None:
Expand All @@ -126,6 +129,8 @@ def register(**kwargs: BaseEncoding) -> None:
re.match(r"^base[0-9][a-zA-Z0-9_]*$", name)
```
"""
for arg in kwargs.values():
validate(arg, BaseEncoding)
for name, encoding in kwargs.items():
if not re.match(r"^base[0-9][a-zA-Z0-9_]*$", name):
raise ValueError(f"Invalid encoding name {repr(name)}")
Expand Down Expand Up @@ -161,6 +166,8 @@ def unregister(*names: str) -> None:
block_nchars=2)
```
"""
for name in names:
validate(name, str)
for name in names:
if name not in _base_encodings:
raise KeyError(f"Encoding named {repr(name)} does not exist.")
Expand Down Expand Up @@ -190,6 +197,7 @@ def table(*, prefix: str = "") -> Iterator[Tuple[str, BaseEncoding]]:
```
"""
validate(prefix, str)
encodings = [(name, encoding) for name, encoding in _base_encodings.items()
if name.startswith(prefix)]
encodings = sorted(encodings, key=lambda pair: pair[0])
Expand Down Expand Up @@ -248,6 +256,10 @@ def make(chars: Union[str, range, Alphabet, BaseEncoding], *, kind: str, name: O
```
"""
validate(chars, Union[str, range, Alphabet, BaseEncoding])
validate(kind, str)
validate(name, Optional[str])
validate(case_sensitive, Optional[bool])
kwargs["case_sensitive"] = case_sensitive
if kind == "simple-enc":
if isinstance(chars, BaseEncoding):
Expand Down
12 changes: 8 additions & 4 deletions bases/encoding/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from abc import ABC, abstractmethod
from typing import Any, Mapping, Optional, TypeVar, Union
from typing_validation import validate

from bases import alphabet
from bases.alphabet import Alphabet
Expand All @@ -24,6 +25,8 @@ class BaseEncoding(ABC):

def __init__(self, chars: Union[str, range, Alphabet], *,
case_sensitive: Optional[bool] = None):
validate(chars, Union[str, range, Alphabet])
validate(case_sensitive, Optional[bool])
if isinstance(chars, Alphabet):
if case_sensitive is not None:
chars = chars.with_case_sensitivity(case_sensitive)
Expand Down Expand Up @@ -99,6 +102,8 @@ def with_alphabet(self: Self, chars: Union[str, range, Alphabet], *,
Returns a new encoding with the same kind and options as this one,
but a different alphabet and/or case sensitivity.
"""
validate(chars, Union[str, range, Alphabet])
validate(case_sensitive, Optional[bool])
options = {**self.options()}
options["case_sensitive"] = case_sensitive
return type(self)(chars, **options)
Expand All @@ -121,6 +126,7 @@ def with_case_sensitivity(self: Self, case_sensitive: bool) -> Self:
pad_char='=', padding='include')
```
"""
validate(case_sensitive, bool)
return self.with_alphabet(self.alphabet.with_case_sensitivity(case_sensitive))

def upper(self: Self) -> Self:
Expand Down Expand Up @@ -234,13 +240,11 @@ def canonical_string(self, s: str) -> str:

def _validate_bytes(self, b: bytes) -> bytes:
# pylint: disable = no-self-use
if not isinstance(b, bytes):
raise TypeError()
validate(b, bytes)
return b

def _validate_string(self, s: str) -> str:
if not isinstance(s, str):
raise TypeError()
validate(s, str)
alphabet = self.alphabet
for c in s:
if c not in alphabet:
Expand Down
8 changes: 7 additions & 1 deletion bases/encoding/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import math
from types import MappingProxyType
from typing import Any, Dict, List, Mapping, Optional, Union
from typing_validation import validate

from bases.alphabet import Alphabet
from .base import BaseEncoding
Expand Down Expand Up @@ -93,10 +94,13 @@ def __init__(self, encoding: Union[str, range, Alphabet, BaseEncoding], *,
block_size: Union[int, Mapping[int, int]],
sep_char: str = "",
reverse_blocks: bool = False):
validate(encoding, Union[str, range, Alphabet, BaseEncoding])
validate(block_size, Union[int, Mapping[int, int]])
validate(sep_char, str)
validate(reverse_blocks, bool)
self._init_encoding = encoding
self._init_case_sensitive = case_sensitive
self._init_block_size = block_size

if isinstance(encoding, BaseEncoding):
alphabet: Union[str, range, Alphabet] = encoding.alphabet
else:
Expand Down Expand Up @@ -221,6 +225,7 @@ def _validate_bytes(self, b: bytes) -> bytes:
return b

def _validate_string(self, s: str) -> str:
validate(s, str)
sep_char = self.sep_char
block_nchars = self.block_nchars
if sep_char:
Expand Down Expand Up @@ -293,6 +298,7 @@ def _decode(self, s: str) -> bytes:
return b"".join(byte_blocks)

def options(self, skip_defaults: bool = False) -> Mapping[str, Any]:
validate(skip_defaults, bool)
options: Dict[str, Any] = {
"block_size": self._init_block_size,
}
Expand Down
7 changes: 7 additions & 0 deletions bases/encoding/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import binascii
from typing_validation import validate

from bases.alphabet import Alphabet

Expand All @@ -28,6 +29,8 @@ class InvalidDigitError(EncodingError):
base: int

def __init__(self, digit: int, base: int) -> None:
validate(digit, int)
validate(base, int)
self.digit = digit
self.base = base
if 0 <= digit < base:
Expand Down Expand Up @@ -57,6 +60,8 @@ class NonAlphabeticCharError(DecodingError):
alphabet: Alphabet

def __init__(self, char: str, alphabet: Alphabet) -> None:
validate(char, str)
validate(alphabet, Alphabet)
self.char = char
self.alphabet = alphabet
if char in alphabet:
Expand All @@ -73,6 +78,8 @@ class PaddingError(DecodingError):
expected_padding: int

def __init__(self, padding: int, expected_padding: int) -> None:
validate(padding, int)
validate(expected_padding, int)
self.padding = padding
self.expected_padding = expected_padding
if padding < expected_padding:
Expand Down
Loading

0 comments on commit b5d871c

Please sign in to comment.