types - Common data types

Overview

This module provides collection of classes that are often used by other library modules or applications.

Exceptions

exception firebird.base.types.Error(*args, **kwargs)[source]

Bases: Exception

Exception intended as a base for application-related errors.

Unlike the standard Exception, this class accepts arbitrary keyword arguments during initialization. These keyword arguments are stored as attributes on the exception instance.

Attribute lookup on instances of Error (or its subclasses) will return None for any attribute that was not explicitly set via keyword arguments during __init__, preventing AttributeError for common checks.

Important

Attribute lookup on this class never fails, as all attributes that are not actually set, have None value. The special attribute __notes__ (used by add_note since Python 3.11) is explicitly excluded from this behavior to ensure compatibility.

Example:

try:
    if condition:
        raise Error("Error message", err_code=1)
    else:
        raise Error("Unknown error")
except Error as e:
    if e.err_code is None:
        ...
    elif e.err_code == 1:
        ...

Note

Warnings are not errors and should typically derive from Warning, not this class.

Singletons

Singleton is a pattern that restricts the instantiation of a class to one “single” instance. This is useful when exactly one object is needed to coordinate actions across the system.

Common uses:

  • The abstract factory, factory method, builder, and prototype patterns can use singletons in their implementation.

  • Facade objects are often singletons because only one facade object is required.

  • State objects are often singletons.

  • Singletons are often preferred to global variables because:

    • They do not pollute the global namespace with unnecessary variables.

    • They permit lazy allocation and initialization.

To create your own singletons, use Singleton as the base class.

example

>>> class MySingleton(Singleton):
...     "Description"
...     ...
...
>>> obj1 = MySingleton()
>>> obj1 = MySingleton()
>>> obj1 is obj2
True
class firebird.base.types.Singleton(*args, **kwargs)[source]

Bases: object

Base class for singletons.

Ensures that only one instance of a class derived from Singleton exists. Subsequent attempts to ‘create’ an instance will return the existing one.

Important

If a descendant class’s __init__ method accepts arguments, these arguments are only used the first time the instance is created. Subsequent calls that retrieve the cached instance will not invoke __init__ again.

Example:

class MyService(Singleton):
    def __init__(self, config_param=None):
        if hasattr(self, '_initialized'): # Prevent re-init
            return
        print("Initializing MyService...")
        self.config = config_param
        self._initialized = True

    def do_something(self):
        print(f"Doing something with config: {self.config}")

service1 = MyService("config1") # Prints "Initializing MyService..."
service2 = MyService("config2") # Does *not* print, returns existing instance

print(service1 is service2) # Output: True
service2.do_something()     # Output: Doing something with config: config1
Return type:

Singleton

Sentinels

The Sentinel Object pattern is a standard Pythonic approach that’s used both in the Standard Library and beyond. The pattern most often uses Python’s built-in None object, but in situations where None might be a useful value, a unique sentinel object() can be used instead to indicate missing or unspecified data, or other specific condition.

However, the plain object() sentinel has not very useful str and repr values. The Sentinel class provides named sentinels, with meaningful str and repr.


class firebird.base.types.Sentinel(name, bases=None, namespace=None, /, *, repr=None)[source]

Bases: _SentinelMeta

Base class for creating unique sentinel objects.

Sentinels are special singleton objects used to signal unique states or conditions, particularly useful when None might be a valid data value. They offer a more explicit and readable alternative to magic constants or using object().

You can define specific sentinels in two primary ways:

  1. By Subclassing: Inherit directly from Sentinel. The name of the subclass becomes the sentinel’s identity.

    class DEFAULT(Sentinel):
        "Represents a default value placeholder."
    
    class ALL(Sentinel):
        "Represents all possible values."
    

    This creates classes DEFAULT and ALL, each acting as a unique sentinel object.

  2. Using the Functional Call: Use the Sentinel base class itself as a factory function.

    # Signature: Sentinel(name: str, *, repr: str | None = None) -> Sentinel
    NOT_FOUND = Sentinel("NOT_FOUND", repr="<Value Not Found>")
    UNKNOWN = Sentinel("UNKNOWN")
    
    • The required name argument (e.g., "NOT_FOUND") specifies the __name__ of the dynamically created sentinel class.

    • The optional repr keyword argument provides a custom string to be returned by repr() for this specific sentinel. If omitted, repr() defaults to the sentinel’s name.

    This dynamically creates new classes derived from Sentinel, assigns them to the variables (NOT_FOUND, UNKNOWN), and sets a custom __repr__ if provided.

