Module couchdb3.utils

Expand source code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import base64
from collections.abc import Generator
from enum import Enum
import mimetypes
import re
import requests
from typing import Any, Dict, Optional, Set, Type
from urllib import parse
from urllib3.util import Url, parse_url

from . import exceptions


__all__ = [
    "basic_auth",
    "build_query",
    "build_url",
    "user_name_to_id",
    "validate_auth_method",
    "validate_db_name",
    "validate_proxy",
    "validate_user_id",
    "check_response",
    "extract_url_data",
    "partitioned_db_resource_parser",
    "COUCHDB_USERS_DB_NAME",
    "COUCHDB_REPLICATOR_DB_NAME",
    "COUCHDB_GLOBAL_CHANGES_DB_NAME",
    "COUCH_DB_RESERVED_DB_NAMES",
    "DEFAULT_AUTH_METHOD",
    "DEFAULT_TIMEOUT",
    "MimeTypeEnum",
    "PATTERN_DB_NAME",
    "PATTERN_USER_ID",
    "VALID_AUTH_METHODS",
    "VALID_SCHEMES",
]


COUCHDB_USERS_DB_NAME: str = "_users"
"""Reserved CouchDB users database name."""
COUCHDB_REPLICATOR_DB_NAME: str = "_replicator"
"""Reserved CouchDB replicator database name."""
COUCHDB_GLOBAL_CHANGES_DB_NAME: str = "_global_changes"
"""Reserved CouchDB global changes database name."""

COUCH_DB_RESERVED_DB_NAMES: Set[str] = {
    COUCHDB_USERS_DB_NAME,
    COUCHDB_REPLICATOR_DB_NAME,
    COUCHDB_GLOBAL_CHANGES_DB_NAME
}
"""Reserved CouchDB database names."""

DEFAULT_AUTH_METHOD: str = "cookie"
"""The default authentication method - values to `\"cookie\"`."""

DEFAULT_TIMEOUT: int = 300
"""The default timeout set in requests - values to `300`."""

MimeTypeEnum: Type[Enum] = Enum(
    'MimeTypeEnum',
    {'mime_type_' + k.removeprefix('.'): v for k, v in mimetypes.types_map.items()}
)
"""An Enum containing all existing mime types."""

PATTERN_DB_NAME: re.Pattern = re.compile(r"^[a-z][a-z0-9_$()+/-]*$")
"""The pattern for valid database names."""
PATTERN_USER_ID: re.Pattern = re.compile(r"^org\.couchdb\.user:.*")
"""The pattern for valid user IDs."""

VALID_AUTH_METHODS: Set[str] = {"basic", "cookie"}
"""The valid auth method arguments. Possible values are `\"basic\"` or `\"cookie\"`."""
VALID_SCHEMES: Set[str] = {"http", "https", "socks5"}
"""The valid TCP schemes. Possible values are `\"http\"` or `\"https\"` or `\"socks5\"`."""


def _handler(x: Any) -> str:
    if isinstance(x, (Generator, map, list, set, tuple)):
        return "[%s]" % ",".join(f"\"{_handler(_)}\"" for _ in x)
    elif isinstance(x, dict):
        return str({key: _handler(val) for key, val in x.items()})
    elif isinstance(x, bool):
        return str(x).lower()
    return str(x)


def basic_auth(
        user: str,
        password: str
) -> str:
    """
    Create basic authentication headers value.

    Parameters
    ----------
    user : str
        A CouchDB user name.
    password : str
        A corresponding CouchDB user password.

    Returns
    -------
    str : The credentials concatenated with a colon and base64 encoded.
    """
    return base64.b64encode(f"{user}:{password}".encode()).decode()


def build_query(
        **kwargs,
) -> Optional[str]:
    """

    Parameters
    ----------
    kwargs
        Arbitrary keyword-args to be passed as query-params in a URL.
    Returns
    -------
    str : A string containing the keyword-args encoded as URL query-params.
    """
    return parse.urlencode({key: _handler(val) for key, val in kwargs.items() if val is not None})


def build_url(
        *,
        scheme: str,
        host: str,
        path: str = None,
        port: int = None,
        **kwargs,
) -> Url:
    """
    Build a URL using the provided scheme, host, path & kwargs.

    Parameters
    ----------
    scheme : str
        The URL scheme (e.g `http`).
    host : str
        The URL host (e.g. `example.com`).
    path : str
        The URL path (e.g. `/api/data`). Default `None`.
    port : int
        The port to connect to (e.g. `5984`). Default `None`.
    kwargs
        Arbitrary keyword-args to be passed as query-params in a URL.
    Returns
    -------
    Url : An instance of `Url`.
    """
    return Url(
        scheme=scheme,
        host=host,
        port=port,
        path=path,
        query=build_query(**kwargs),
    )


