Module pyracmon.marker

This module provides the abstration mechanism for marker creation used to embed parameters in a query.

DB-API 2.0 defines some paramstyles and available style depends on DB driver. In addition, some styles are not suitable for query construction in python code. This library adopts unified marker $_ instead of the styles to make things simple.

Everything in this module is used internally in most cases, thus user does not need to know the detail.

Expand source code
"""
This module provides the abstration mechanism for marker creation used to embed parameters in a query.

DB-API 2.0 defines some `paramstyle`s and available style depends on DB driver.
In addition, some styles are not suitable for query construction in python code.
This library adopts unified marker `$_` instead of the styles to make things simple.

Everything in this module is used internally in most cases, thus user does not need to know the detail.
"""
from typing import Any, Optional, Union


class Marker:
    """
    Base class to enable unified marker instead of markers defined in DB-API 2.0.

    Each subclass corresponds to a `paramstyle` and implements methods to convert unified marker into its style.
    """
    @classmethod
    def of(cls, paramstyle: str) -> 'Marker':
        """
        Creates an instance for given `paramstyle`.

        Args:
            paramstyle: The name of `paramstyle` defined in DB-API 2.0.
        Returns:
            Created instance.
        """
        if paramstyle == 'qmark':
            return QMarker()
        elif paramstyle == 'numeric':
            return NumericMarker()
        elif paramstyle == 'named':
            return NamedMarker()
        elif paramstyle == 'format':
            return FormatMarker()
        elif paramstyle == 'pyformat':
            return PyformatMarker()
        else:
            raise ValueError(f"Unknown parameter style: {paramstyle}")

    def __call__(self, key: Optional[Union[int, str]] = None) -> str:
        """
        Renders a marker string for the key.

        Each invocation changes internal state of this instance.

        Args:
            key: A key which indicates the parameter. Type of the key depends on the style.
        Returns:
            Marker string.
        """
        if key is None:
            return self.default()
        elif isinstance(key, int) and key > 0:
            return self.indexed(key)
        elif isinstance(key, str) and key != "":
            return self.keyed(key)
        else:
            raise ValueError("Argument of marker invocation must be a positive int or str.")

    def default(self) -> str:
        """
        Generates a string of marker which is not indexed and keyed.

        Returns:
            Marker string.
        """
        raise NotImplementedError()

    def indexed(self, index: int) -> str:
        """
        Generates a string of indexed marker.

        Returns:
            Marker string.
        """
        raise NotImplementedError()

    def keyed(self, key: str) -> str:
        """
        Generates a string of keyed marker.

        Returns:
            Marker string.
        """
        raise NotImplementedError()

    def reset(self):
        """
        Resets the internal state.
        """
        pass

    def params(self, *args: Any, **kwargs: Any) -> Union[list[Any], dict[str, Any]]:
        """
        Generates parameters in the form which is available for query execution.

        This method adjusts the form of given parameters according to the internal state
        which has been changed on each marker generation.

        Args:
            args: Parameters for indexed markers.
            kwargs: Parameters for keyed markers.
        Returns:
            List or dictionary of parameters.
        """
        raise NotImplementedError()


class ListMarker(Marker):
    def __init__(self, start=0):
        self.index = start
        self.param_keys = []

    def render(self):
        raise NotImplementedError()

    def default(self):
        self.param_keys.append(self.index)
        self.index += 1
        return self.render()

    def indexed(self, index):
        self.param_keys.append(index-1)
        return self.render()

    def keyed(self, key):
        self.param_keys.append(key)
        return self.render()

    def reset(self):
        self.index = 0
        self.param_keys = []

    def params(self, *args, **kwargs):
        def get(k):
            if isinstance(k, int):
                return args[k]
            else:
                return kwargs[k]

        return [get(k) for k in self.param_keys]