Behavior:

Regardless of the creation method:

  • Each sentinel is a unique object (a class behaving as a singleton).

  • Sentinels are identified using the is operator.

  • They cannot be instantiated (e.g., DEFAULT() raises TypeError).

  • They cannot be subclassed further after their initial definition.

  • str(MySentinel) returns the sentinel’s name (MySentinel.__name__).

  • repr(MySentinel) returns the custom repr if provided via the functional call, otherwise it defaults to the sentinel’s name.

Example Usage:

# Define using subclassing
class DEFAULT_SETTING(Sentinel):
    "Indicates a setting should use its compiled-in default."

# Define using functional call with custom repr
NOT_APPLICABLE = Sentinel("NOT_APPLICABLE", repr="<N/A>")

def get_config(key, user_override=NOT_APPLICABLE):
    if user_override is NOT_APPLICABLE:
        # User did not provide an override, check stored config
        value = read_stored_config(key, default=DEFAULT_SETTING)
        if value is DEFAULT_SETTING:
            return get_hardcoded_default(key)
        return value
    else:
        # User provided an override (which could be None)
        return user_override

config1 = get_config("timeout") # Uses stored or hardcoded default
config2 = get_config("retries", user_override=None) # Explicitly set to None
config3 = get_config("feature_flag", user_override=NOT_APPLICABLE) # Same as providing nothing

print(repr(DEFAULT_SETTING)) # Output: DEFAULT_SETTING
print(repr(NOT_APPLICABLE))  # Output: <N/A>
Return type:

type[Sentinel]

Predefined sentinels

firebird.base.types.DEFAULT = DEFAULT[source]

Sentinel that denotes default value

firebird.base.types.INFINITY = INFINITY[source]

Sentinel that denotes infinity value

firebird.base.types.UNLIMITED = UNLIMITED[source]

Sentinel that denotes unlimited value

firebird.base.types.UNKNOWN = UNKNOWN[source]

Sentinel that denotes unknown value

firebird.base.types.NOT_FOUND = NOT_FOUND[source]

Sentinel that denotes a condition when value was not found

firebird.base.types.UNDEFINED = UNDEFINED[source]

Sentinel that denotes explicitly undefined value

firebird.base.types.ANY = ANY[source]

Sentinel that denotes any value

firebird.base.types.ALL = ALL[source]

Sentinel that denotes all possible values

firebird.base.types.SUSPEND = SUSPEND[source]

Sentinel that denotes suspend request (in message queue)

firebird.base.types.RESUME = RESUME[source]

Sentinel that denotes resume request (in message queue)

firebird.base.types.STOP = STOP[source]

Sentinel that denotes stop request (in message queue)

Distinct objects

Some complex data structures or data processing algorithms require unique object identification (ie object identity). In Python, an object identity is defined internally as unique instance identity that is not suitable for complex objects whose identity is derived from content.

The Distinct abstract base class is intended as a unified solution to these needs.

See also

module firebird.base.collections


class firebird.base.types.Distinct[source]

Bases: ABC

Abstract base class for objects with distinct instances based on a key.

Instances are considered equal (==) if their keys, returned by get_key(), are equal. The hash of an instance is derived from the hash of its key by default.

Important

If used with @dataclass, it must be defined with eq=False to prevent overriding the custom __eq__ and __hash__ methods:

from dataclasses import dataclass

@dataclass(eq=False)
class MyDistinctData(Distinct):
    id: int
    name: str

    def get_key(self) -> Hashable:
        return self.id
abstract get_key() Hashable[source]

Return the unique key identifying this instance.

The key must be hashable. It determines equality and hashing behavior unless __eq__ or __hash__ are explicitly overridden.

Return type:

Hashable


class firebird.base.types.CachedDistinct(*args, **kwargs)[source]

Bases: Distinct

Abstract Distinct descendant that caches instances.

Behaves like Distinct, but ensures only one instance is created per unique key. Subsequent attempts to create an instance with the same key (as determined by extract_key from the constructor arguments) will return the cached instance instead of creating a new one.