def validate_db_name(name: str) -> bool:
    """
    Checks a name for CouchDB name-compliance.

    Parameters
    ----------
    name : str
        A prospective database name.

    Returns
    -------
    bool : `True` if the provided name is CouchDB compliant.
    """
    return name in COUCH_DB_RESERVED_DB_NAMES or bool(PATTERN_DB_NAME.fullmatch(name))


def validate_auth_method(auth_method: str) -> bool:
    """
    Checks if the provided authentication method is valid.

    Parameters
    ----------
    auth_method : str

    Returns
    -------
    bool: `True` if `auth_method` is in `VALID_AUTH_METHODS`.
    """
    return auth_method in VALID_AUTH_METHODS


def validate_proxy(proxy: str) -> bool:
    """
    Check a proxy scheme for CouchDB proxy-scheme-compliance

    Parameters
    ----------
    proxy : str
        A prospective proxy.

    Returns
    -------
    bool : `True` if the provided proxy is CouchDB compliant.
    """
    return parse_url(proxy).scheme in VALID_SCHEMES


def validate_user_id(user_id: str) -> bool:
    """
    Checks a user ID for CouchDB user-id-compliance.

    Parameters
    ----------
    user_id : str
        A prospective user ID.

    Returns
    -------
    bool : `True` if the provided user ID is CouchDB compliant.

    """
    return bool(PATTERN_USER_ID.fullmatch(user_id))


def user_name_to_id(name: str) -> str:
    """
    Convert a name into a valid CouchDB user ID.

    Parameters
    ----------
    name : str
        A user name.

    Returns
    -------
    str : A valid CouchDB ID, i.e. of the form `org.couchdb.user:{name}`.
    """
    return f"org.couchdb.user:{name}"


def check_response(response: requests.Response) -> None:
    """
    Check if a request yields a successful response.

    Parameters
    ----------
    response : requests.Response
        A `requests.Response` object.
    Returns
    -------
    None
    Raises
    ------
    One of the following exceptions:

    - couchdb3.error.CouchDBError
    - ConnectionError
    - TimeoutError
    - requests.exceptions.ConnectionError
    - requests.exceptions.HTTPError

    """
    try:
        response.raise_for_status()
    except (
            ConnectionError,
            TimeoutError,
            requests.exceptions.ConnectionError,
            requests.exceptions.HTTPError,
    ) as err:
        if response.status_code in exceptions.STATUS_CODE_ERROR_MAPPING:
            _ = exceptions.STATUS_CODE_ERROR_MAPPING[response.status_code]
            if _:
                raise _(response.text)
            else:
                return None
        raise err


def extract_url_data(url: str) -> Dict:
    """
    Extract scheme, credentials, host, port & path from a URL.

    Parameters
    ----------
    url : str
        A URL string.

    Returns
    -------
    Dict : A dictionary containing with the following items.

      - scheme
      - user
      - password
      - host
      - port
      - path
    """
    if not any(url.startswith(_) for _ in VALID_SCHEMES):
        url = f"http://{url}"
    parsed = parse_url(url)
    return {
        "scheme": parsed.scheme,
        "user": parsed.auth.split(":")[0] if hasattr(parsed.auth, "split") else None,
        "password": parsed.auth.split(":")[1] if hasattr(parsed.auth, "split") else None,
        "host": parsed.host,
        "port": parsed.port,
        "path": parsed.path
    }


def partitioned_db_resource_parser(
        resource: str = None,
        partition: str = None,
) -> Optional[str]:
    """
    Build resource path with optional partition ID.

    Parameters
    ----------
    resource : str
        The resource to fetch (relative to the host). Default `None`.
    partition: str
        An optional partition ID. Only valid for partitioned databases. (Default `None`.)
    Returns
    ----------
        The (relative) path of the resource.
    """
    return f"_partition/{partition}/{resource}" if partition else resource

Global variables

var COUCHDB_GLOBAL_CHANGES_DB_NAME : str

Reserved CouchDB global changes database name.

var COUCHDB_REPLICATOR_DB_NAME : str

