from textwrap import TextWrapper from typing import Optional from typing import Tuple from typing import Union def _wrap_docstring(docstring: str) -> str: wrapper = TextWrapper(width=100) lines = [] for long_line in docstring.splitlines(keepends=False): lines.extend(wrapper.wrap(long_line)) return "\n".join(lines) def _get_docstring(name: str, doc: Union[None, str, Tuple[str, str]], type_: Optional[type]) -> str: doc_string_parts = [] if isinstance(doc, tuple): doc_string_parts.append(_wrap_docstring(doc[0])) doc_string_parts.append("see https://postgis.net/docs/{0}.html".format(doc[1])) elif doc is not None: doc_string_parts.append(_wrap_docstring(doc)) doc_string_parts.append("see https://postgis.net/docs/{0}.html".format(name)) if type_ is not None: return_type_str = "{0}.{1}".format(type_.__module__, type_.__name__) doc_string_parts.append("Return type: :class:`{0}`.".format(return_type_str)) return "\n\n".join(doc_string_parts) def _replace_indent(text: str, indent: str) -> str: lines = [] for i, line in enumerate(text.splitlines()): if i == 0 or not line.strip(): lines.append(line) else: lines.append(f"{indent}{line}") return "\n".join(lines) def _generate_stubs() -> str: """Generates type stubs for the dynamic functions described in `geoalchemy2/_functions.py`.""" from geoalchemy2._functions import _FUNCTIONS from geoalchemy2.functions import ST_AsGeoJSON header = '''\ # this file is automatically generated from typing import List import sqlalchemy from sqlalchemy.sql import functions from sqlalchemy.sql.elements import ColumnElement from sqlalchemy.sql.selectable import FromClause import geoalchemy2.types class GenericFunction(functions.GenericFunction): ... class TableRowElement(ColumnElement): inherit_cache: bool = ... """The cache is disabled for this class.""" def __init__(self, selectable: bool) -> None: ... @property def _from_objects(self) -> List[FromClause]: ... ''' stub_file_parts = [header] functions = _FUNCTIONS.copy() functions.insert(0, ("ST_AsGeoJSON", None, ST_AsGeoJSON.__doc__)) for name, type_, doc_parts in functions: doc = _replace_indent(_get_docstring(name, doc_parts, type_), " ") if type_ is None: type_str = "" else: type_str = f"\n\n type = {type_.__module__}.{type_.__name__}()" signature = f'''\ class {name}(GenericFunction): """ {doc} """{type_str} ''' stub_file_parts.append(signature) return "\n".join(stub_file_parts)