class DictMarker(Marker):
    def __init__(self, start=0):
        self.index = start
        self.param_keys = {}

    def render(self, key):
        raise NotImplementedError()

    def new_key(self, index):
        return f"param{index}"

    def default(self):
        key = self.param_keys.setdefault(self.index, self.new_key(self.index+1))
        self.index += 1
        return self.render(key)

    def indexed(self, index):
        key = self.param_keys.setdefault(index-1, self.new_key(index))
        return self.render(key)

    def keyed(self, key):
        self.param_keys[key] = key
        return self.render(key)

    def reset(self):
        self.index = 0
        self.param_keys = {}

    def params(self, *args, **kwargs):
        def get(k, v):
            if isinstance(k, int):
                return (v, args[k])
            else:
                return (v, kwargs[k])

        return dict([get(k, v) for k, v in self.param_keys.items()])


class QMarker(ListMarker):
    """
    This marker renders `?` for any parameter.

    Returns:
        Marker string.
    """
    def render(self) -> str:
        return '?'


class NumericMarker(ListMarker):
    """
    This marker renders `:x` for the parameter at index `x` (starting from 1).

    Automatic numbering is used when invoked with no `key`, otherwise specified position is selected.
    The index obtained by next automatic numbering is an incremented value of the last obtained index.

    Returns:
        Marker string.
    """
    def render(self) -> str:
        return f":{len(self.param_keys)}"


class NamedMarker(DictMarker):
    """
    This marker renders `:key` for the parameter bound to `key`.

    Automatic key selection is used when invoked with no `key`, otherwise specified key is selected.
    Each automatic key is in the form of `keyn` where `n` is specified index or its order in generated keys.

    Returns:
        Marker string.
    """
    def render(self, key: str) -> str:
        return f":{key}"


class FormatMarker(ListMarker):
    """
    This marker renders `%s` for any parameter.

    Returns:
        Marker string.
    """
    def render(self) -> str:
        return '%s'


class PyformatMarker(DictMarker):
    """
    This marker renders `%(key)s` for the parameter bound to `key`.

    This works similarly to `NamedMarker`.

    Returns:
        Marker string.
    """
    def render(self, key: str) -> str:
        return f"%({key})s"

Classes

class DictMarker (start=0)

Base class to enable unified marker instead of markers defined in DB-API 2.0.

Each subclass corresponds to a paramstyle and implements methods to convert unified marker into its style.

Expand source code
class DictMarker(Marker):
    def __init__(self, start=0):
        self.index = start
        self.param_keys = {}

    def render(self, key):
        raise NotImplementedError()

    def new_key(self, index):
        return f"param{index}"

    def default(self):
        key = self.param_keys.setdefault(self.index, self.new_key(self.index+1))
        self.index += 1
        return self.render(key)

    def indexed(self, index):
        key = self.param_keys.setdefault(index-1, self.new_key(index))
        return self.render(key)

    def keyed(self, key):
        self.param_keys[key] = key
        return self.render(key)

    def reset(self):
        self.index = 0
        self.param_keys = {}

    def params(self, *args, **kwargs):
        def get(k, v):
            if isinstance(k, int):
                return (v, args[k])
            else:
                return (v, kwargs[k])

        return dict([get(k, v) for k, v in self.param_keys.items()])

Ancestors

Subclasses

Methods

def new_key(self, index)
Expand source code
def new_key(self, index):
    return f"param{index}"
def render(self, key)
Expand source code
def render(self, key):
    raise NotImplementedError()

Inherited members

class FormatMarker (start=0)

This marker renders %s for any parameter.

Returns

Marker string.

Expand source code
class FormatMarker(ListMarker):
    """
    This marker renders `%s` for any parameter.

    Returns:
        Marker string.
    """
    def render(self) -> str:
        return '%s'

Ancestors

Methods

def render(self) ‑> str
Expand source code
def render(self) -> str:
    return '%s'

Inherited members

class ListMarker (start=0)

Base class to enable unified marker instead of markers defined in DB-API 2.0.

Each subclass corresponds to a paramstyle and implements methods to convert unified marker into its style.