Reserved CouchDB replicator database name.

var COUCHDB_USERS_DB_NAME : str

Reserved CouchDB users database name.

var COUCH_DB_RESERVED_DB_NAMES : Set[str]

Reserved CouchDB database names.

var DEFAULT_AUTH_METHOD : str

The default authentication method - values to "cookie".

var DEFAULT_TIMEOUT : int

The default timeout set in requests - values to 300.

var PATTERN_DB_NAME : re.Pattern

The pattern for valid database names.

var PATTERN_USER_ID : re.Pattern

The pattern for valid user IDs.

var VALID_AUTH_METHODS : Set[str]

The valid auth method arguments. Possible values are "basic" or "cookie".

var VALID_SCHEMES : Set[str]

The valid TCP schemes. Possible values are "http" or "https" or "socks5".

Functions

def basic_auth(user: str, password: str) ‑> str

Create basic authentication headers value.

Parameters

user : str
A CouchDB user name.
password : str
A corresponding CouchDB user password.

Returns

str : The credentials concatenated with a colon and base64 encoded.

Expand source code
def basic_auth(
        user: str,
        password: str
) -> str:
    """
    Create basic authentication headers value.

    Parameters
    ----------
    user : str
        A CouchDB user name.
    password : str
        A corresponding CouchDB user password.

    Returns
    -------
    str : The credentials concatenated with a colon and base64 encoded.
    """
    return base64.b64encode(f"{user}:{password}".encode()).decode()
def build_query(**kwargs) ‑> Optional[str]

Parameters

kwargs
Arbitrary keyword-args to be passed as query-params in a URL.

Returns

str : A string containing the keyword-args encoded as URL query-params.

Expand source code
def build_query(
        **kwargs,
) -> Optional[str]:
    """

    Parameters
    ----------
    kwargs
        Arbitrary keyword-args to be passed as query-params in a URL.
    Returns
    -------
    str : A string containing the keyword-args encoded as URL query-params.
    """
    return parse.urlencode({key: _handler(val) for key, val in kwargs.items() if val is not None})
def build_url(*, scheme: str, host: str, path: str = None, port: int = None, **kwargs) ‑> urllib3.util.url.Url

Build a URL using the provided scheme, host, path & kwargs.

Parameters

scheme : str
The URL scheme (e.g http).
host : str
The URL host (e.g. example.com).
path : str
The URL path (e.g. /api/data). Default None.
port : int
The port to connect to (e.g. 5984). Default None.
kwargs
Arbitrary keyword-args to be passed as query-params in a URL.

Returns

Url : An instance of Url.

Expand source code
def build_url(
        *,
        scheme: str,
        host: str,
        path: str = None,
        port: int = None,
        **kwargs,
) -> Url:
    """
    Build a URL using the provided scheme, host, path & kwargs.

    Parameters
    ----------
    scheme : str
        The URL scheme (e.g `http`).
    host : str
        The URL host (e.g. `example.com`).
    path : str
        The URL path (e.g. `/api/data`). Default `None`.
    port : int
        The port to connect to (e.g. `5984`). Default `None`.
    kwargs
        Arbitrary keyword-args to be passed as query-params in a URL.
    Returns
    -------
    Url : An instance of `Url`.
    """
    return Url(
        scheme=scheme,
        host=host,
        port=port,
        path=path,
        query=build_query(**kwargs),
    )
def check_response(response: requests.models.Response) ‑> None

Check if a request yields a successful response.

Parameters

response : requests.Response
A requests.Response object.

Returns

None
 

Raises

One of the following exceptions:
 
  • couchdb3.error.CouchDBError
  • ConnectionError
  • TimeoutError
  • requests.exceptions.ConnectionError
  • requests.exceptions.HTTPError
Expand source code
def check_response(response: requests.Response) -> None:
    """
    Check if a request yields a successful response.

    Parameters
    ----------
    response : requests.Response
        A `requests.Response` object.
    Returns
    -------
    None
    Raises
    ------
    One of the following exceptions:

    - couchdb3.error.CouchDBError
    - ConnectionError
    - TimeoutError
    - requests.exceptions.ConnectionError
    - requests.exceptions.HTTPError

    """
    try:
        response.raise_for_status()
    except (
            ConnectionError,
            TimeoutError,
            requests.exceptions.ConnectionError,
            requests.exceptions.HTTPError,
    ) as err:
        if response.status_code in exceptions.STATUS_CODE_ERROR_MAPPING:
            _ = exceptions.STATUS_CODE_ERROR_MAPPING[response.status_code]
            if _:
                raise _(response.text)
            else:
                return None
        raise err
