Module bases.alphabet

Module containing classes for alphabets.

To access existing alphabets, use the get() function:

    >>> from bases import encoding
    >>> alphabet.get("base16")
    StringAlphabet('0123456789ABCDEF')

To create new alphabets, use the make() function:

    >>> alphabet.make("0123456789abcdef")
    StringAlphabet('0123456789abcdef')

To register new alphabets, use the register() function:

    >>> myalpha = alphabet.make("0123456789abcdef")
    >>> alphabet.register(base16lower=myalpha)

Alternatively, use the optional "name" argument of the make() function:

    >>> alphabet.make("0123456789abcdef", name="base16lower")
    StringAlphabet('0123456789abcdef')
    >>> alphabet.get("base16lower")
    StringAlphabet('0123456789abcdef')

To list alphabets (with optional filtering), use table():

    >>> dict(alphabet.table(prefix="base32"))
    {'base32': StringAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', case_sensitive=False),
     'base32hex': StringAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV', case_sensitive=False),
     'base32z': StringAlphabet('ybndrfg8ejkmcpqxot1uwisza345h769', case_sensitive=False)}
Expand source code
"""
    Module containing classes for alphabets.

    To access existing alphabets, use the `get` function:

    ```py
        >>> from bases import encoding
        >>> alphabet.get("base16")
        StringAlphabet('0123456789ABCDEF')
    ```

    To create new alphabets, use the `make` function:

    ```py
        >>> alphabet.make("0123456789abcdef")
        StringAlphabet('0123456789abcdef')
    ```

    To register new alphabets, use the `register` function:

    ```py
        >>> myalpha = alphabet.make("0123456789abcdef")
        >>> alphabet.register(base16lower=myalpha)
    ```

    Alternatively, use the optional `"name"` argument of the `make` function:

    ```py
        >>> alphabet.make("0123456789abcdef", name="base16lower")
        StringAlphabet('0123456789abcdef')
        >>> alphabet.get("base16lower")
        StringAlphabet('0123456789abcdef')
    ```

    To list alphabets (with optional filtering), use `table`:

    ```py
        >>> dict(alphabet.table(prefix="base32"))
        {'base32': StringAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', case_sensitive=False),
         'base32hex': StringAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV', case_sensitive=False),
         'base32z': StringAlphabet('ybndrfg8ejkmcpqxot1uwisza345h769', case_sensitive=False)}
    ```
"""

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
from .range_alphabet import RangeAlphabet as RangeAlphabet

_alphabets: Dict[str, Alphabet] = {}

def get(name: str) -> Alphabet:
    """
        Gets an alphabet by name, raising `KeyError` if none exists.

        Example usage:

        ```py
        >>> alphabet.get("base16")
        StringAlphabet('0123456789ABCDEF')
        ```
    """
    validate(name, str)
    if name not in _alphabets:
        raise KeyError(f"Alphabet named {repr(name)} does not exist.")
    return _alphabets[name]

def has(name: str) -> bool:
    """
        Checks whether an alphabet with the given name exists.

        Example usage:

        ```py
        >>> from bases import alphabet
        >>> alphabet.has("base32")
        True
        ```
    """
    validate(name, str)
    return name in _alphabets

