From bed8f016409426c2900b1f125c1d7cd18bb825a2 Mon Sep 17 00:00:00 2001 From: Travis Hathaway Date: Sat, 14 Jan 2023 12:28:48 +0100 Subject: [PATCH] Tagging version 0.1.3 Things are getting closer and closer to what I would call a stable release (this will be 0.2.0). In this commit, I've added stub files which I'm hoping will help out mypy for the plugin modules. --- .pre-commit-config.yaml | 10 ++++++---- latz/__version__.py | 2 +- latz/plugins/__init__.py | 3 +-- latz/plugins/hookspec.py | 16 ++++++++++++++-- latz/plugins/types.py | 17 ----------------- mypy.ini | 2 +- pyproject.toml | 6 +++++- stubs/latz/__init__.pyi | 0 stubs/latz/cli.pyi | 6 ++++++ stubs/latz/commands/__init__.pyi | 0 stubs/latz/commands/config/__init__.pyi | 0 stubs/latz/commands/config/commands.pyi | 10 ++++++++++ stubs/latz/commands/config/validators.pyi | 9 +++++++++ stubs/latz/commands/search.pyi | 3 +++ stubs/latz/config/__init__.pyi | 2 ++ stubs/latz/config/errors.pyi | 9 +++++++++ stubs/latz/config/main.pyi | 23 +++++++++++++++++++++++ stubs/latz/config/models.pyi | 8 ++++++++ stubs/latz/constants.pyi | 8 ++++++++ stubs/latz/image.pyi | 17 +++++++++++++++++ stubs/latz/plugins/__init__.pyi | 1 + stubs/latz/plugins/hookspec.pyi | 17 +++++++++++++++++ stubs/latz/plugins/image/__init__.pyi | 0 stubs/latz/plugins/image/placeholder.pyi | 20 ++++++++++++++++++++ stubs/latz/plugins/image/unsplash.pyi | 22 ++++++++++++++++++++++ stubs/latz/plugins/manager.pyi | 19 +++++++++++++++++++ 26 files changed, 202 insertions(+), 28 deletions(-) delete mode 100644 latz/plugins/types.py create mode 100644 stubs/latz/__init__.pyi create mode 100644 stubs/latz/cli.pyi create mode 100644 stubs/latz/commands/__init__.pyi create mode 100644 stubs/latz/commands/config/__init__.pyi create mode 100644 stubs/latz/commands/config/commands.pyi create mode 100644 stubs/latz/commands/config/validators.pyi create mode 100644 stubs/latz/commands/search.pyi create mode 100644 stubs/latz/config/__init__.pyi create mode 100644 stubs/latz/config/errors.pyi create mode 100644 stubs/latz/config/main.pyi create mode 100644 stubs/latz/config/models.pyi create mode 100644 stubs/latz/constants.pyi create mode 100644 stubs/latz/image.pyi create mode 100644 stubs/latz/plugins/__init__.pyi create mode 100644 stubs/latz/plugins/hookspec.pyi create mode 100644 stubs/latz/plugins/image/__init__.pyi create mode 100644 stubs/latz/plugins/image/placeholder.pyi create mode 100644 stubs/latz/plugins/image/unsplash.pyi create mode 100644 stubs/latz/plugins/manager.pyi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cc21816..f5bbc27 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,10 +6,12 @@ repos: exclude: "mkdocs.yml" - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/pre-commit/mirrors-mypy - rev: '' # Use the sha / tag you want to point at - hooks: - - id: mypy +# Disabling mypy until I can figure out how to exclude the "stubs" directory 🤷‍ +# - repo: https://github.com/pre-commit/mirrors-mypy +# rev: '' # Use the sha / tag you want to point at +# hooks: +# - id: mypy +# args: ["--exclude=stubs", "--no-strict-optional", "--ignore-missing-imports"] - repo: https://github.com/asottile/pyupgrade rev: v3.3.1 hooks: diff --git a/latz/__version__.py b/latz/__version__.py index b6bba40..8ed18e2 100644 --- a/latz/__version__.py +++ b/latz/__version__.py @@ -1,3 +1,3 @@ __title__ = "latz" __description__ = "Tool for finding images. Maybe with location 🤷..." -__version__ = "0.1.2" +__version__ = "0.1.3" diff --git a/latz/plugins/__init__.py b/latz/plugins/__init__.py index 0519e64..3eba3e5 100644 --- a/latz/plugins/__init__.py +++ b/latz/plugins/__init__.py @@ -1,2 +1 @@ -from .hookspec import hookimpl # noqa: F401 -from .types import ImageAPIPlugin # noqa: F401 +from .hookspec import hookimpl, ImageAPIPlugin # noqa: F401 diff --git a/latz/plugins/hookspec.py b/latz/plugins/hookspec.py index 2fbc63d..ad46211 100644 --- a/latz/plugins/hookspec.py +++ b/latz/plugins/hookspec.py @@ -1,14 +1,26 @@ from collections.abc import Iterable +from typing import NamedTuple, Any import pluggy # type: ignore +from pydantic import BaseModel -from ..constants import APP_NAME -from .types import ImageAPIPlugin +from latz.constants import APP_NAME +from latz.image import ImageAPIContextManager hookspec = pluggy.HookspecMarker(APP_NAME) hookimpl = pluggy.HookimplMarker(APP_NAME) +class ImageAPIPlugin(NamedTuple): + """ + Holds the metadata, config fields and context manager for the image search backend + """ + + name: str + image_api_context_manager: type[ImageAPIContextManager] + config_fields: dict[str, tuple[type[BaseModel], Any]] + + class AppHookSpecs: """Holds all hookspecs for this application""" diff --git a/latz/plugins/types.py b/latz/plugins/types.py deleted file mode 100644 index 7e1c699..0000000 --- a/latz/plugins/types.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import annotations - -from typing import NamedTuple, Any - -from pydantic import BaseModel - -from ..image import ImageAPIContextManager - - -class ImageAPIPlugin(NamedTuple): - """ - Holds the metadata and class for the image search backend - """ - - name: str - image_api_context_manager: type[ImageAPIContextManager] - config_fields: dict[str, tuple[type[BaseModel], Any]] diff --git a/mypy.ini b/mypy.ini index e727faf..772c4e3 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,3 +1,3 @@ [mypy] -files=latz/**/*.py warn_no_return = False +exclude = "stubs/*" diff --git a/pyproject.toml b/pyproject.toml index 1451866..6ded69c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,14 @@ [tool.poetry] name = "latz" -version = "0.1.2" +version = "0.1.3" description = "CLI Program for downloading images. Maybe by location too..." authors = ["Travis Hathaway "] license = "GNU v3" readme = "README.md" +packages = [ + { include = "latz/**/*.py" }, + { include = "stubs/**/*.pyi" } +] [tool.poetry.dependencies] python = "^3.10" diff --git a/stubs/latz/__init__.pyi b/stubs/latz/__init__.pyi new file mode 100644 index 0000000..e69de29 diff --git a/stubs/latz/cli.pyi b/stubs/latz/cli.pyi new file mode 100644 index 0000000..77ac902 --- /dev/null +++ b/stubs/latz/cli.pyi @@ -0,0 +1,6 @@ +from .commands import config_group as config_group, search_command as search_command +from .config import BaseAppConfig as BaseAppConfig, ConfigError as ConfigError, get_app_config as get_app_config +from .constants import CONFIG_FILES as CONFIG_FILES +from .plugins.manager import get_plugin_manager as get_plugin_manager + +def cli(ctx) -> None: ... diff --git a/stubs/latz/commands/__init__.pyi b/stubs/latz/commands/__init__.pyi new file mode 100644 index 0000000..e69de29 diff --git a/stubs/latz/commands/config/__init__.pyi b/stubs/latz/commands/config/__init__.pyi new file mode 100644 index 0000000..e69de29 diff --git a/stubs/latz/commands/config/commands.pyi b/stubs/latz/commands/config/commands.pyi new file mode 100644 index 0000000..b49e7ad --- /dev/null +++ b/stubs/latz/commands/config/commands.pyi @@ -0,0 +1,10 @@ +from ...config import ConfigError as ConfigError, parse_config_file_as_json as parse_config_file_as_json, write_config_file as write_config_file +from ...constants import CONFIG_FILE_CWD as CONFIG_FILE_CWD, CONFIG_FILE_HOME_DIR as CONFIG_FILE_HOME_DIR +from .validators import ConfigValuesValidator as ConfigValuesValidator +from _typeshed import Incomplete + +validate_and_parse_config_values: Incomplete + +def group() -> None: ... +def show_command(ctx) -> None: ... +def set_command(home, config_values) -> None: ... diff --git a/stubs/latz/commands/config/validators.pyi b/stubs/latz/commands/config/validators.pyi new file mode 100644 index 0000000..1ef6e3e --- /dev/null +++ b/stubs/latz/commands/config/validators.pyi @@ -0,0 +1,9 @@ +from typing import Any + +class ConfigValuesValidator: + def __init__(self) -> None: ... + def __call__(self, ctx, _, values) -> dict[str, str]: ... + def validate_single_value(self, value: str) -> dict: ... + +def get_dotted_path_value(nest: dict, dotted_path: str) -> Any: ... +def get_nested_dict_from_path(path: str, value: Any) -> dict: ... diff --git a/stubs/latz/commands/search.pyi b/stubs/latz/commands/search.pyi new file mode 100644 index 0000000..2373c0c --- /dev/null +++ b/stubs/latz/commands/search.pyi @@ -0,0 +1,3 @@ +from ..image import ImageAPI as ImageAPI + +def command(ctx, query: str): ... diff --git a/stubs/latz/config/__init__.pyi b/stubs/latz/config/__init__.pyi new file mode 100644 index 0000000..51b05a4 --- /dev/null +++ b/stubs/latz/config/__init__.pyi @@ -0,0 +1,2 @@ +from .main import ConfigError as ConfigError, get_app_config as get_app_config, parse_config_file_as_json as parse_config_file_as_json, write_config_file as write_config_file +from .models import BaseAppConfig as BaseAppConfig diff --git a/stubs/latz/config/errors.pyi b/stubs/latz/config/errors.pyi new file mode 100644 index 0000000..bdc2384 --- /dev/null +++ b/stubs/latz/config/errors.pyi @@ -0,0 +1,9 @@ +from collections.abc import Sequence +from pathlib import Path +from pydantic import ValidationError as ValidationError + +PARSE_ERROR_SUGGESTION: str +CONFIG_ERROR_PREFIX: str + +def format_validation_error(exc: ValidationError, path: Path) -> str: ... +def format_all_validation_errors(validation_errors: Sequence[str]) -> str: ... diff --git a/stubs/latz/config/main.pyi b/stubs/latz/config/main.pyi new file mode 100644 index 0000000..ae6378d --- /dev/null +++ b/stubs/latz/config/main.pyi @@ -0,0 +1,23 @@ +from .errors import format_all_validation_errors as format_all_validation_errors, format_validation_error as format_validation_error +from .models import BaseAppConfig as BaseAppConfig +from _typeshed import Incomplete +from collections.abc import Iterable, Sequence +from pathlib import Path +from typing import Any, NamedTuple + +logger: Incomplete + +class ConfigError(Exception): ... + +class ParsedConfigFile(NamedTuple): + path: Path + data: Union[dict, None] + error: Union[str, None] + model: Union[BaseAppConfig, None] + +def merge_app_configs(app_configs: Iterable[BaseAppConfig], model_class: type[BaseAppConfig]) -> BaseAppConfig: ... +def parse_config_file_as_json(path: Path) -> ParsedConfigFile: ... +def parse_app_config_model(parsed_config: ParsedConfigFile, model_class: type[BaseAppConfig]) -> ParsedConfigFile: ... +def parse_config_files(paths: Sequence[Path]) -> Union[tuple[ParsedConfigFile, ...], None]: ... +def get_app_config(paths: Sequence[Path], model_class: type[BaseAppConfig]) -> BaseAppConfig: ... +def write_config_file(config_file_data: dict[str, Any], config_file: Path) -> None: ... diff --git a/stubs/latz/config/models.pyi b/stubs/latz/config/models.pyi new file mode 100644 index 0000000..d31d508 --- /dev/null +++ b/stubs/latz/config/models.pyi @@ -0,0 +1,8 @@ +from ..constants import ENV_PREFIX as ENV_PREFIX +from _typeshed import Incomplete +from pydantic import BaseSettings + +class BaseAppConfig(BaseSettings): + backend: str + class Config: + env_prefix: Incomplete diff --git a/stubs/latz/constants.pyi b/stubs/latz/constants.pyi new file mode 100644 index 0000000..0612481 --- /dev/null +++ b/stubs/latz/constants.pyi @@ -0,0 +1,8 @@ +from _typeshed import Incomplete + +APP_NAME: str +ENV_PREFIX: str +CONFIG_FILE_NAME: str +CONFIG_FILE_CWD: Incomplete +CONFIG_FILE_HOME_DIR: Incomplete +CONFIG_FILES: Incomplete diff --git a/stubs/latz/image.pyi b/stubs/latz/image.pyi new file mode 100644 index 0000000..3ff154d --- /dev/null +++ b/stubs/latz/image.pyi @@ -0,0 +1,17 @@ +from typing import NamedTuple, Protocol + +class ImageSearchResult(NamedTuple): + url: Union[str, None] + width: Union[int, None] + height: Union[int, None] + +class ImageSearchResultSet(NamedTuple): + results: tuple[ImageSearchResult, ...] + total_number_results: Union[int, None] + +class ImageAPIContextManager(Protocol): + def __enter__(self) -> ImageAPI: ... + def __exit__(self, exc_type, exc_val, exc_tb) -> None: ... + +class ImageAPI(Protocol): + def search(self, query: str) -> ImageSearchResultSet: ... diff --git a/stubs/latz/plugins/__init__.pyi b/stubs/latz/plugins/__init__.pyi new file mode 100644 index 0000000..432f985 --- /dev/null +++ b/stubs/latz/plugins/__init__.pyi @@ -0,0 +1 @@ +from .hookspec import ImageAPIPlugin as ImageAPIPlugin, hookimpl as hookimpl diff --git a/stubs/latz/plugins/hookspec.pyi b/stubs/latz/plugins/hookspec.pyi new file mode 100644 index 0000000..549d1ca --- /dev/null +++ b/stubs/latz/plugins/hookspec.pyi @@ -0,0 +1,17 @@ +from _typeshed import Incomplete +from collections.abc import Iterable +from latz.constants import APP_NAME as APP_NAME +from latz.image import ImageAPIContextManager as ImageAPIContextManager +from pydantic import BaseModel as BaseModel +from typing import Any, NamedTuple + +hookspec: Incomplete +hookimpl: Incomplete + +class ImageAPIPlugin(NamedTuple): + name: str + image_api_context_manager: type[ImageAPIContextManager] + config_fields: dict[str, tuple[type[BaseModel], Any]] + +class AppHookSpecs: + def image_api(self) -> Iterable[ImageAPIPlugin]: ... diff --git a/stubs/latz/plugins/image/__init__.pyi b/stubs/latz/plugins/image/__init__.pyi new file mode 100644 index 0000000..e69de29 diff --git a/stubs/latz/plugins/image/placeholder.pyi b/stubs/latz/plugins/image/placeholder.pyi new file mode 100644 index 0000000..6894e0a --- /dev/null +++ b/stubs/latz/plugins/image/placeholder.pyi @@ -0,0 +1,20 @@ +from .. import ImageAPIPlugin as ImageAPIPlugin, hookimpl as hookimpl +from ...image import ImageSearchResult as ImageSearchResult, ImageSearchResultSet as ImageSearchResultSet +from _typeshed import Incomplete +from collections.abc import Generator +from pydantic import BaseModel +from typing import Literal + +PLUGIN_NAME: str + +class PlaceholderBackendConfig(BaseModel): + type: Literal['bear', 'kitten'] + +CONFIG_FIELDS: Incomplete + +class PlaceholderImageAPI: + def __init__(self, placeholder_type: str) -> None: ... + def search(self, query: str) -> ImageSearchResultSet: ... + +def placeholder_context_manager(config) -> Generator[Incomplete, None, None]: ... +def image_api(): ... diff --git a/stubs/latz/plugins/image/unsplash.pyi b/stubs/latz/plugins/image/unsplash.pyi new file mode 100644 index 0000000..fd61969 --- /dev/null +++ b/stubs/latz/plugins/image/unsplash.pyi @@ -0,0 +1,22 @@ +from .. import ImageAPIPlugin as ImageAPIPlugin, hookimpl as hookimpl +from ...image import ImageSearchResult as ImageSearchResult, ImageSearchResultSet as ImageSearchResultSet +from _typeshed import Incomplete +from collections.abc import Iterator +from httpx import Client +from pydantic import BaseModel + +PLUGIN_NAME: str + +class UnsplashBackendConfig(BaseModel): + access_key: str + +CONFIG_FIELDS: Incomplete +BASE_URL: str +SEARCH_ENDPOINT: str + +class UnsplashImageAPI: + def __init__(self, client: Client) -> None: ... + def search(self, query: str) -> ImageSearchResultSet: ... + +def unsplash_context_manager(config) -> Iterator[UnsplashImageAPI]: ... +def image_api(): ... diff --git a/stubs/latz/plugins/manager.pyi b/stubs/latz/plugins/manager.pyi new file mode 100644 index 0000000..dd457f3 --- /dev/null +++ b/stubs/latz/plugins/manager.pyi @@ -0,0 +1,19 @@ +from ..config.models import BaseAppConfig as BaseAppConfig +from ..constants import APP_NAME as APP_NAME +from ..image import ImageAPI as ImageAPI +from .hookspec import AppHookSpecs as AppHookSpecs +from .image import placeholder as placeholder, unsplash as unsplash +from collections.abc import Callable as Callable +from pluggy import PluginManager + +class AppPluginManager(PluginManager): + def __init__(self, *args, **kwargs) -> None: ... + def reset_cache(self) -> None: ... + @property + def image_api_names(self) -> tuple[str, ...]: ... + @property + def image_api_config_fields(self) -> dict: ... + def get_image_api_context_manager(self, app_config: BaseAppConfig) -> ImageAPI: ... + def get_backend_validator_func(self) -> Callable: ... + +def get_plugin_manager() -> AppPluginManager: ...