def extract_url_data(url: str) ‑> Dict

Extract scheme, credentials, host, port & path from a URL.

Parameters

url : str
A URL string.

Returns

Dict : A dictionary containing with the following items.

  • scheme
  • user
  • password
  • host
  • port
  • path
Expand source code
def extract_url_data(url: str) -> Dict:
    """
    Extract scheme, credentials, host, port & path from a URL.

    Parameters
    ----------
    url : str
        A URL string.

    Returns
    -------
    Dict : A dictionary containing with the following items.

      - scheme
      - user
      - password
      - host
      - port
      - path
    """
    if not any(url.startswith(_) for _ in VALID_SCHEMES):
        url = f"http://{url}"
    parsed = parse_url(url)
    return {
        "scheme": parsed.scheme,
        "user": parsed.auth.split(":")[0] if hasattr(parsed.auth, "split") else None,
        "password": parsed.auth.split(":")[1] if hasattr(parsed.auth, "split") else None,
        "host": parsed.host,
        "port": parsed.port,
        "path": parsed.path
    }
def partitioned_db_resource_parser(resource: str = None, partition: str = None) ‑> Optional[str]

Build resource path with optional partition ID.

Parameters

resource : str
The resource to fetch (relative to the host). Default None.
partition : str
An optional partition ID. Only valid for partitioned databases. (Default None.)

Returns

The (relative) path of the resource.
Expand source code
def partitioned_db_resource_parser(
        resource: str = None,
        partition: str = None,
) -> Optional[str]:
    """
    Build resource path with optional partition ID.

    Parameters
    ----------
    resource : str
        The resource to fetch (relative to the host). Default `None`.
    partition: str
        An optional partition ID. Only valid for partitioned databases. (Default `None`.)
    Returns
    ----------
        The (relative) path of the resource.
    """
    return f"_partition/{partition}/{resource}" if partition else resource
def user_name_to_id(name: str) ‑> str

Convert a name into a valid CouchDB user ID.

Parameters

name : str
A user name.

Returns

str : A valid CouchDB ID, i.e. of the form org.couchdb.user:{name}.

Expand source code
def user_name_to_id(name: str) -> str:
    """
    Convert a name into a valid CouchDB user ID.

    Parameters
    ----------
    name : str
        A user name.

    Returns
    -------
    str : A valid CouchDB ID, i.e. of the form `org.couchdb.user:{name}`.
    """
    return f"org.couchdb.user:{name}"
def validate_auth_method(auth_method: str) ‑> bool

Checks if the provided authentication method is valid.

Parameters

auth_method : str
 

Returns

bool: True if auth_method is in VALID_AUTH_METHODS.

Expand source code
def validate_auth_method(auth_method: str) -> bool:
    """
    Checks if the provided authentication method is valid.

    Parameters
    ----------
    auth_method : str

    Returns
    -------
    bool: `True` if `auth_method` is in `VALID_AUTH_METHODS`.
    """
    return auth_method in VALID_AUTH_METHODS
def validate_db_name(name: str) ‑> bool

Checks a name for CouchDB name-compliance.

Parameters

name : str
A prospective database name.

Returns

bool : True if the provided name is CouchDB compliant.

Expand source code
def validate_db_name(name: str) -> bool:
    """
    Checks a name for CouchDB name-compliance.

    Parameters
    ----------
    name : str
        A prospective database name.

    Returns
    -------
    bool : `True` if the provided name is CouchDB compliant.
    """
    return name in COUCH_DB_RESERVED_DB_NAMES or bool(PATTERN_DB_NAME.fullmatch(name))
def validate_proxy(proxy: str) ‑> bool

Check a proxy scheme for CouchDB proxy-scheme-compliance

Parameters

proxy : str
A prospective proxy.

Returns

bool : True if the provided proxy is CouchDB compliant.

Expand source code
def validate_proxy(proxy: str) -> bool:
    """
    Check a proxy scheme for CouchDB proxy-scheme-compliance

    Parameters
    ----------
    proxy : str
        A prospective proxy.

    Returns
    -------
    bool : `True` if the provided proxy is CouchDB compliant.
    """
    return parse_url(proxy).scheme in VALID_SCHEMES
def validate_user_id(user_id: str) ‑> bool

