diff --git a/mypy_boto3_builder/chat/prompts/base_prompt.py b/mypy_boto3_builder/chat/prompts/base_prompt.py index 910d7904..f88c012f 100644 --- a/mypy_boto3_builder/chat/prompts/base_prompt.py +++ b/mypy_boto3_builder/chat/prompts/base_prompt.py @@ -7,7 +7,7 @@ from abc import ABC, abstractmethod from collections.abc import Iterator from contextlib import contextmanager -from typing import Any, ClassVar, Final, Generic, TypeVar +from typing import Any, ClassVar, Final, Generic, NoReturn, TypeVar from unittest.mock import patch from prompt_toolkit import PromptSession @@ -82,7 +82,7 @@ def get_prompt_tokens(self) -> list[tuple[str, str]]: return TextStyle.text.stylize(self._get_prompt_tokens()) @abstractmethod - def _get_prompt_tokens(self) -> Message: + def _get_prompt_tokens(self) -> NoReturn: raise NotImplementedError("Method is not implemented") def _is_answered(self) -> bool: @@ -100,7 +100,7 @@ def get_question(self) -> Question: return self._question @abstractmethod - def _create_question(self) -> Question: + def _create_question(self) -> NoReturn: raise NotImplementedError("Method is not implemented") def _patch_answer(self) -> None: diff --git a/mypy_boto3_builder/enums/product.py b/mypy_boto3_builder/enums/product.py index a172681b..29721c60 100644 --- a/mypy_boto3_builder/enums/product.py +++ b/mypy_boto3_builder/enums/product.py @@ -41,6 +41,8 @@ class Product(Enum): boto3_stubs_custom = "boto3-custom" boto3_stubs_docs = "boto3-docs" + mypy_boto3 = "mypy-boto3" + def __str__(self) -> str: """ Get string representation for debugging. @@ -86,6 +88,8 @@ def get_library(self) -> ProductLibrary: | Product.types_aioboto3_custom ): return ProductLibrary.aioboto3 + case Product.mypy_boto3: + return ProductLibrary.mypy_boto3 def get_type(self) -> ProductType: """ @@ -97,6 +101,7 @@ def get_type(self) -> ProductType: | Product.boto3_stubs | Product.types_aiobotocore | Product.types_aioboto3 + | Product.mypy_boto3 ): return ProductType.stubs case ( diff --git a/mypy_boto3_builder/enums/product_library.py b/mypy_boto3_builder/enums/product_library.py index 9880acaa..ae5c0d9b 100644 --- a/mypy_boto3_builder/enums/product_library.py +++ b/mypy_boto3_builder/enums/product_library.py @@ -16,15 +16,14 @@ class ProductLibrary(Enum): boto3_legacy = "boto3-legacy" aiobotocore = "aiobotocore" aioboto3 = "aioboto3" + mypy_boto3 = "mypy_boto3" def get_library_name(self) -> str: """ Get library name. """ match self: - case ProductLibrary.boto3: - return "boto3" - case ProductLibrary.boto3_legacy: + case ProductLibrary.boto3 | ProductLibrary.boto3_legacy | ProductLibrary.mypy_boto3: return "boto3" case ProductLibrary.aiobotocore: return "aiobotocore" @@ -36,7 +35,7 @@ def get_chat_choice(self) -> str: Get chat choice. """ match self: - case ProductLibrary.boto3: + case ProductLibrary.boto3 | ProductLibrary.mypy_boto3: return "boto3" case ProductLibrary.boto3_legacy: return "boto3-stubs" diff --git a/mypy_boto3_builder/generators/aioboto3_generator.py b/mypy_boto3_builder/generators/aioboto3_generator.py index 9af668eb..f0b803c0 100644 --- a/mypy_boto3_builder/generators/aioboto3_generator.py +++ b/mypy_boto3_builder/generators/aioboto3_generator.py @@ -5,6 +5,7 @@ """ from pathlib import Path +from typing import NoReturn from mypy_boto3_builder.constants import ( StaticStubsPath, @@ -24,8 +25,8 @@ parse_types_aiobotocore_package, ) from mypy_boto3_builder.postprocessors.aioboto3 import AioBoto3Postprocessor -from mypy_boto3_builder.structures.package import Package from mypy_boto3_builder.structures.packages.service_package import ServicePackage +from mypy_boto3_builder.structures.packages.types_aioboto3_package import TypesAioBoto3Package from mypy_boto3_builder.writers.package_writer import PackageWriter @@ -55,16 +56,16 @@ def _get_postprocessor(self, service_package: ServicePackage) -> AioBoto3Postpro """ return AioBoto3Postprocessor(service_package, self.main_service_names) - def generate_stubs(self) -> list[Package]: + def generate_stubs(self) -> TypesAioBoto3Package | None: """ Generate `types-aioboto3` package. """ package_data = TypesAioBoto3PackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") - return [] + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") + return None self.logger.info(f"Generating {package_data.pypi_name} {version}") package = parse_types_aioboto3_package( @@ -79,17 +80,17 @@ def generate_stubs(self) -> list[Package]: template_path=TemplatePath.types_aioboto3, static_files_path=self._get_static_files_path(), ) - return [package] + return package - def generate_stubs_lite(self) -> Package | None: + def generate_stubs_lite(self) -> TypesAioBoto3Package | None: """ Generate `types-aioboto3-lite` package. """ package_data = TypesAioBoto3LitePackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") return None self.logger.info(f"Generating {package_data.pypi_name} {version}") @@ -137,18 +138,19 @@ def generate_docs(self) -> None: version=self.version, ) - def generate_service_stubs(self) -> list[ServicePackage]: + def generate_service_stubs(self) -> NoReturn: """ - Do nothing. + Not implemented. """ - return [] + raise NotImplementedError("Should never be called") - def generate_full_stubs(self) -> None: + def generate_full_stubs(self) -> NoReturn: """ - Do nothing. + Not implemented. """ + raise NotImplementedError("Should never be called") - def generate_custom_stubs(self) -> Package: + def generate_custom_stubs(self) -> TypesAioBoto3Package: """ Generate `types-aioboto3-custom` package. """ diff --git a/mypy_boto3_builder/generators/aiobotocore_generator.py b/mypy_boto3_builder/generators/aiobotocore_generator.py index 756c2e3b..86f54aa7 100644 --- a/mypy_boto3_builder/generators/aiobotocore_generator.py +++ b/mypy_boto3_builder/generators/aiobotocore_generator.py @@ -21,8 +21,8 @@ ) from mypy_boto3_builder.parsers.parse_wrapper_package import parse_types_aiobotocore_package from mypy_boto3_builder.postprocessors.aiobotocore import AioBotocorePostprocessor -from mypy_boto3_builder.structures.package import Package from mypy_boto3_builder.structures.packages.service_package import ServicePackage +from mypy_boto3_builder.structures.packages.types_aiobotocore_package import TypesAioBotocorePackage class AioBotocoreGenerator(BaseGenerator): @@ -45,16 +45,16 @@ def _get_postprocessor(self, service_package: ServicePackage) -> AioBotocorePost """ return AioBotocorePostprocessor(service_package, self.main_service_names) - def generate_stubs(self) -> list[Package]: + def generate_stubs(self) -> TypesAioBotocorePackage | None: """ Generate `types-aiobotocore` package. """ package_data = TypesAioBotocorePackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") - return [] + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") + return None self.logger.info(f"Generating {package_data.pypi_name} {version}") @@ -70,17 +70,17 @@ def generate_stubs(self) -> list[Package]: template_path=TemplatePath.types_aiobotocore, static_files_path=self._get_static_files_path(), ) - return [package] + return package - def generate_stubs_lite(self) -> Package | None: + def generate_stubs_lite(self) -> TypesAioBotocorePackage | None: """ Generate `types-aiobotocore-lite` package. """ package_data = TypesAioBotocoreLitePackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") return None self.logger.info(f"Generating {package_data.pypi_name} {version}") @@ -128,15 +128,15 @@ def generate_docs(self) -> None: version=self.version, ) - def generate_full_stubs(self) -> Package | None: + def generate_full_stubs(self) -> TypesAioBotocorePackage | None: """ Generate `types-aiobotocore-full` package. """ package_data = TypesAioBotocoreFullPackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") return None self.logger.info(f"Generating {package_data.pypi_name} {version}") @@ -164,7 +164,7 @@ def generate_full_stubs(self) -> Package | None: ) return package - def generate_custom_stubs(self) -> Package: + def generate_custom_stubs(self) -> TypesAioBotocorePackage: """ Generate `types-aiobotocore-custom` package. """ diff --git a/mypy_boto3_builder/generators/base_generator.py b/mypy_boto3_builder/generators/base_generator.py index ab6d5f20..c3f9aa6d 100644 --- a/mypy_boto3_builder/generators/base_generator.py +++ b/mypy_boto3_builder/generators/base_generator.py @@ -147,20 +147,20 @@ def _get_library_version(self) -> str: """ return self.service_package_data.get_library_version() - def _get_package_version(self, pypi_name: str, version: str) -> str: + def _get_package_build_version(self, pypi_name: str) -> str: if self.config.disable_smart_version: - return version + return self.version pypi_manager = PyPIManager(pypi_name) - if not pypi_manager.has_version(version): - return version + if not pypi_manager.has_version(self.version): + return self.version if self.config.skip_published: - raise AlreadyPublishedError(f"{pypi_name} {version} is already on PyPI") + raise AlreadyPublishedError(f"{pypi_name} {self.version} is already on PyPI") - return pypi_manager.get_next_version(version) + return pypi_manager.get_next_version(self.version) @abstractmethod - def generate_stubs(self) -> Sequence[Package]: + def generate_stubs(self) -> Package | None: """ Generate main stubs. """ @@ -182,7 +182,6 @@ def generate_custom_stubs(self) -> Package | None: """ Generate custom stubs. """ - raise NotImplementedError("Method should be implemented in child class") def _generate_full_stubs_services(self, package: Package) -> None: service_package_writer = PackageWriter( @@ -225,7 +224,7 @@ def generate_product(self, product_type: ProductType) -> None: packages: list[Package | None] = [] match product_type: case ProductType.stubs: - packages.extend(self.generate_stubs()) + packages.append(self.generate_stubs()) case ProductType.stubs_lite: packages.append(self.generate_stubs_lite()) case ProductType.service_stubs: @@ -317,11 +316,9 @@ def generate_service_stubs(self) -> list[ServicePackage]: pypi_name = self.service_package_data.get_service_pypi_name(service_name) try: - version = self._get_package_version(pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info( - f"{progress_str} Skipping {pypi_name} {self.version}, already on PyPI" - ) + version = self._get_package_build_version(pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"{progress_str} Skipping {pypi_name}: {e}") continue self.logger.info(f"{progress_str} Generating {pypi_name} {version}") @@ -346,7 +343,7 @@ def cleanup_temporary_files(self) -> None: self.logger.debug(f"Removing {dir_path}") shutil.rmtree(dir_path) - def _get_wrapper_package_extras(self, package: Package) -> list[PackageExtra]: + def _get_wrapper_package_extras(self, package: Package) -> tuple[PackageExtra, ...]: result: list[PackageExtra] = [] if package.data.pypi_full_name: result.append( @@ -400,4 +397,4 @@ def _get_wrapper_package_extras(self, package: Package) -> list[PackageExtra]: ) for service_name in package.service_names ) - return result + return tuple(result) diff --git a/mypy_boto3_builder/generators/boto3_generator.py b/mypy_boto3_builder/generators/boto3_generator.py index ff7da06c..24592d29 100644 --- a/mypy_boto3_builder/generators/boto3_generator.py +++ b/mypy_boto3_builder/generators/boto3_generator.py @@ -18,13 +18,10 @@ Boto3StubsFullPackageData, Boto3StubsLitePackageData, Boto3StubsPackageData, - MypyBoto3PackageData, ) -from mypy_boto3_builder.parsers.mypy_boto3_package import parse_mypy_boto3_package from mypy_boto3_builder.parsers.parse_wrapper_package import parse_types_boto3_package from mypy_boto3_builder.postprocessors.botocore import BotocorePostprocessor from mypy_boto3_builder.structures.package import Package -from mypy_boto3_builder.structures.packages.mypy_boto3_package import MypyBoto3Package from mypy_boto3_builder.structures.packages.service_package import ServicePackage from mypy_boto3_builder.structures.packages.types_boto3_package import TypesBoto3Package @@ -43,37 +40,21 @@ def _get_postprocessor(self, service_package: ServicePackage) -> BotocorePostpro """ return BotocorePostprocessor(service_package, self.main_service_names) - def _generate_mypy_boto3(self) -> MypyBoto3Package | None: - """ - Generate `mypy-boto3` package. - """ - package_data = MypyBoto3PackageData() - try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") - return None - - self.logger.info(f"Generating {package_data.pypi_name} {version}") - package = parse_mypy_boto3_package(service_names=self.main_service_names, version=version) - self.package_writer.write_package( - package, - template_path=TemplatePath.mypy_boto3, - ) - return package - def _get_static_files_path(self) -> Path: return self._get_or_download_static_files_path( StaticStubsPath.types_boto3, StaticStubsPullURL.types_boto3, ) - def _generate_stubs(self) -> TypesBoto3Package | None: + def generate_stubs(self) -> TypesBoto3Package | None: + """ + Generate `boto3-stubs` package. + """ package_data = Boto3StubsPackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") return None self.logger.info(f"Generating {package_data.pypi_name} {version}") @@ -90,33 +71,15 @@ def _generate_stubs(self) -> TypesBoto3Package | None: ) return package - def generate_stubs(self) -> list[Package]: - """ - Generate `boto3-stubs` package. - """ - result: list[Package] = [] - package: Package | None = None - - package = self._generate_stubs() - if package: - result.append(package) - - if not self.is_package(): - package = self._generate_mypy_boto3() - if package: - result.append(package) - - return result - def generate_stubs_lite(self) -> Package | None: """ Generate `boto3-stubs-lite` package. """ package_data = Boto3StubsLitePackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") return None self.logger.info(f"Generating {package_data.pypi_name} {version}") @@ -174,9 +137,9 @@ def generate_full_stubs(self) -> Package | None: """ package_data = Boto3StubsFullPackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") return None self.logger.info(f"Generating {package_data.pypi_name} {version}") diff --git a/mypy_boto3_builder/generators/mypy_boto3_generator.py b/mypy_boto3_builder/generators/mypy_boto3_generator.py new file mode 100644 index 00000000..fa5036e6 --- /dev/null +++ b/mypy_boto3_builder/generators/mypy_boto3_generator.py @@ -0,0 +1,70 @@ +""" +Generator for mypy-boto3 package. + +Copyright 2025 Vlad Emelianov +""" + +from typing import NoReturn + +from mypy_boto3_builder.constants import TemplatePath +from mypy_boto3_builder.exceptions import AlreadyPublishedError +from mypy_boto3_builder.generators.base_generator import BaseGenerator +from mypy_boto3_builder.package_data import MypyBoto3PackageData +from mypy_boto3_builder.parsers.mypy_boto3_package import parse_mypy_boto3_package +from mypy_boto3_builder.structures.packages.mypy_boto3_package import MypyBoto3Package +from mypy_boto3_builder.structures.packages.service_package import ServicePackage + + +class MypyBoto3Generator(BaseGenerator): + """ + Generator for mypy-boto3 package. + """ + + def generate_stubs(self) -> MypyBoto3Package | None: + """ + Generate main stubs. + """ + package_data = MypyBoto3PackageData() + try: + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") + return None + + self.logger.info(f"Generating {package_data.pypi_name} {version}") + package = parse_mypy_boto3_package(service_names=self.main_service_names, version=version) + self.package_writer.write_package( + package, + template_path=TemplatePath.mypy_boto3, + ) + return package + + def generate_stubs_lite(self) -> NoReturn: + """ + Not implemented. + """ + raise NotImplementedError("Should never be called") + + def generate_full_stubs(self) -> NoReturn: + """ + Not implemented. + """ + raise NotImplementedError("Should never be called") + + def generate_custom_stubs(self) -> NoReturn: + """ + Not implemented. + """ + raise NotImplementedError("Should never be called") + + def generate_docs(self) -> None: + """ + Not implemented. + """ + raise NotImplementedError("Should never be called") + + def _get_postprocessor(self, service_package: ServicePackage) -> NoReturn: + """ + Not implemented. + """ + raise NotImplementedError("Should never be called") diff --git a/mypy_boto3_builder/generators/types_boto3_generator.py b/mypy_boto3_builder/generators/types_boto3_generator.py index d979cdab..2c8c6cc9 100644 --- a/mypy_boto3_builder/generators/types_boto3_generator.py +++ b/mypy_boto3_builder/generators/types_boto3_generator.py @@ -21,8 +21,8 @@ ) from mypy_boto3_builder.parsers.parse_wrapper_package import parse_types_boto3_package from mypy_boto3_builder.postprocessors.botocore import BotocorePostprocessor -from mypy_boto3_builder.structures.package import Package from mypy_boto3_builder.structures.packages.service_package import ServicePackage +from mypy_boto3_builder.structures.packages.types_boto3_package import TypesBoto3Package class TypesBoto3Generator(BaseGenerator): @@ -45,16 +45,16 @@ def _get_static_files_path(self) -> Path: StaticStubsPullURL.types_boto3, ) - def generate_stubs(self) -> list[Package]: + def generate_stubs(self) -> TypesBoto3Package | None: """ Generate `types-boto3` package. """ package_data = TypesBoto3PackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") - return [] + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") + return None self.logger.info(f"Generating {package_data.pypi_name} {version}") package = parse_types_boto3_package( @@ -69,17 +69,17 @@ def generate_stubs(self) -> list[Package]: template_path=TemplatePath.types_boto3, static_files_path=self._get_static_files_path(), ) - return [package] + return package - def generate_stubs_lite(self) -> Package | None: + def generate_stubs_lite(self) -> TypesBoto3Package | None: """ Generate `types-boto3-lite` package. """ package_data = TypesBoto3LitePackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") return None self.logger.info(f"Generating {package_data.pypi_name} {version}") @@ -131,15 +131,15 @@ def generate_docs(self) -> None: version=self.version, ) - def generate_full_stubs(self) -> Package | None: + def generate_full_stubs(self) -> TypesBoto3Package | None: """ Generate `types-boto3-full` package. """ package_data = TypesBoto3FullPackageData() try: - version = self._get_package_version(package_data.pypi_name, self.version) - except AlreadyPublishedError: - self.logger.info(f"Skipping {package_data.pypi_name} {self.version}, already on PyPI") + version = self._get_package_build_version(package_data.pypi_name) + except AlreadyPublishedError as e: + self.logger.info(f"Skipping {package_data.pypi_name}: {e}") return None self.logger.info(f"Generating {package_data.pypi_name} {version}") @@ -168,7 +168,7 @@ def generate_full_stubs(self) -> Package | None: ) return package - def generate_custom_stubs(self) -> Package: + def generate_custom_stubs(self) -> TypesBoto3Package: """ Generate `types-boto3-custom` package. """ diff --git a/mypy_boto3_builder/main.py b/mypy_boto3_builder/main.py index b34e8e01..85e4e4ae 100644 --- a/mypy_boto3_builder/main.py +++ b/mypy_boto3_builder/main.py @@ -22,6 +22,7 @@ from mypy_boto3_builder.generators.aiobotocore_generator import AioBotocoreGenerator from mypy_boto3_builder.generators.base_generator import BaseGenerator from mypy_boto3_builder.generators.boto3_generator import Boto3Generator +from mypy_boto3_builder.generators.mypy_boto3_generator import MypyBoto3Generator from mypy_boto3_builder.generators.types_boto3_generator import TypesBoto3Generator from mypy_boto3_builder.jinja_manager import JinjaManager from mypy_boto3_builder.logger import get_logger @@ -98,6 +99,8 @@ def get_generator(product: Product, kwargs: GeneratorKwargs) -> BaseGenerator: match library: case ProductLibrary.boto3: return TypesBoto3Generator(**kwargs) + case ProductLibrary.mypy_boto3: + return MypyBoto3Generator(**kwargs) case ProductLibrary.boto3_legacy: return Boto3Generator(**kwargs) case ProductLibrary.aiobotocore: