text: #!/usr/bin/env python
  # cardinal_pythonlib/typing_helpers.py
  
  """
  ===============================================================================
  
      Original code copyright (C) 2009-2021 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
  # =============================================================================
  
  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))):
              # ...
  
      """  # noqa
      if TYPE_CHECKING:
          return baseclass
      return object
  
  
  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))):
              # ...
  
      """  # noqa
      if TYPE_CHECKING:
          return baseclasses
      return object
  
  
  # =============================================================================
  # csv.writer
  # =============================================================================
  
  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
  
      @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
  # =============================================================================
  
  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
  
      @abstractmethod
      def close(self) -> None:
          """
          See https://www.python.org/dev/peps/pep-0249/#connection-objects
          """
          pass
  
      @abstractmethod
      def commit(self) -> None:
          """
          See https://www.python.org/dev/peps/pep-0249/#connection-objects
          """
          pass
  
      @abstractmethod
      def rollback(self) -> None:
          """
          See https://www.python.org/dev/peps/pep-0249/#connection-objects
          """
          pass
  
      @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]
  
  
  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
  
      @abstractmethod
      def callproc(self, procname: str, *args, **kwargs) -> None:
          """
          See https://www.python.org/dev/peps/pep-0249/#cursor-objects
          """
          pass
  
      @abstractmethod
      def close(self) -> None:
          """
          See https://www.python.org/dev/peps/pep-0249/#cursor-objects
          """
          pass
  
      @abstractmethod
      def execute(self, operation: str, *args, **kwargs) -> None:
          """
          See https://www.python.org/dev/peps/pep-0249/#cursor-objects
          """
          pass
  
      @abstractmethod
      def executemany(self, operation: str, *args, **kwargs) -> None:
          """
          See https://www.python.org/dev/peps/pep-0249/#cursor-objects
          """
          pass
  
      @abstractmethod
      def fetchone(self) -> Optional[_DATABASE_ROW_TYPE]:
          """
          See https://www.python.org/dev/peps/pep-0249/#cursor-objects
          """
          pass
  
      @abstractmethod
      def fetchmany(self, size: int = None) -> Sequence[_DATABASE_ROW_TYPE]:
          """
          See https://www.python.org/dev/peps/pep-0249/#cursor-objects
          """
          pass
  
      @abstractmethod
      def fetchall(self) -> Sequence[_DATABASE_ROW_TYPE]:
          """
          See https://www.python.org/dev/peps/pep-0249/#cursor-objects
          """
          pass
  
      @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
  
      @abstractmethod
      def setinputsizes(self, sizes: Sequence[Union[Type, int]]) -> None:
          """
          See https://www.python.org/dev/peps/pep-0249/#cursor-objects
          """
          pass
  
      @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
  
      @abstractmethod
      def scroll(self, value: int, mode: str = 'relative') -> None:
          """
          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
  
      @abstractmethod
      def next(self) -> _DATABASE_ROW_TYPE:
          """
          See
          https://www.python.org/dev/peps/pep-0249/#optional-db-api-extensions
          """
          pass
  