def register(**kwargs: Alphabet) -> None:
    """
        Registers any number of new alphabets by name.
        Raises `KeyError` is an alphabet with one of the given name already exists.

        Example usage:

        ```py
        >>> from bases import alphabet
        >>> alphabet.register(base16lower=alphabet.base16.lower())
        >>> alphabet.get("base16lower")
        StringAlphabet('0123456789abcdef')
        ```

        Alphabet names must conform with:

        ```py
        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)}")
        if not isinstance(alphabet, Alphabet):
            raise TypeError()
        if name in _alphabets:
            raise ValueError(f"Alphabet named {repr(name)} already exists.")
        _alphabets[name] = alphabet

def unregister(*names: str) -> None:
    """
        Unregisters any number of existing alphabets by name.
        Raises `KeyError` is an alphabet with one of the given names doesn't exist.

        Example usage:

        ```py
        >>> from bases import alphabet
        >>> alphabet.unregister("base16", "base32")
        >>> alphabet.has("base16")
        False
        >>> alphabet.get("base16")
        KeyError: "Alphabet named 'base16' does not exist."
        ```

        Note that pre-defined constants are unaffected by this:

        ```py
        >>> alphabet.base16
        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.")
        del _alphabets[name]

def table(*, prefix: str = "") -> Iterator[Tuple[str, Alphabet]]:
    """
        Iterates over all registered alphabets, optionally restricting to those with given prefix.

        Example usage:

        ```py
        >>> from bases import alphabet
        >>> dict(alphabet.table(prefix="base32"))
        {'base32': StringAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', case_sensitive=False),
         'base32hex': StringAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV', case_sensitive=False),
         'base32z': StringAlphabet('ybndrfg8ejkmcpqxot1uwisza345h769', case_sensitive=False)}
        ```

    """
    validate(prefix, str)
    alphabets = [(name, alphabet) for name, alphabet in _alphabets.items()
                 if name.startswith(prefix)]
    alphabets = sorted(alphabets, key=lambda pair: pair[0])
    return iter(alphabets)

@overload
def make(chars: str, *, case_sensitive: bool = True, name: Optional[str] = None) -> StringAlphabet:
    ...

@overload
def make(chars: range, *, case_sensitive: bool = True, name: Optional[str] = None) -> RangeAlphabet:
    ...

def make(chars: Union[str, range], *, case_sensitive: bool = True, name: Optional[str] = None) -> Union[StringAlphabet, RangeAlphabet]:
    """
        Utility function to create custom alphabets.
        Automatically creates an instance of `bases.alphabet.string_alphabet.StringAlphabet`
        or `bases.alphabet.range_alphabet.RangeAlphabet` based on whether a string or range is passed.

        Example usage with string (case-insensitive base-16):

        ```py
        >>> alphabet.make("0123456789ABCDEF", case_sensitive=False)
        StringAlphabet('0123456789ABCDEF', case_sensitive=False)
        ```

        Example usage with range (extended ASCII):

        ```py
        >>> alphabet.make(range(0x00, 0x100))
        RangeAlphabet(range(0x00, 0x100))
        ```

        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:
            register(**{name: string_alphabet})
        return string_alphabet
    if isinstance(chars, range):
        range_alphabet = RangeAlphabet(chars, case_sensitive=case_sensitive)
        if name is not None:
            register(**{name: range_alphabet})
        return range_alphabet
    raise NotImplementedError(f"Character type {type(chars)} not supported.")

base2 = make("01", name="base2")
""" Base-2 alphabet. """

base8 = make("01234567", name="base8")
""" Base-8 alphabet. """

base16 = make("0123456789ABCDEF", case_sensitive=False, name="base16")
""" Uppercase case-insensitive base-16 alphabet. """

base32 = make("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", case_sensitive=False, name="base32")
""" Uppercase case-insensitive base-32 alphabet from [rfc4648](https://datatracker.ietf.org/doc/html/rfc4648). """

base32hex = make("0123456789ABCDEFGHIJKLMNOPQRSTUV", case_sensitive=False, name="base32hex")
""" Uppercase case-insensitive hex base-32 alphabet from [rfc4648](https://datatracker.ietf.org/doc/html/rfc4648). """

base32z = make("ybndrfg8ejkmcpqxot1uwisza345h769", case_sensitive=False, name="base32z")
"""
    Lowercase case-insensitive human-oriented base-32 alphabet from https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
"""

base64 = make("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", name="base64")
""" Uppercase case-insensitive base-64 alphabet from [rfc4648](https://datatracker.ietf.org/doc/html/rfc4648). """

base64url = make("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", name="base64url")
""" Uppercase case-insensitive url-safe base-64 alphabet from [rfc4648](https://datatracker.ietf.org/doc/html/rfc4648). """

base10 = make("0123456789", name="base10")
""" Base-10 alphabet. """

base36 = make("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", case_sensitive=False, name="base36")
""" Uppercase case-insensitive base-36 alphabet. """

base58btc = make("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", name="base58btc")
""" Case-sensitive base-58 alphabet used by Bitcoin. """

base58flickr = make("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", name="base58flickr")
""" Case-sensitive base-58 alphabet used by Flickr. """

base58ripple = make("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz", name="base58ripple")
""" Case-sensitive base-58 alphabet used by Ripple. """

base45 = make("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", case_sensitive=False, name="base45")
"""
    Uppercase case-insensitive base-45 alphabet from https://datatracker.ietf.org/doc/draft-faltstrom-base45/
"""

Sub-modules

bases.alphabet.abstract

Abstract alphabets.

bases.alphabet.range_alphabet

Alphabets implicitly specified by Unicode codepoint range.

bases.alphabet.string_alphabet

Alphabets explicitly specified by strings.

Global variables

var base10

Base-10 alphabet.

var base16

Uppercase case-insensitive base-16 alphabet.

var base2

Base-2 alphabet.

var base32

Uppercase case-insensitive base-32 alphabet from rfc4648.

var base32hex

Uppercase case-insensitive hex base-32 alphabet from rfc4648.

var base32z

Lowercase case-insensitive human-oriented base-32 alphabet from https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt

var base36

Uppercase case-insensitive base-36 alphabet.

var base45

Uppercase case-insensitive base-45 alphabet from https://datatracker.ietf.org/doc/draft-faltstrom-base45/

var base58btc

Case-sensitive base-58 alphabet used by Bitcoin.

var base58flickr

Case-sensitive base-58 alphabet used by Flickr.

var base58ripple

Case-sensitive base-58 alphabet used by Ripple.

var base64

Uppercase case-insensitive base-64 alphabet from rfc4648.

var base64url

Uppercase case-insensitive url-safe base-64 alphabet from rfc4648.

var base8

Base-8 alphabet.

Functions

def get(name: str) ‑> Alphabet

Gets an alphabet by name, raising KeyError if none exists.

Example usage:

>>> alphabet.get("base16")
StringAlphabet('0123456789ABCDEF')
Expand source code
def get(name: str) -> Alphabet:
    """
        Gets an alphabet by name, raising `KeyError` if none exists.

        Example usage:

        ```py
        >>> alphabet.get("base16")
        StringAlphabet('0123456789ABCDEF')
        ```
    """
    validate(name, str)
    if name not in _alphabets:
        raise KeyError(f"Alphabet named {repr(name)} does not exist.")
    return _alphabets[name]
def has(name: str) ‑> bool

Checks whether an alphabet with the given name exists.

Example usage:

>>> from bases import alphabet
>>> alphabet.has("base32")
True
Expand source code
def has(name: str) -> bool:
    """
        Checks whether an alphabet with the given name exists.

        Example usage:

        ```py
        >>> from bases import alphabet
        >>> alphabet.has("base32")
        True
        ```
    """
    validate(name, str)
    return name in _alphabets
def make(chars: Union[str, range], *, case_sensitive: bool = True, name: Optional[str] = None) ‑> Union[StringAlphabetRangeAlphabet]

Utility function to create custom alphabets. Automatically creates an instance of StringAlphabet or RangeAlphabet based on whether a string or range is passed.

Example usage with string (case-insensitive base-16):

>>> alphabet.make("0123456789ABCDEF", case_sensitive=False)
StringAlphabet('0123456789ABCDEF', case_sensitive=False)

Example usage with range (extended ASCII):

>>> alphabet.make(range(0x00, 0x100))
RangeAlphabet(range(0x00, 0x100))

If the optional keyword argument name is specified, the alphabet is automatically registered using register().

Expand source code
def make(chars: Union[str, range], *, case_sensitive: bool = True, name: Optional[str] = None) -> Union[StringAlphabet, RangeAlphabet]:
    """
        Utility function to create custom alphabets.
        Automatically creates an instance of `bases.alphabet.string_alphabet.StringAlphabet`
        or `bases.alphabet.range_alphabet.RangeAlphabet` based on whether a string or range is passed.

        Example usage with string (case-insensitive base-16):

        ```py
        >>> alphabet.make("0123456789ABCDEF", case_sensitive=False)
        StringAlphabet('0123456789ABCDEF', case_sensitive=False)
        ```

        Example usage with range (extended ASCII):

        ```py
        >>> alphabet.make(range(0x00, 0x100))
        RangeAlphabet(range(0x00, 0x100))
        ```

        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:
            register(**{name: string_alphabet})
        return string_alphabet
    if isinstance(chars, range):
        range_alphabet = RangeAlphabet(chars, case_sensitive=case_sensitive)
        if name is not None:
            register(**{name: range_alphabet})
        return range_alphabet
    raise NotImplementedError(f"Character type {type(chars)} not supported.")
def register(**kwargs: Alphabet) ‑> None

Registers any number of new alphabets by name. Raises KeyError is an alphabet with one of the given name already exists.

Example usage:

>>> from bases import alphabet
>>> alphabet.register(base16lower=alphabet.base16.lower())
>>> alphabet.get("base16lower")
StringAlphabet('0123456789abcdef')

Alphabet names must conform with:

re.match(r"^base[0-9][a-zA-Z0-9_]*$", name)
Expand source code
def register(**kwargs: Alphabet) -> None:
    """
        Registers any number of new alphabets by name.
        Raises `KeyError` is an alphabet with one of the given name already exists.

        Example usage:

        ```py
        >>> from bases import alphabet
        >>> alphabet.register(base16lower=alphabet.base16.lower())
        >>> alphabet.get("base16lower")
        StringAlphabet('0123456789abcdef')
        ```

        Alphabet names must conform with:

        ```py
        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)}")
        if not isinstance(alphabet, Alphabet):
            raise TypeError()
        if name in _alphabets:
            raise ValueError(f"Alphabet named {repr(name)} already exists.")
        _alphabets[name] = alphabet