Expand source code
class ListMarker(Marker):
    def __init__(self, start=0):
        self.index = start
        self.param_keys = []

    def render(self):
        raise NotImplementedError()

    def default(self):
        self.param_keys.append(self.index)
        self.index += 1
        return self.render()

    def indexed(self, index):
        self.param_keys.append(index-1)
        return self.render()

    def keyed(self, key):
        self.param_keys.append(key)
        return self.render()

    def reset(self):
        self.index = 0
        self.param_keys = []

    def params(self, *args, **kwargs):
        def get(k):
            if isinstance(k, int):
                return args[k]
            else:
                return kwargs[k]

        return [get(k) for k in self.param_keys]

Ancestors

Subclasses

Methods

def render(self)
Expand source code
def render(self):
    raise NotImplementedError()

Inherited members

class Marker

Base class to enable unified marker instead of markers defined in DB-API 2.0.

Each subclass corresponds to a paramstyle and implements methods to convert unified marker into its style.

Expand source code
class Marker:
    """
    Base class to enable unified marker instead of markers defined in DB-API 2.0.

    Each subclass corresponds to a `paramstyle` and implements methods to convert unified marker into its style.
    """
    @classmethod
    def of(cls, paramstyle: str) -> 'Marker':
        """
        Creates an instance for given `paramstyle`.

        Args:
            paramstyle: The name of `paramstyle` defined in DB-API 2.0.
        Returns:
            Created instance.
        """
        if paramstyle == 'qmark':
            return QMarker()
        elif paramstyle == 'numeric':
            return NumericMarker()
        elif paramstyle == 'named':
            return NamedMarker()
        elif paramstyle == 'format':
            return FormatMarker()
        elif paramstyle == 'pyformat':
            return PyformatMarker()
        else:
            raise ValueError(f"Unknown parameter style: {paramstyle}")

    def __call__(self, key: Optional[Union[int, str]] = None) -> str:
        """
        Renders a marker string for the key.

        Each invocation changes internal state of this instance.

        Args:
            key: A key which indicates the parameter. Type of the key depends on the style.
        Returns:
            Marker string.
        """
        if key is None:
            return self.default()
        elif isinstance(key, int) and key > 0:
            return self.indexed(key)
        elif isinstance(key, str) and key != "":
            return self.keyed(key)
        else:
            raise ValueError("Argument of marker invocation must be a positive int or str.")

    def default(self) -> str:
        """
        Generates a string of marker which is not indexed and keyed.

        Returns:
            Marker string.
        """
        raise NotImplementedError()

    def indexed(self, index: int) -> str:
        """
        Generates a string of indexed marker.

        Returns:
            Marker string.
        """
        raise NotImplementedError()

    def keyed(self, key: str) -> str:
        """
        Generates a string of keyed marker.

        Returns:
            Marker string.
        """
        raise NotImplementedError()

    def reset(self):
        """
        Resets the internal state.
        """
        pass

    def params(self, *args: Any, **kwargs: Any) -> Union[list[Any], dict[str, Any]]:
        """
        Generates parameters in the form which is available for query execution.

        This method adjusts the form of given parameters according to the internal state
        which has been changed on each marker generation.

        Args:
            args: Parameters for indexed markers.
            kwargs: Parameters for keyed markers.
        Returns:
            List or dictionary of parameters.
        """
        raise NotImplementedError()

Subclasses

Static methods

def of(paramstyle: str) ‑> Marker

Creates an instance for given paramstyle.

Args

paramstyle
The name of paramstyle defined in DB-API 2.0.

Returns

Created instance.

Expand source code
@classmethod
def of(cls, paramstyle: str) -> 'Marker':
    """
    Creates an instance for given `paramstyle`.

    Args:
        paramstyle: The name of `paramstyle` defined in DB-API 2.0.
    Returns:
        Created instance.
    """
    if paramstyle == 'qmark':
        return QMarker()
    elif paramstyle == 'numeric':
        return NumericMarker()
    elif paramstyle == 'named':
        return NamedMarker()
    elif paramstyle == 'format':
        return FormatMarker()
    elif paramstyle == 'pyformat':
        return PyformatMarker()
    else:
        raise ValueError(f"Unknown parameter style: {paramstyle}")

Methods

def default(self) ‑> str

Generates a string of marker which is not indexed and keyed.

Returns

Marker string.

Expand source code
def default(self) -> str:
    """
    Generates a string of marker which is not indexed and keyed.

    Returns:
        Marker string.
    """
    raise NotImplementedError()
