diff --git a/.github/scripts/assert-unchanged.sh b/.github/scripts/assert-unchanged.sh new file mode 100755 index 0000000..ef32496 --- /dev/null +++ b/.github/scripts/assert-unchanged.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Assert that there are no changes in a given directory compared to HEAD. +# Expects a relative directory as the one and only argument. + +set -e + +CHECK_DIR=$1 + +# Find untracked files +UNTRACKED=$(git ls-files --others --exclude-standard "$CHECK_DIR") +# and display their content by comparing with '/dev/null' +echo "$UNTRACKED" | xargs -I _ git --no-pager diff --no-index /dev/null _ + +# Display changes in tracked files and capture non-zero exit code if so +set +e +git diff --exit-code HEAD "$CHECK_DIR" +GIT_DIFF_HEAD_EXIT_CODE=$? +set -e + +# Display changes in tracked files and capture exit status +if [ $GIT_DIFF_HEAD_EXIT_CODE -ne 0 ] || [ -n "$UNTRACKED" ]; then + echo "::error::Uncommited changes in directory '$CHECK_DIR'" + exit 1 +else + echo "::notice::No Uncommited changes, directory '$CHECK_DIR' is clean" +fi + +set +e diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ee2f57..e99fe4f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,6 @@ env: # Many color libraries just need this to be set to any value, but at least # one distinguishes color depth, where "3" -> "256-bit color". FORCE_COLOR: 3 - MYPYPATH: ${{ github.workspace }}/stubs defaults: run: @@ -78,17 +77,22 @@ jobs: # TODO upload coverage statistics, and fail on decrease? - - name: Compare example stubs + - name: Check example_pkg-stubs + # Check that stubs for example_pkg are up-to-date by regenerating them + # with docstub and looking for differences. run: | python -m docstub run -v \ --config=examples/docstub.toml \ --out-dir=examples/example_pkg-stubs \ examples/example_pkg - git diff --exit-code examples/ && echo "Stubs for example_pkg did not change" + .github/scripts/assert-unchanged.sh examples/ - - name: Generate stubs for docstub + - name: Check docstub-stubs + # Check that stubs for docstub are up-to-date by regenerating them + # with docstub and looking for differences. run: | - python -m docstub run -v src/docstub -o ${MYPYPATH}/docstub + python -m docstub run -v src/docstub -o src/docstub-stubs + .github/scripts/assert-unchanged.sh src/docstub-stubs/ - name: Check with mypy.stubtest run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1c5fdb4..bb4437c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,7 @@ exclude: | (?x)^( examples/.*-stubs/.*| + src/docstub-stubs/.*| )$ repos: diff --git a/pyproject.toml b/pyproject.toml index 953ec88..34bb588 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -163,5 +163,4 @@ ignore_missing_imports = true [tool.basedpyright] -stubPath = "stubs/" typeCheckingMode = "standard" diff --git a/src/docstub-stubs/__init__.pyi b/src/docstub-stubs/__init__.pyi new file mode 100644 index 0000000..0f6f1df --- /dev/null +++ b/src/docstub-stubs/__init__.pyi @@ -0,0 +1,5 @@ +# File generated with docstub + +from ._version import __version__ + +__all__ = ["__version__"] diff --git a/src/docstub-stubs/__main__.pyi b/src/docstub-stubs/__main__.pyi new file mode 100644 index 0000000..ce39888 --- /dev/null +++ b/src/docstub-stubs/__main__.pyi @@ -0,0 +1,6 @@ +# File generated with docstub + +from ._cli import cli + +if __name__ == "__main__": + pass diff --git a/src/docstub-stubs/_analysis.pyi b/src/docstub-stubs/_analysis.pyi new file mode 100644 index 0000000..4e439d9 --- /dev/null +++ b/src/docstub-stubs/_analysis.pyi @@ -0,0 +1,88 @@ +# File generated with docstub + +import builtins +import importlib +import json +import logging +import re +from collections.abc import Iterable +from dataclasses import asdict, dataclass +from functools import cache +from pathlib import Path +from typing import Any, ClassVar + +import libcst as cst +import libcst.matchers as cstm + +from ._utils import accumulate_qualname, module_name_from_path, pyfile_checksum + +logger: logging.Logger + +def _shared_leading_qualname(*qualnames: tuple[str]) -> str: ... +@dataclass(slots=True, frozen=True) +class PyImport: + + import_: str | None = ... + from_: str | None = ... + as_: str | None = ... + implicit: str | None = ... + + @classmethod + def typeshed_Incomplete(cls) -> PyImport: ... + def format_import(self, relative_to: str | None = ...) -> str: ... + @property + def target(self) -> str: ... + @property + def has_import(self) -> None: ... + def __post_init__(self) -> None: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + +def _is_type(value: Any) -> bool: ... +def _builtin_types() -> dict[str, PyImport]: ... +def _runtime_types_in_module(module_name: str) -> dict[str, PyImport]: ... +def common_known_types() -> dict[str, PyImport]: ... + +class TypeCollector(cst.CSTVisitor): + class ImportSerializer: + + suffix: ClassVar[str] + encoding: ClassVar[str] + + def hash_args(self, path: Path) -> str: ... + def serialize( + self, data: tuple[dict[str, PyImport], dict[str, PyImport]] + ) -> bytes: ... + def deserialize( + self, raw: bytes + ) -> tuple[dict[str, PyImport], dict[str, PyImport]]: ... + + @classmethod + def collect(cls, file: Path) -> tuple[dict[str, PyImport], dict[str, PyImport]]: ... + def __init__(self, *, module_name: str) -> None: ... + def visit_ClassDef(self, node: cst.ClassDef) -> bool: ... + def leave_ClassDef(self, original_node: cst.ClassDef) -> None: ... + def visit_FunctionDef(self, node: cst.FunctionDef) -> bool: ... + def visit_TypeAlias(self, node: cst.TypeAlias) -> bool: ... + def visit_AnnAssign(self, node: cst.AnnAssign) -> bool: ... + def visit_ImportFrom(self, node: cst.ImportFrom) -> bool: ... + def visit_Import(self, node: cst.Import) -> bool: ... + def _collect_type_annotation(self, stack: Iterable[str]) -> None: ... + +class TypeMatcher: + types: dict[str, PyImport] + type_prefixes: dict[str, PyImport] + type_nicknames: dict[str, str] + successful_queries: int + unknown_qualnames: list + current_file: Path | None + + def __init__( + self, + *, + types: dict[str, PyImport] | None = ..., + type_prefixes: dict[str, PyImport] | None = ..., + type_nicknames: dict[str, str] | None = ..., + ) -> None: ... + def _resolve_nickname(self, name: str) -> str: ... + def match(self, search: str) -> tuple[str | None, PyImport | None]: ... diff --git a/src/docstub-stubs/_cache.pyi b/src/docstub-stubs/_cache.pyi new file mode 100644 index 0000000..43c2af2 --- /dev/null +++ b/src/docstub-stubs/_cache.pyi @@ -0,0 +1,49 @@ +# File generated with docstub + +import logging +from collections.abc import Callable +from functools import cached_property +from pathlib import Path +from typing import Any, Protocol + +logger: logging.Logger + +CACHE_DIR_NAME: str + +CACHEDIR_TAG_CONTENT: str + +GITHUB_IGNORE_CONTENT: str + +def _directory_size(path: Path) -> int: ... +def create_cache(path: Path) -> None: ... +def validate_cache(path: Path) -> None: ... + +class FuncSerializer[T](Protocol): + + suffix: str + + def hash_args(self, *args: Any, **kwargs: Any) -> str: ... + def serialize(self, data: T) -> bytes: ... + def deserialize(self, raw: bytes) -> T: ... + +class FileCache: + func: Callable + serializer: FuncSerializer + sub_dir: str + cache_hits: int + cache_misses: int + cached_last_call: bool | None + + def __init__( + self, + *, + func: Callable, + serializer: FuncSerializer, + cache_dir: Path, + sub_dir: str | None = ..., + ) -> None: ... + @cached_property + def cache_dir(self) -> Path: ... + @property + def cache_sub_dir(self) -> None: ... + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... diff --git a/src/docstub-stubs/_cli.pyi b/src/docstub-stubs/_cli.pyi new file mode 100644 index 0000000..bab8dd4 --- /dev/null +++ b/src/docstub-stubs/_cli.pyi @@ -0,0 +1,57 @@ +# File generated with docstub + +import logging +import shutil +import sys +import time +from collections import Counter +from collections.abc import Iterable, Sequence +from contextlib import contextmanager +from pathlib import Path +from typing import Literal + +import click + +from ._analysis import PyImport, TypeCollector, TypeMatcher, common_known_types +from ._cache import CACHE_DIR_NAME, FileCache, validate_cache +from ._config import Config +from ._path_utils import ( + STUB_HEADER_COMMENT, + find_package_root, + walk_source_and_targets, + walk_source_package, +) +from ._report import setup_logging +from ._stubs import Py2StubTransformer, try_format_stub +from ._version import __version__ + +logger: logging.Logger + +def _cache_dir_in_cwd() -> Path: ... +def _load_configuration(config_paths: list[Path] | None = ...) -> Config: ... +def _calc_verbosity( + *, verbose: Literal[0, 1, 2], quiet: Literal[0, 1, 2] +) -> Literal[-2, -1, 0, 1, 2]: ... +def _collect_type_info( + root_path: Path, *, ignore: Sequence[str] = ..., cache: bool = ... +) -> tuple[dict[str, PyImport], dict[str, PyImport]]: ... +def _format_unknown_names(unknown_names: Iterable[str]) -> str: ... +def log_execution_time() -> None: ... +@click.group() +def cli() -> None: ... +@cli.command() +def run( + *, + root_path: Path, + out_dir: Path, + config_paths: Sequence[Path], + ignore: Sequence[str], + group_errors: bool, + allow_errors: int, + fail_on_warning: bool, + no_cache: bool, + verbose: int, + quiet: int, +) -> None: ... +@cli.command() +def clean(verbose: int, quiet: int) -> None: ... diff --git a/src/docstub-stubs/_config.pyi b/src/docstub-stubs/_config.pyi new file mode 100644 index 0000000..2bfebcf --- /dev/null +++ b/src/docstub-stubs/_config.pyi @@ -0,0 +1,31 @@ +# File generated with docstub + +import dataclasses +import logging +import tomllib +from collections.abc import Mapping +from pathlib import Path +from typing import ClassVar, Self + +logger: logging.Logger + +@dataclasses.dataclass(frozen=True, slots=True, kw_only=True) +class Config: + TEMPLATE_PATH: ClassVar[Path] + NUMPY_PATH: ClassVar[Path] + + types: dict[str, str] = ... + type_prefixes: dict[str, str] = ... + type_nicknames: dict[str, str] = ... + ignore_files: list[str] = ... + + config_paths: tuple[Path, ...] = ... + + @classmethod + def from_toml(cls, path: Path | str) -> Self: ... + def merge(self, other: Self) -> Self: ... + def to_dict(self) -> None: ... + def __post_init__(self) -> None: ... + def __repr__(self) -> str: ... + @staticmethod + def validate(mapping: Mapping) -> None: ... diff --git a/src/docstub-stubs/_docstrings.pyi b/src/docstub-stubs/_docstrings.pyi new file mode 100644 index 0000000..4fda48d --- /dev/null +++ b/src/docstub-stubs/_docstrings.pyi @@ -0,0 +1,118 @@ +# File generated with docstub + +import logging +import traceback +from collections.abc import Generator, Iterable +from dataclasses import dataclass, field +from functools import cached_property +from pathlib import Path +from typing import Any, ClassVar + +import click +import lark +import lark.visitors +import numpydoc.docscrape as npds + +from ._analysis import PyImport, TypeMatcher +from ._report import ContextReporter +from ._utils import DocstubError, escape_qualname + +logger: logging.Logger + +here: Path +grammar_path: Path + +with grammar_path.open() as file: + _grammar: str + +_lark: lark.Lark + +def _find_one_token(tree: lark.Tree, *, name: str) -> lark.Token: ... +@dataclass(frozen=True, slots=True, kw_only=True) +class Annotation: + + value: str + imports: frozenset[PyImport] = ... + + def __post_init__(self) -> None: ... + def __str__(self) -> str: ... + @classmethod + def many_as_tuple(cls, types: Iterable[Annotation]) -> Annotation: ... + @classmethod + def as_generator( + cls, + *, + yield_types: Iterable[Annotation], + receive_types: Iterable[Annotation] = ..., + return_types: Iterable[Annotation] = ..., + ) -> Annotation: ... + def as_union_with_none(self) -> Annotation: ... + @staticmethod + def _aggregate_annotations( + *types: Iterable[Annotation], + ) -> tuple[list[str], set[PyImport]]: ... + +FallbackAnnotation: Annotation + +class QualnameIsKeyword(DocstubError): + pass + +class DoctypeTransformer(lark.visitors.Transformer): + matcher: TypeMatcher + stats: dict[str, Any] + + blacklisted_qualnames: ClassVar[frozenset[str]] + + def __init__( + self, *, matcher: TypeMatcher | None = ..., **kwargs: dict[Any, Any] + ) -> None: ... + def doctype_to_annotation( + self, doctype: str + ) -> tuple[Annotation, list[tuple[str, int, int]]]: ... + def qualname(self, tree: lark.Tree) -> lark.Token: ... + def rst_role(self, tree: lark.Tree) -> lark.Token: ... + def union(self, tree: lark.Tree) -> str: ... + def subscription(self, tree: lark.Tree) -> str: ... + def natlang_literal(self, tree: lark.Tree) -> str: ... + def natlang_container(self, tree: lark.Tree) -> str: ... + def natlang_array(self, tree: lark.Tree) -> str: ... + def array_name(self, tree: lark.Tree) -> lark.Token: ... + def shape(self, tree: lark.Tree) -> lark.visitors._DiscardType: ... + def optional_info(self, tree: lark.Tree) -> lark.visitors._DiscardType: ... + def __default__( + self, data: lark.Token, children: list[lark.Token], meta: lark.tree.Meta + ) -> lark.Token | list[lark.Token]: ... + def _match_import(self, qualname: str, *, meta: lark.tree.Meta) -> str: ... + +def _uncombine_numpydoc_params( + params: list[npds.Parameter], +) -> Generator[npds.Parameter]: ... + +class DocstringAnnotations: + docstring: str + transformer: DoctypeTransformer + reporter: ContextReporter + + def __init__( + self, + docstring: str, + *, + transformer: DoctypeTransformer, + reporter: ContextReporter | None = ..., + ) -> None: ... + def _doctype_to_annotation( + self, doctype: str, ds_line: int = ... + ) -> Annotation: ... + @cached_property + def attributes(self) -> dict[str, Annotation]: ... + @cached_property + def parameters(self) -> dict[str, Annotation]: ... + @cached_property + def returns(self) -> Annotation | None: ... + @cached_property + def _returns(self) -> Annotation | None: ... + @cached_property + def _yields(self) -> Annotation | None: ... + def _handle_missing_whitespace(self, param: npds.Parameter) -> npds.Parameter: ... + def _section_annotations(self, name: str) -> dict[str, Annotation]: ... + def _find_docstring_line(self, *substrings: str) -> int: ... diff --git a/src/docstub-stubs/_path_utils.pyi b/src/docstub-stubs/_path_utils.pyi new file mode 100644 index 0000000..1108f4f --- /dev/null +++ b/src/docstub-stubs/_path_utils.pyi @@ -0,0 +1,34 @@ +# File generated with docstub + +import logging +import re +import sys +from collections.abc import Generator, Sequence +from functools import lru_cache +from pathlib import Path + +if sys.version_info >= (3, 13): + from glob import translate as glob_translate +else: + from ._vendored.stdlib import glob_translate + +logger: logging.Logger + +STUB_HEADER_COMMENT: str + +def is_docstub_generated(stub_path: Path) -> bool: ... +def is_python_or_stub_file(path: Path) -> bool: ... +def is_python_package_dir(path: Path) -> bool: ... +def find_package_root(path: Path) -> Path: ... +def glob_patterns_to_regex( + patterns: tuple[str, ...], relative_to: Path | None = ... +) -> re.Pattern | None: ... +def _walk_source_package( + path: Path, *, ignore_regex: re.Pattern +) -> Generator[Path]: ... +def walk_source_package( + path: Path, *, ignore: Sequence[str] = ... +) -> Generator[Path]: ... +def walk_source_and_targets( + root_path: Path, target_dir: Path, *, ignore: Sequence[str] = ... +) -> Generator[tuple[Path, Path]]: ... diff --git a/src/docstub-stubs/_report.pyi b/src/docstub-stubs/_report.pyi new file mode 100644 index 0000000..9b0740b --- /dev/null +++ b/src/docstub-stubs/_report.pyi @@ -0,0 +1,57 @@ +# File generated with docstub + +import dataclasses +import logging +from pathlib import Path +from textwrap import indent +from typing import Any, ClassVar, Self, TextIO + +import click + +logger: logging.Logger + +@dataclasses.dataclass(kw_only=True, slots=True, frozen=True) +class ContextReporter: + + logger: logging.Logger + path: Path | None = ... + line: int | None = ... + + def copy_with( + self, + *, + logger: logging.Logger | None = ..., + path: Path | None = ..., + line: int | None = ..., + line_offset: int | None = ... + ) -> Self: ... + def report( + self, short: str, *, log_level: int, details: str | None = ..., **log_kw: Any + ) -> None: ... + def debug( + self, short: str, *, details: str | None = ..., **log_kw: Any + ) -> None: ... + def info(self, short: str, *, details: str | None = ..., **log_kw: Any) -> None: ... + def warn(self, short: str, *, details: str | None = ..., **log_kw: Any) -> None: ... + def error( + self, short: str, *, details: str | None = ..., **log_kw: Any + ) -> None: ... + def __post_init__(self) -> None: ... + @staticmethod + def underline(line: str, *, char: str = ...) -> str: ... + +class ReportHandler(logging.StreamHandler): + group_errors: bool + error_count: int + warning_count: int + + level_to_color: ClassVar[dict[int, str]] + + def __init__( + self, stream: TextIO | None = ..., group_errors: bool = ... + ) -> None: ... + def format(self, record: logging.LogRecord) -> str: ... + def emit(self, record: logging.LogRecord) -> None: ... + def emit_grouped(self) -> None: ... + +def setup_logging(*, verbosity: int, group_errors: bool) -> ReportHandler: ... diff --git a/src/docstub-stubs/_stubs.pyi b/src/docstub-stubs/_stubs.pyi new file mode 100644 index 0000000..a6521f6 --- /dev/null +++ b/src/docstub-stubs/_stubs.pyi @@ -0,0 +1,127 @@ +# File generated with docstub + +import enum +import logging +from dataclasses import dataclass +from functools import wraps +from pathlib import Path +from typing import ClassVar, Literal + +import libcst as cst +import libcst.matchers as cstm +from _typeshed import Incomplete + +from ._analysis import PyImport, TypeMatcher +from ._docstrings import ( + Annotation, + DocstringAnnotations, + DoctypeTransformer, + FallbackAnnotation, +) +from ._report import ContextReporter +from ._utils import module_name_from_path + +logger: logging.Logger + +def try_format_stub(stub: str) -> str: ... + +class ScopeType(enum.StrEnum): + + MODULE = "module" + CLASS = "class" + FUNC = "func" + METHOD = "method" + CLASSMETHOD = "classmethod" + STATICMETHOD = "staticmethod" + +@dataclass(slots=True, frozen=True) +class _Scope: + + type: ScopeType + node: cst.CSTNode | None = ... + + @property + def has_self_or_cls(self) -> bool: ... + @property + def is_method(self) -> bool: ... + @property + def is_class_init(self) -> bool: ... + @property + def is_dataclass(self) -> bool: ... + +def _get_docstring_node( + node: cst.FunctionDef | cst.ClassDef | cst.Module, +) -> cst.SimpleString | cst.ConcatenatedString | None: ... +def _log_error_with_line_context(cls: Py2StubTransformer) -> Py2StubTransformer: ... +def _docstub_comment_directives(cls: Py2StubTransformer) -> Py2StubTransformer: ... +def _inline_node_as_code(node: cst.CSTNode) -> str: ... + +class Py2StubTransformer(cst.CSTTransformer): + transformer: DoctypeTransformer + + METADATA_DEPENDENCIES: ClassVar[tuple] + + _body_replacement: ClassVar[cst.SimpleStatementSuite] + _Annotation_Incomplete: ClassVar[cst.Annotation] + _Annotation_None: ClassVar[cst.Annotation] + + def __init__(self, *, matcher: TypeMatcher | None = ...) -> None: ... + @property + def current_source(self) -> Path: ... + @current_source.setter + def current_source(self, value: Path) -> None: ... + @property + def is_inside_function_def(self) -> bool: ... + def python_to_stub(self, source: str, *, module_path: Path | None = ...) -> str: ... + def visit_ClassDef(self, node: cst.ClassDef) -> Literal[True]: ... + def leave_ClassDef( + self, original_node: cst.ClassDef, updated_node: cst.ClassDef + ) -> cst.ClassDef: ... + def visit_FunctionDef(self, node: cst.FunctionDef) -> Literal[True]: ... + def visit_IndentedBlock(self, node: cst.IndentedBlock) -> bool: ... + def visit_SimpleStatementSuite(self, node: cst.SimpleStatementSuite) -> bool: ... + def leave_FunctionDef( + self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef + ) -> cst.FunctionDef: ... + def leave_Param( + self, original_node: cst.Param, updated_node: cst.Param + ) -> cst.Param: ... + def leave_Expr( + self, original_node: cst.Expr, updated_node: cst.Expr + ) -> cst.RemovalSentinel: ... + def leave_Comment( + self, original_node: cst.Comment, updated_node: cst.Comment + ) -> cst.Comment: ... + def leave_Assign( + self, original_node: cst.Assign, updated_node: cst.Assign + ) -> cst.Assign | cst.FlattenSentinel: ... + def leave_AnnAssign( + self, original_node: cst.AnnAssign, updated_node: cst.AnnAssign + ) -> cst.AnnAssign: ... + def visit_Module(self, node: cst.Module) -> Literal[True]: ... + def leave_Module( + self, original_node: cst.Module, updated_node: cst.Module + ) -> cst.Module: ... + def visit_Lambda(self, node: cst.Lambda) -> Literal[False]: ... + def leave_Decorator( + self, original_node: cst.Decorator, updated_node: cst.Decorator + ) -> cst.Decorator | cst.RemovalSentinel: ... + @staticmethod + def _parse_imports( + imports: set[PyImport], *, current_module: str | None = ... + ) -> tuple[cst.SimpleStatementLine, ...]: ... + def _function_type(self, func_def: cst.FunctionDef) -> ScopeType: ... + def _annotations_from_node( + self, node: cst.FunctionDef | cst.ClassDef | cst.Module + ) -> DocstringAnnotations: ... + def _create_annotated_assign( + self, + *, + name: str, + trailing_semicolon: bool = ..., + reporter: ContextReporter | None = ... + ) -> cst.AnnAssign: ... + def _insert_instance_attributes( + self, updated_node: cst.ClassDef, attributes: dict[str, Annotation] + ) -> cst.ClassDef: ... + def _reporter_with_ctx(self, node: cst.CSTNode) -> ContextReporter: ... diff --git a/src/docstub-stubs/_utils.pyi b/src/docstub-stubs/_utils.pyi new file mode 100644 index 0000000..8b8b4bd --- /dev/null +++ b/src/docstub-stubs/_utils.pyi @@ -0,0 +1,17 @@ +# File generated with docstub + +import itertools +import re +from collections.abc import Callable +from functools import lru_cache, wraps +from pathlib import Path +from zlib import crc32 + +def accumulate_qualname(qualname: str, *, start_right: bool = ...) -> None: ... +def escape_qualname(name: str) -> str: ... +def _resolve_path_before_caching(func: Callable) -> Callable: ... +def module_name_from_path(path: Path) -> str: ... +def pyfile_checksum(path: Path) -> str: ... + +class DocstubError(Exception): + pass diff --git a/src/docstub-stubs/_vendored/__init__.pyi b/src/docstub-stubs/_vendored/__init__.pyi new file mode 100644 index 0000000..9cf3027 --- /dev/null +++ b/src/docstub-stubs/_vendored/__init__.pyi @@ -0,0 +1 @@ +# File generated with docstub diff --git a/src/docstub-stubs/_vendored/stdlib.pyi b/src/docstub-stubs/_vendored/stdlib.pyi new file mode 100644 index 0000000..b4ff650 --- /dev/null +++ b/src/docstub-stubs/_vendored/stdlib.pyi @@ -0,0 +1,14 @@ +# File generated with docstub + +import os +import re +from collections.abc import Sequence + +def _fnmatch_translate(pat: str, STAR: str, QUESTION_MARK: str) -> str: ... +def glob_translate( + pat: str, + *, + recursive: bool = ..., + include_hidden: bool = ..., + seps: Sequence[str] | None = ..., +) -> str: ... diff --git a/src/docstub-stubs/_version.pyi b/src/docstub-stubs/_version.pyi new file mode 100644 index 0000000..d02022d --- /dev/null +++ b/src/docstub-stubs/_version.pyi @@ -0,0 +1,8 @@ +__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"] + +VERSION_TUPLE = tuple[int | str, ...] + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE diff --git a/src/docstub-stubs/py.typed b/src/docstub-stubs/py.typed new file mode 100644 index 0000000..e69de29