def table(*, prefix: str = '') ‑> Iterator[Tuple[str, Alphabet]]

Iterates over all registered alphabets, optionally restricting to those with given prefix.

Example usage:

>>> from bases import alphabet
>>> dict(alphabet.table(prefix="base32"))
{'base32': StringAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', case_sensitive=False),
 'base32hex': StringAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV', case_sensitive=False),
 'base32z': StringAlphabet('ybndrfg8ejkmcpqxot1uwisza345h769', case_sensitive=False)}
Expand source code
def table(*, prefix: str = "") -> Iterator[Tuple[str, Alphabet]]:
    """
        Iterates over all registered alphabets, optionally restricting to those with given prefix.

        Example usage:

        ```py
        >>> from bases import alphabet
        >>> dict(alphabet.table(prefix="base32"))
        {'base32': StringAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', case_sensitive=False),
         'base32hex': StringAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV', case_sensitive=False),
         'base32z': StringAlphabet('ybndrfg8ejkmcpqxot1uwisza345h769', case_sensitive=False)}
        ```

    """
    validate(prefix, str)
    alphabets = [(name, alphabet) for name, alphabet in _alphabets.items()
                 if name.startswith(prefix)]
    alphabets = sorted(alphabets, key=lambda pair: pair[0])
    return iter(alphabets)
def unregister(*names: str) ‑> None

Unregisters any number of existing alphabets by name. Raises KeyError is an alphabet with one of the given names doesn't exist.

Example usage:

>>> from bases import alphabet
>>> alphabet.unregister("base16", "base32")
>>> alphabet.has("base16")
False
>>> alphabet.get("base16")
KeyError: "Alphabet named 'base16' does not exist."

Note that pre-defined constants are unaffected by this:

>>> alphabet.base16
StringAlphabet('0123456789ABCDEF')
Expand source code
def unregister(*names: str) -> None:
    """
        Unregisters any number of existing alphabets by name.
        Raises `KeyError` is an alphabet with one of the given names doesn't exist.

        Example usage:

        ```py
        >>> from bases import alphabet
        >>> alphabet.unregister("base16", "base32")
        >>> alphabet.has("base16")
        False
        >>> alphabet.get("base16")
        KeyError: "Alphabet named 'base16' does not exist."
        ```

        Note that pre-defined constants are unaffected by this:

        ```py
        >>> alphabet.base16
        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.")
        del _alphabets[name]