#!/usr/bin/env python
# cardinal_pythonlib/typing_helpers.py
"""
===============================================================================
Original code copyright (C) 2009-2022 Rudolf Cardinal (rudolf@pobox.com).
This file is part of cardinal_pythonlib.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
===============================================================================
**Methods and unusual types for type hints.**
"""
from abc import abstractmethod
import csv
from typing import (
Any,
Iterator,
List,
Optional,
Sequence,
Tuple,
Type,
TYPE_CHECKING,
TypeVar,
Union,
)
# =============================================================================
# Constants
# =============================================================================
T = TypeVar("T")
# =============================================================================
# with_typehint
# =============================================================================
[docs]def with_typehint(baseclass: Type[T]) -> Type[T]:
"""
Useful function to make mixins with type hints from a base class.
From
https://stackoverflow.com/questions/51930339/how-do-i-correctly-add-type-hints-to-mixin-classes.
Specimen usage:
.. code-block:: python
class MyMixin1(with_typehint(SomeBaseClass))):
# ...
"""
if TYPE_CHECKING:
return baseclass
return object
[docs]def with_typehints(*baseclasses: Type[T]) -> Type[T]:
"""
Useful function to make mixins with type hints from multiple base classes.
From
https://stackoverflow.com/questions/51930339/how-do-i-correctly-add-type-hints-to-mixin-classes.
Specimen usage:
.. code-block:: python
class MyMixin2(*with_typehints(SomeBaseClass, AnotherBaseClass))):
# ...
"""
if TYPE_CHECKING:
return baseclasses
return object
# =============================================================================
# csv.writer
# =============================================================================
[docs]class CSVWriterType(object):
"""
Type hint for the result of ``csv.writer()``
See https://stackoverflow.com/questions/51264355/how-to-type-annotate-object-returned-by-csv-writer
""" # noqa: E501
@abstractmethod
def writerow(self, row: List[str]) -> None:
pass
@abstractmethod
def writerows(self, rows: List[List[str]]) -> None:
pass
@property
@abstractmethod
def dialect(self) -> csv.Dialect:
pass
# =============================================================================
# Pep249DatabaseConnectionType
# =============================================================================
[docs]class Pep249DatabaseConnectionType(object):
"""
Type hint for a database connection compliant with PEP 249. See
https://www.python.org/dev/peps/pep-0249/.
Not supported:
- https://www.python.org/dev/peps/pep-0249/#optional-error-handling-extensions
- https://www.python.org/dev/peps/pep-0249/#optional-two-phase-commit-extensions
""" # noqa: E501
[docs] @abstractmethod
def close(self) -> None:
"""
See https://www.python.org/dev/peps/pep-0249/#connection-objects
"""
pass
[docs] @abstractmethod
def commit(self) -> None:
"""
See https://www.python.org/dev/peps/pep-0249/#connection-objects
"""
pass
[docs] @abstractmethod
def rollback(self) -> None:
"""
See https://www.python.org/dev/peps/pep-0249/#connection-objects
"""
pass
[docs] @abstractmethod
def cursor(self) -> "Pep249DatabaseCursorType":
"""
See https://www.python.org/dev/peps/pep-0249/#connection-objects
"""
pass
@property
@abstractmethod
def messages(self) -> List[Tuple[Type, Any]]:
"""
See
https://www.python.org/dev/peps/pep-0249/#optional-db-api-extensions
"""
pass
# =============================================================================
# Pep249DatabaseConnectionType
# =============================================================================
_DATABASE_ROW_TYPE = Sequence[Any]
[docs]class Pep249DatabaseCursorType(object):
"""
Type hint for a database cursor compliant with PEP 249. See
https://www.python.org/dev/peps/pep-0249/#cursor-objects
Example, as per https://docs.python.org/3.6/library/sqlite3.html:
.. code-block:: python
import sqlite3
conn = sqlite3.connect(':memory:')
c = conn.cursor()
c.execute('''
CREATE TABLE stocks
(date text, trans text, symbol text, qty real, price real)
''')
c.execute('''
INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)
''')
conn.commit()
c.execute("SELECT * FROM stocks")
print(repr(c.description))
help(c)
See also:
- https://www.psycopg.org/docs/cursor.html
"""
@abstractmethod
def __init__(self, *args, **kwargs) -> None:
pass
@abstractmethod
def __iter__(self) -> Iterator[_DATABASE_ROW_TYPE]:
"""
See
https://www.python.org/dev/peps/pep-0249/#optional-db-api-extensions
"""
pass
@abstractmethod
def __new__(cls, *args, **kwargs) -> "Pep249DatabaseCursorType":
pass
@abstractmethod
def __next__(self) -> None:
pass
@property
@abstractmethod
def description(self) -> Optional[Sequence[Sequence[Any]]]:
"""
A sequence of column_description objects, where each column_description
describes one result column and has the following items:
- name: ``str``
- type_code: ``Optional[Type]``? Not sure.
- display_size: ``Optional[int]``
- internal_size: ``Optional[int]``
- precision: ``Optional[int]``
- scale: ``Optional[int]``
- null_ok: ``Optional[bool]``
The attribute is ``None`` for operations that don't return rows, and
for un-executed cursors.
"""
pass
@property
@abstractmethod
def rowcount(self) -> int:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
[docs] @abstractmethod
def callproc(self, procname: str, *args, **kwargs) -> None:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
[docs] @abstractmethod
def close(self) -> None:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
[docs] @abstractmethod
def execute(self, operation: str, *args, **kwargs) -> None:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
[docs] @abstractmethod
def executemany(self, operation: str, *args, **kwargs) -> None:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
[docs] @abstractmethod
def fetchone(self) -> Optional[_DATABASE_ROW_TYPE]:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
[docs] @abstractmethod
def fetchmany(self, size: int = None) -> Sequence[_DATABASE_ROW_TYPE]:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
[docs] @abstractmethod
def fetchall(self) -> Sequence[_DATABASE_ROW_TYPE]:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
[docs] @abstractmethod
def nextset(self) -> Optional[bool]:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
@property
@abstractmethod
def arraysize(self) -> int:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
# read/write attribute; see below
pass
# noinspection PyUnresolvedReferences
@arraysize.setter
@abstractmethod
def arraysize(self, val: int) -> None:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
# https://stackoverflow.com/questions/35344209/python-abstract-property-setter-with-concrete-getter
pass
[docs] @abstractmethod
def setoutputsize(self, size: int, column: Optional[int]) -> None:
"""
See https://www.python.org/dev/peps/pep-0249/#cursor-objects
"""
pass
@property
@abstractmethod
def connection(self) -> Pep249DatabaseConnectionType:
"""
See
https://www.python.org/dev/peps/pep-0249/#optional-db-api-extensions
"""
pass
@property
@abstractmethod
def lastrowid(self) -> Optional[int]:
"""
See
https://www.python.org/dev/peps/pep-0249/#optional-db-api-extensions
"""
pass
@property
@abstractmethod
def rownumber(self) -> Optional[int]:
"""
See
https://www.python.org/dev/peps/pep-0249/#optional-db-api-extensions
"""
pass
@property
@abstractmethod
def messages(self) -> List[Tuple[Type, Any]]:
"""
See
https://www.python.org/dev/peps/pep-0249/#optional-db-api-extensions
"""
pass
[docs] @abstractmethod
def next(self) -> _DATABASE_ROW_TYPE:
"""
See
https://www.python.org/dev/peps/pep-0249/#optional-db-api-extensions
"""
pass