Instances are stored in a class-level WeakValueDictionary, allowing them to be garbage-collected if no longer referenced elsewhere.

Requires implementation of both get_key() (for instance equality/hashing) and extract_key() (for retrieving the key from constructor arguments before instance creation). These two methods should conceptually return the same identifier for a given object identity.

Important

Like Distinct, if used with @dataclass, define with eq=False.

Example:

from dataclasses import dataclass

@dataclass(eq=False) # Important!
class User(CachedDistinct):
    user_id: int
    name: str

    def get_key(self) -> int:
        return self.user_id

    @classmethod
    def extract_key(cls, user_id: int, name: str) -> int:
        # Extracts the key from __init__ args
        return user_id

user1 = User(1, "Alice")
user2 = User(2, "Bob")
user3 = User(1, "Alice") # Name might be different here, but key is the same

print(user1 is user3)  # Output: True (cached instance returned)
print(user1 == user3)  # Output: True (equality based on get_key)
print(user1 is user2)  # Output: False
Return type:

CachedDistinct

abstract classmethod extract_key(*args, **kwargs) Hashable[source]

Returns key from arguments passed to __init__().

Important

The key is used to store instance in cache. It should be the same as key returned by instance Distinct.get_key()!

Return type:

Hashable

Enums

class firebird.base.types.ByteOrder(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: Enum

Byte order for storing numbers in binary MemoryBuffer.

BIG = 'big'
LITTLE = 'little'
NETWORK = 'big'

class firebird.base.types.ZMQTransport(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: IntEnum

ZeroMQ transport protocol.

EPGM = 5
INPROC = 1
IPC = 2
PGM = 4
TCP = 3
UNKNOWN = 0
VMCI = 6

class firebird.base.types.ZMQDomain(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: IntEnum

ZeroMQ address domain.

LOCAL = 1
NETWORK = 3
NODE = 2
UNKNOWN = 0

Custom string types

Some string values have unified structure and carry specific information (like network address or database connection string). Typical repeating operation with these values are validation and parsing. It makes sense to put these operations under one roof. One such approach uses custom descendants of builtin str type.

Caution

Custom string types have an inherent weakness. They support all inherited string methods, but any method that returns string value return a base str type, not the descendant class type. That same apply when you assign strings to variables that should be of custom string type.

Tip

Module strconv could help you to safely translate strings stored externally to typed strings.


class firebird.base.types.ZMQAddress(value: AnyStr, encoding: str = 'utf8')[source]

Bases: str

ZeroMQ endpoint address.

It behaves like str, but checks that value is valid ZMQ endpoint address, has additional R/O properties and meaningful repr().

Raises:

ValueError – When string value passed to constructor is not a valid ZMQ endpoint address.

Parameters:
  • value (AnyStr) –

  • encoding (str) –

Return type:

Self

Example:

addr_str = "tcp://127.0.0.1:5555"
zmq_addr = ZMQAddress(addr_str)

print(zmq_addr)                 # Output: tcp://127.0.0.1:5555
print(repr(zmq_addr))           # Output: ZMQAddress('tcp://127.0.0.1:5555')
print(zmq_addr.protocol)        # Output: ZMQTransport.TCP
print(zmq_addr.address)         # Output: 127.0.0.1:5555
print(zmq_addr.domain)          # Output: ZMQDomain.NODE

try:
    invalid = ZMQAddress("myfile.txt")
except ValueError as e:
    print(e)                    # Output: Protocol specification required
property address: str

//’).

Type:

Endpoint address part (following ‘

property domain: ZMQDomain

Endpoint address domain (LOCAL, NODE, NETWORK).

property protocol: ZMQTransport

Transport protocol (e.g., TCP, IPC, INPROC).

class firebird.base.types.MIME(value: str)[source]

Bases: str

MIME type specification string (e.g., ‘text/plain; charset=utf-8’).

Behaves like str, but validates the input format (type/subtype[;params]) upon creation and provides convenient read-only properties to access parts of the specification.

Raises:

ValueError – If the input string is not a valid MIME type specification (missing ‘/’, unsupported type, invalid parameters).

Parameters:

value (str) –

Return type:

Self

Example:

mime1_str = "application/json"
mime1 = MIME(mime1_str)
print(mime1)            # Output: application/json
print(repr(mime1))      # Output: MIME('application/json')
print(mime1.type)       # Output: application
print(mime1.subtype)    # Output: json
print(mime1.params)     # Output: {}

mime2_str = "text/html; charset=UTF-8"
mime2 = MIME(mime2_str)
print(mime2.mime_type)  # Output: text/html
print(mime2.params)     # Output: {'charset': 'UTF-8'}

try:
    invalid_mime = MIME("application")
except ValueError as e:
    print(e) # Output: MIME type specification must be 'type/subtype[;param=value;...]'

try:
    invalid_mime = MIME("myapp/data") # 'myapp' is not a standard type
except ValueError as e:
    print(e) # Output: MIME type 'myapp' not supported
MIME_TYPES: ClassVar[list[str]] = ['text', 'image', 'audio', 'video', 'application', 'multipart', 'message']

Supported base MIME types

property mime_type: str

‘<type>/<subtype>’.

Type:

The base MIME type specification

property params: dict[str, str]

‘utf-8’}).

Type:

MIME parameters as a dictionary (e.g., {‘charset’

property subtype: str

The MIME subtype (e.g., ‘plain’, ‘json’).

property type: str

The main MIME type (e.g., ‘text’, ‘application’).

class firebird.base.types.PyExpr(value: str)[source]

Bases: str

Source code string representing a single Python expression.

Behaves like str, but validates that the content is a syntactically valid Python expression during initialization by attempting to compile it in ‘eval’ mode. Provides access to the compiled code object and a helper to create a callable function from the expression.

Raises:

SyntaxError – If the string value is not a valid Python expression.

Parameters:

value (str) –

Return type:

Self

Example:

expr_str = "a + b * 2"
py_expr = PyExpr(expr_str)

print(py_expr)           # Output: a + b * 2
print(repr(py_expr))     # Output: PyExpr('a + b * 2')

# Get the compiled code object
code_obj = py_expr.expr
print(eval(code_obj, {'a': 10, 'b': 5})) # Output: 20

# Get a callable function
func = py_expr.get_callable(arguments='a, b')
print(func(a=3, b=4))    # Output: 11

# Using a namespace
import math
log_expr = PyExpr("math.log10(x)")
log_func = log_expr.get_callable(arguments='x', namespace={'math': math})
print(log_func(x=100))   # Output: 2.0

try:
    invalid_expr = PyExpr("a = 5") # Assignment is not an expression
except SyntaxError as e:
    print(e)              # Output: invalid syntax (<string>, line 1) or similar
get_callable(arguments: str = '', namespace: dict[str, Any] | None = None) Callable[source]

Returns the expression wrapped in a callable function.

Parameters:
  • arguments (str) – Comma-separated string of argument names for the function signature.

  • namespace (dict[str, Any] | None) – Optional dictionary providing the execution namespace for the expression. Can be used to provide access to modules or specific values.

Returns:

A callable function that takes the specified arguments and returns the result of evaluating the expression.

Return type:

Callable

property expr: code

The compiled expression code object, ready for eval().

class firebird.base.types.PyCode(value: str)[source]

Bases: str

Source code string representing a block of Python statements.

Behaves like str, but validates that the content is a syntactically valid Python code block (potentially multiple statements) during initialization by attempting to compile it in ‘exec’ mode. Provides access to the compiled code object.

Raises:

SyntaxError – If the string value is not a valid Python code block.

Parameters:

value (str) –

Return type:

Self

Example:

code_str = '''
import math
result = math.sqrt(x * y)
print(f"Result: {result}")
'''
py_code = PyCode(code_str)

print(py_code[:20])      # Output: import math\nresult
print(repr(py_code))     # Output: PyCode('import math\nresult = ...')

# Get the compiled code object
code_obj = py_code.code

# Execute the code block
exec_namespace = {'x': 4, 'y': 9}
exec(code_obj, exec_namespace) # Output: Result: 6.0
print(exec_namespace['result']) # Output: 6.0

try:
    # Invalid syntax (e.g., unmatched parenthesis)
    invalid_code = PyCode("print('Hello'")
except SyntaxError as e:
    print(e)             # Output: unexpected EOF while parsing (<string>, line 1) or similar
property code: code

The compiled Python code object, ready for exec().

class firebird.base.types.PyCallable(value: str)[source]

Bases: str

Source code string representing a Python callable (function or class definition).

Behaves like str, but validates that the content is a syntactically valid Python function or class definition during initialization. It compiles and executes the definition to capture the resulting callable object.

Instances of PyCallable are themselves callable, acting as a proxy to the defined function or class.

Raises:
  • ValueError – If the string does not contain a recognizable ‘def ‘ or ‘class ‘ definition at the top level.

  • SyntaxError – If the string contains syntactically invalid Python code.

  • NameError – If the definition relies on names not available during its execution.

Parameters:

value (str) –

Return type:

Self

Example:

func_str = '''
def greet(name):
    "Greets the person."
    return f"Hello, {name}!"
'''
py_func = PyCallable(func_str)

print(py_func.name)      # Output: greet
print(py_func.__doc__)   # Output: Greets the person.
print(repr(py_func))     # Output: PyCallable('def greet(name):\n    ...')

# Call the instance directly
message = py_func(name="World")
print(message)           # Output: Hello, World!

class_str = '''
class MyNumber:
    def __init__(self, value):
        self.value = value
    def double(self):
        return self.value * 2
'''
py_class = PyCallable(class_str)

print(py_class.name)     # Output: MyNumber
instance = py_class(value=10) # Instantiate the class via the PyCallable object
print(instance.double()) # Output: 20

try:
    # Missing 'def' or 'class'
    invalid = PyCallable("print('Hello')")
except ValueError as e:
    print(e)             # Output: Python function or class definition not found

try:
    # Syntax error in definition
    invalid = PyCallable("def my_func(x:")
except SyntaxError as e:
    print(e)             # Output: invalid syntax (<string>, line 1) or similar
__call__(*args, **kwargs) Any[source]

Calls the wrapped function or instantiates the wrapped class.

Return type:

Any

name: str = None

Name of the defined function or class.

Meta classes

class firebird.base.types.SingletonMeta[source]

Bases: type

Metaclass for Singleton classes.

Manages internal cache of class instances. If instance for a class is in cache, it’s returned without calling the constructor, otherwise the instance is created normally and stored in cache for later use.

class firebird.base.types._SentinelMeta(name, bases, namespace)[source]

Bases: type

Metaclass for Sentinel objects.

This metaclass ensures that classes defined using it behave as proper sentinels:

  • They cannot be instantiated directly (e.g., MySentinel()).

  • They cannot be subclassed after initial definition.

  • Provides a basic __repr__ and __str__ based on the class name.

  • Allows defining sentinels via class definition (class NAME(Sentinel): ...) or potentially a functional call (though class definition is preferred).

  • Neuters __call__ inherited from type to prevent unintended behavior.

property name
class firebird.base.types.CachedDistinctMeta(name, bases, namespace, /, **kwargs)[source]

Bases: ABCMeta

Metaclass for CachedDistinct.

Intercepts class instantiation (__call__) to implement the instance caching mechanism based on the key extracted by cls.extract_key(). Ensures that only one instance exists per unique key.

firebird.base.types.conjunctive(name, bases, attrs) type[source]

Returns a metaclass that is conjunctive descendant of all metaclasses used by parent classes. It’s necessary to create a class with multiple inheritance, where multiple parent classes use different metaclasses.

Example

class A(type): pass

class B(type): pass

class AA(metaclass=A):pass

class BB(metaclass=B):pass

class CC(AA, BB, metaclass=Conjunctive): pass

Return type:

type

Functions

firebird.base.types.load(spec: str) Any[source]

Dynamically load an object (class, function, variable) from a module.

The module is imported automatically if it hasn’t been already.

Parameters:

spec (str) – Object specification string in the format 'module[.submodule...]:object_name[.attribute...]'.

Returns:

The loaded object.

Raises:
  • ImportError – If the module cannot be imported.

  • AttributeError – If the specified object cannot be found within the module.

Return type:

Any

Example:

# Assuming 'my_package/my_module.py' contains: class MyClass: pass
MyClassRef = load("my_package.my_module:MyClass")
instance = MyClassRef()

# Load a function
pprint_func = load("pprint:pprint")
pprint_func({"a": 1})