def indexed(self, index: int) ‑> str

Generates a string of indexed marker.

Returns

Marker string.

Expand source code
def indexed(self, index: int) -> str:
    """
    Generates a string of indexed marker.

    Returns:
        Marker string.
    """
    raise NotImplementedError()
def keyed(self, key: str) ‑> str

Generates a string of keyed marker.

Returns

Marker string.

Expand source code
def keyed(self, key: str) -> str:
    """
    Generates a string of keyed marker.

    Returns:
        Marker string.
    """
    raise NotImplementedError()
def params(self, *args: Any, **kwargs: Any) ‑> Union[list[Any], dict[str, Any]]

Generates parameters in the form which is available for query execution.

This method adjusts the form of given parameters according to the internal state which has been changed on each marker generation.

Args

args
Parameters for indexed markers.
kwargs
Parameters for keyed markers.

Returns

List or dictionary of parameters.

Expand source code
def params(self, *args: Any, **kwargs: Any) -> Union[list[Any], dict[str, Any]]:
    """
    Generates parameters in the form which is available for query execution.

    This method adjusts the form of given parameters according to the internal state
    which has been changed on each marker generation.

    Args:
        args: Parameters for indexed markers.
        kwargs: Parameters for keyed markers.
    Returns:
        List or dictionary of parameters.
    """
    raise NotImplementedError()
def reset(self)

Resets the internal state.

Expand source code
def reset(self):
    """
    Resets the internal state.
    """
    pass
class NamedMarker (start=0)

This marker renders :key for the parameter bound to key.

Automatic key selection is used when invoked with no key, otherwise specified key is selected. Each automatic key is in the form of keyn where n is specified index or its order in generated keys.

Returns

Marker string.

Expand source code
class NamedMarker(DictMarker):
    """
    This marker renders `:key` for the parameter bound to `key`.

    Automatic key selection is used when invoked with no `key`, otherwise specified key is selected.
    Each automatic key is in the form of `keyn` where `n` is specified index or its order in generated keys.

    Returns:
        Marker string.
    """
    def render(self, key: str) -> str:
        return f":{key}"

Ancestors

Methods

def render(self, key: str) ‑> str
Expand source code
def render(self, key: str) -> str:
    return f":{key}"

Inherited members

class NumericMarker (start=0)

This marker renders :x for the parameter at index x (starting from 1).

Automatic numbering is used when invoked with no key, otherwise specified position is selected. The index obtained by next automatic numbering is an incremented value of the last obtained index.

Returns

Marker string.

Expand source code
class NumericMarker(ListMarker):
    """
    This marker renders `:x` for the parameter at index `x` (starting from 1).

    Automatic numbering is used when invoked with no `key`, otherwise specified position is selected.
    The index obtained by next automatic numbering is an incremented value of the last obtained index.

    Returns:
        Marker string.
    """
    def render(self) -> str:
        return f":{len(self.param_keys)}"

Ancestors

Methods

def render(self) ‑> str
Expand source code
def render(self) -> str:
    return f":{len(self.param_keys)}"

Inherited members

class PyformatMarker (start=0)

This marker renders %(key)s for the parameter bound to key.

This works similarly to NamedMarker.

Returns

Marker string.

Expand source code
class PyformatMarker(DictMarker):
    """
    This marker renders `%(key)s` for the parameter bound to `key`.

    This works similarly to `NamedMarker`.

    Returns:
        Marker string.
    """
    def render(self, key: str) -> str:
        return f"%({key})s"

Ancestors

Methods

def render(self, key: str) ‑> str
Expand source code
def render(self, key: str) -> str:
    return f"%({key})s"

Inherited members

class QMarker (start=0)

This marker renders ? for any parameter.

Returns

Marker string.

Expand source code
class QMarker(ListMarker):
    """
    This marker renders `?` for any parameter.

    Returns:
        Marker string.
    """
    def render(self) -> str:
        return '?'

Ancestors

Methods

def render(self) ‑> str
Expand source code
def render(self) -> str:
    return '?'

Inherited members