Checks a user ID for CouchDB user-id-compliance.

Parameters

user_id : str
A prospective user ID.

Returns

bool : True if the provided user ID is CouchDB compliant.

Expand source code
def validate_user_id(user_id: str) -> bool:
    """
    Checks a user ID for CouchDB user-id-compliance.

    Parameters
    ----------
    user_id : str
        A prospective user ID.

    Returns
    -------
    bool : `True` if the provided user ID is CouchDB compliant.

    """
    return bool(PATTERN_USER_ID.fullmatch(user_id))

Classes

class MimeTypeEnum (*args, **kwds)

Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
...     RED = 1
...     BLUE = 2
...     GREEN = 3

Access them by:

  • attribute access:

Color.RED

  • value lookup:

Color(1)

  • name lookup:

Color['RED']

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.

Ancestors

  • enum.Enum

Class variables

var mime_type_3g2
var mime_type_3gp
var mime_type_3gpp
var mime_type_3gpp2
var mime_type_a
var mime_type_aac
var mime_type_adts
var mime_type_ai
var mime_type_aif
var mime_type_aifc
var mime_type_aiff
var mime_type_ass
var mime_type_au
var mime_type_avi
var mime_type_avif
var mime_type_bat
var mime_type_bcpio
var mime_type_bin
var mime_type_bmp
var mime_type_c
var mime_type_cdf
var mime_type_cpio
var mime_type_csh
var mime_type_css
var mime_type_csv
var mime_type_dll
var mime_type_doc
var mime_type_dot
var mime_type_dvi
var mime_type_eml
var mime_type_eps
var mime_type_etx
var mime_type_exe
var mime_type_gif
var mime_type_gtar
var mime_type_h
var mime_type_h5
var mime_type_hdf
var mime_type_heic
var mime_type_heif
var mime_type_htm
var mime_type_html
var mime_type_ico
var mime_type_ief
var mime_type_jpe
var mime_type_jpeg
var mime_type_jpg
var mime_type_js
var mime_type_json
var mime_type_ksh
var mime_type_latex
var mime_type_loas
var mime_type_m1v
var mime_type_m3u
var mime_type_m3u8
var mime_type_man
var mime_type_me
var mime_type_mht
var mime_type_mhtml
var mime_type_mif
var mime_type_mjs
var mime_type_mov
var mime_type_movie
var mime_type_mp2
var mime_type_mp3
var mime_type_mp4
var mime_type_mpa
var mime_type_mpe
var mime_type_mpeg
var mime_type_mpg
var mime_type_ms
var mime_type_n3
var mime_type_nc
var mime_type_nq
var mime_type_nt
var mime_type_nws
var mime_type_o
var mime_type_obj
var mime_type_oda
var mime_type_opus
var mime_type_p12
var mime_type_p7c
var mime_type_pbm
var mime_type_pdf
var mime_type_pfx
var mime_type_pgm
var mime_type_pl
var mime_type_png
var mime_type_pnm
var mime_type_pot
var mime_type_ppa
var mime_type_ppm
var mime_type_pps
var mime_type_ppt
var mime_type_ps
var mime_type_pwz
var mime_type_py
var mime_type_pyc
var mime_type_pyo
var mime_type_qt
var mime_type_ra
var mime_type_ram
var mime_type_ras
var mime_type_rdf
var mime_type_rgb
var mime_type_roff
var mime_type_rtx
var mime_type_sgm
var mime_type_sgml
var mime_type_sh
var mime_type_shar
var mime_type_snd
var mime_type_so
var mime_type_src
var mime_type_srt
var mime_type_sv4cpio
var mime_type_sv4crc
var mime_type_svg
var mime_type_swf
var mime_type_t
var mime_type_tar
var mime_type_tcl
var mime_type_tex
var mime_type_texi
var mime_type_texinfo
var mime_type_tif
var mime_type_tiff
var mime_type_tr
var mime_type_trig
var mime_type_tsv
var mime_type_txt
var mime_type_ustar
var mime_type_vcf
var mime_type_vtt
var mime_type_wasm
var mime_type_wav
var mime_type_webm
var mime_type_webmanifest
var mime_type_wiz
var mime_type_wsdl
var mime_type_xbm
var mime_type_xlb
var mime_type_xls
var mime_type_xml
var mime_type_xpdl
var mime_type_xpm
var mime_type_xsl
var mime_type_xwd
var mime_type_zip