diff --git a/mypy_boto3_builder/__init__.py b/mypy_boto3_builder/__init__.py index 4695c378..d78addea 100644 --- a/mypy_boto3_builder/__init__.py +++ b/mypy_boto3_builder/__init__.py @@ -1,3 +1,7 @@ """ -Builder for `mypy_boto3`. +Main library entrypoint. """ + +from mypy_boto3_builder.logger import disable_logger + +disable_logger() diff --git a/mypy_boto3_builder/__main__.py b/mypy_boto3_builder/__main__.py index 85891190..cd0965ae 100644 --- a/mypy_boto3_builder/__main__.py +++ b/mypy_boto3_builder/__main__.py @@ -4,7 +4,9 @@ Copyright 2024 Vlad Emelianov """ +from mypy_boto3_builder.logger import enable_logger from mypy_boto3_builder.main import main if __name__ == "__main__": + enable_logger() main() diff --git a/mypy_boto3_builder/generators/aioboto3_generator.py b/mypy_boto3_builder/generators/aioboto3_generator.py index f0b803c0..202f3e99 100644 --- a/mypy_boto3_builder/generators/aioboto3_generator.py +++ b/mypy_boto3_builder/generators/aioboto3_generator.py @@ -63,11 +63,10 @@ def generate_stubs(self) -> TypesAioBoto3Package | None: package_data = TypesAioBoto3PackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_types_aioboto3_package( service_names=self.main_service_names, package_data=package_data, @@ -89,11 +88,10 @@ def generate_stubs_lite(self) -> TypesAioBoto3Package | None: package_data = TypesAioBoto3LitePackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_types_aioboto3_package( service_names=self.main_service_names, package_data=package_data, @@ -116,7 +114,10 @@ def generate_docs(self) -> None: package_data = TypesAioBoto3PackageData() total_str = f"{len(self.service_names)}" - self.logger.info(f"Generating {package_data.pypi_name} module docs") + self.logger.info( + f"Generating {package_data.pypi_name} module docs", + tags=(package_data.pypi_name,), + ) package = parse_types_aioboto3_package( service_names=self.service_names, package_data=package_data, @@ -157,7 +158,7 @@ def generate_custom_stubs(self) -> TypesAioBoto3Package: package_data = TypesAioBoto3CustomPackageData() aiobotocore_package_data = TypesAioBotocorePackageData() - self.logger.info(f"Generating {package_data.pypi_name} {self.version}") + self._log_generate(package_data.pypi_name, self.version) package = parse_types_aioboto3_package( service_names=self.service_names, package_data=package_data, diff --git a/mypy_boto3_builder/generators/aiobotocore_generator.py b/mypy_boto3_builder/generators/aiobotocore_generator.py index 86f54aa7..618e26ea 100644 --- a/mypy_boto3_builder/generators/aiobotocore_generator.py +++ b/mypy_boto3_builder/generators/aiobotocore_generator.py @@ -52,12 +52,10 @@ def generate_stubs(self) -> TypesAioBotocorePackage | None: package_data = TypesAioBotocorePackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") - + self._log_generate(package_data.pypi_name, version) package = parse_types_aiobotocore_package( service_names=self.main_service_names, package_data=package_data, @@ -79,11 +77,10 @@ def generate_stubs_lite(self) -> TypesAioBotocorePackage | None: package_data = TypesAioBotocoreLitePackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, self.version) package = parse_types_aiobotocore_package( service_names=self.main_service_names, package_data=package_data, @@ -106,7 +103,10 @@ def generate_docs(self) -> None: package_data = TypesAioBotocorePackageData() total_str = f"{len(self.service_names)}" - self.logger.info(f"Generating {package_data.pypi_name} module docs") + self.logger.info( + f"Generating {package_data.pypi_name} module docs", + tags=(package_data.pypi_name,), + ) package = parse_types_aiobotocore_package( service_names=self.service_names, package_data=package_data, @@ -135,11 +135,10 @@ def generate_full_stubs(self) -> TypesAioBotocorePackage | None: package_data = TypesAioBotocoreFullPackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_types_aiobotocore_package( service_names=self.service_names, package_data=package_data, @@ -170,7 +169,7 @@ def generate_custom_stubs(self) -> TypesAioBotocorePackage: """ package_data = TypesAioBotocoreCustomPackageData() - self.logger.info(f"Generating {package_data.pypi_name} {self.version}") + self._log_generate(package_data.pypi_name, self.version) package = parse_types_aiobotocore_package( service_names=self.service_names, package_data=package_data, diff --git a/mypy_boto3_builder/generators/base_generator.py b/mypy_boto3_builder/generators/base_generator.py index c3f9aa6d..1db9e00b 100644 --- a/mypy_boto3_builder/generators/base_generator.py +++ b/mypy_boto3_builder/generators/base_generator.py @@ -155,6 +155,10 @@ def _get_package_build_version(self, pypi_name: str) -> str: return self.version if self.config.skip_published: + self.logger.info( + f"Skipping {pypi_name}: {self.version} is already on PyPI", + tags=(pypi_name,), + ) raise AlreadyPublishedError(f"{pypi_name} {self.version} is already on PyPI") return pypi_manager.get_next_version(self.version) @@ -196,6 +200,7 @@ def _generate_full_stubs_services(self, package: Package) -> None: self.logger.info( f"{progress_str} Generating {service_name.boto3_name} service directory", + tags=(service_name.boto3_name,), ) service_package = self._parse_service_package( service_name=service_name, @@ -251,7 +256,9 @@ def _parse_service_package( version: str, package_data: BasePackageData, ) -> ServicePackage: - self.logger.debug(f"Parsing {service_name.boto3_name}") + self.logger.debug( + f"Parsing {service_name.boto3_name} botocore service", tags=(service_name.boto3_name,) + ) parser = ServicePackageParser(service_name, package_data, version) service_package = parser.parse() @@ -277,7 +284,10 @@ def _process_service( ) service_package.mark_safe_typed_dicts() - self.logger.debug(f"Writing {service_name.boto3_name}") + self.logger.debug( + f"Writing {service_name.boto3_name} service package", + tags=(service_name.boto3_name,), + ) self.package_writer.write_service_package( service_package, templates_path=templates_path, @@ -297,7 +307,10 @@ def _process_service_docs( package_data=package_data, ) - self.logger.debug(f"Writing {service_name.boto3_name}") + self.logger.debug( + f"Writing {service_name.boto3_name} service docs", + tags=(service_name.boto3_name,), + ) self.package_writer.write_service_docs( service_package, templates_path=templates_path, @@ -317,11 +330,10 @@ def generate_service_stubs(self) -> list[ServicePackage]: pypi_name = self.service_package_data.get_service_pypi_name(service_name) try: version = self._get_package_build_version(pypi_name) - except AlreadyPublishedError as e: - self.logger.info(f"{progress_str} Skipping {pypi_name}: {e}") + except AlreadyPublishedError: continue - self.logger.info(f"{progress_str} Generating {pypi_name} {version}") + self.logger.info(f"{progress_str} Generating {pypi_name} {version}", tags=(pypi_name,)) package = self._process_service( service_name=service_name, version=version, @@ -398,3 +410,10 @@ def _get_wrapper_package_extras(self, package: Package) -> tuple[PackageExtra, . for service_name in package.service_names ) return tuple(result) + + def _log_generate(self, pypi_name: str, version: str | None = None) -> None: + version_str = version or self.version + self.logger.info( + f"Generating {pypi_name} {version_str}", + tags=(pypi_name,), + ) diff --git a/mypy_boto3_builder/generators/boto3_generator.py b/mypy_boto3_builder/generators/boto3_generator.py index 24592d29..b8545216 100644 --- a/mypy_boto3_builder/generators/boto3_generator.py +++ b/mypy_boto3_builder/generators/boto3_generator.py @@ -53,11 +53,10 @@ def generate_stubs(self) -> TypesBoto3Package | None: package_data = Boto3StubsPackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_types_boto3_package( service_names=self.main_service_names, package_data=package_data, @@ -78,11 +77,10 @@ def generate_stubs_lite(self) -> Package | None: package_data = Boto3StubsLitePackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_types_boto3_package( service_names=self.main_service_names, package_data=package_data, @@ -108,7 +106,10 @@ def generate_docs(self) -> None: package_data = Boto3StubsPackageData() total_str = f"{len(self.service_names)}" - self.logger.info(f"Generating {package_data.name} module docs") + self.logger.info( + f"Generating {package_data.pypi_name} package docs", + tags=(package_data.pypi_name,), + ) package = parse_types_boto3_package( service_names=self.service_names, package_data=package_data, @@ -138,11 +139,10 @@ def generate_full_stubs(self) -> Package | None: package_data = Boto3StubsFullPackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_types_boto3_package( service_names=self.service_names, package_data=package_data, @@ -174,7 +174,7 @@ def generate_custom_stubs(self) -> Package: """ package_data = Boto3StubsCustomPackageData() - self.logger.info(f"Generating {package_data.pypi_name} {self.version}") + self._log_generate(package_data.pypi_name, self.version) package = parse_types_boto3_package( service_names=self.service_names, package_data=package_data, diff --git a/mypy_boto3_builder/generators/mypy_boto3_generator.py b/mypy_boto3_builder/generators/mypy_boto3_generator.py index fa5036e6..d71a2193 100644 --- a/mypy_boto3_builder/generators/mypy_boto3_generator.py +++ b/mypy_boto3_builder/generators/mypy_boto3_generator.py @@ -27,11 +27,10 @@ def generate_stubs(self) -> MypyBoto3Package | None: 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_mypy_boto3_package(service_names=self.main_service_names, version=version) self.package_writer.write_package( package, diff --git a/mypy_boto3_builder/generators/types_boto3_generator.py b/mypy_boto3_builder/generators/types_boto3_generator.py index 2c8c6cc9..78ea5ef7 100644 --- a/mypy_boto3_builder/generators/types_boto3_generator.py +++ b/mypy_boto3_builder/generators/types_boto3_generator.py @@ -52,11 +52,10 @@ def generate_stubs(self) -> TypesBoto3Package | None: package_data = TypesBoto3PackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_types_boto3_package( service_names=self.main_service_names, package_data=package_data, @@ -78,11 +77,10 @@ def generate_stubs_lite(self) -> TypesBoto3Package | None: package_data = TypesBoto3LitePackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_types_boto3_package( service_names=self.main_service_names, package_data=package_data, @@ -108,7 +106,10 @@ def generate_docs(self) -> None: package_data = TypesBoto3PackageData() total_str = f"{len(self.service_names)}" - self.logger.info(f"Generating {package_data.name} module docs") + self.logger.info( + f"Generating {package_data.pypi_name} package docs", + tags=(package_data.pypi_name,), + ) package = parse_types_boto3_package( service_names=self.service_names, package_data=package_data, @@ -138,11 +139,10 @@ def generate_full_stubs(self) -> TypesBoto3Package | None: package_data = TypesBoto3FullPackageData() 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}") + except AlreadyPublishedError: return None - self.logger.info(f"Generating {package_data.pypi_name} {version}") + self._log_generate(package_data.pypi_name, version) package = parse_types_boto3_package( service_names=self.service_names, package_data=package_data, @@ -174,7 +174,7 @@ def generate_custom_stubs(self) -> TypesBoto3Package: """ package_data = TypesBoto3CustomPackageData() - self.logger.info(f"Generating {package_data.pypi_name} {self.version}") + self._log_generate(package_data.pypi_name, self.version) package = parse_types_boto3_package( service_names=self.service_names, package_data=package_data, diff --git a/mypy_boto3_builder/logger.py b/mypy_boto3_builder/logger.py index 0cdb21df..b53e1892 100644 --- a/mypy_boto3_builder/logger.py +++ b/mypy_boto3_builder/logger.py @@ -4,37 +4,58 @@ Copyright 2024 Vlad Emelianov """ +from __future__ import annotations + import logging +import sys + +import loguru from mypy_boto3_builder.constants import LOGGER_NAME -__all__ = ("get_logger",) +__all__ = ("get_logger", "setup_logger") + + +def _formatter(record: loguru.Record) -> str: + tags = record["extra"].get("tags") or () + message = record["message"] + for tag in tags: + message = message.replace(f"{tag}", f"{tag}") + return ( + "{time:YYYY-MM-DD HH:mm:ss.SSS} | " + "{level: <8} | " + f"{LOGGER_NAME} - " + f"{message}" + "\n" + ) + + +def setup_logger(level: int) -> None: + """ + Set up logger. + """ + level_name = logging.getLevelName(level) + loguru.logger.configure( + handlers=[{"sink": sys.stderr, "level": level_name, "format": _formatter}] + ) -def get_logger(level: int | None = None, name: str = LOGGER_NAME) -> logging.Logger: +def get_logger() -> loguru.Logger: """ Get Logger instance. + """ + return loguru.logger + + +def disable_logger() -> None: + """ + Disable logger. + """ + loguru.logger.disable(LOGGER_NAME) + - Arguments: - level -- Log level. - - Returns: - Overriden Logger. - """ - logger = logging.getLogger(name) - if not logger.handlers: - stream_handler = logging.StreamHandler() - formatter = logging.Formatter( - "%(asctime)s %(name)s: %(levelname)-7s %(message)s", - datefmt="%H:%M:%S", - ) - stream_handler.setFormatter(formatter) - stream_handler.setLevel(level or logging.NOTSET) - logger.addHandler(stream_handler) - - if level is not None: - logger.setLevel(level) - for handler in logger.handlers: - handler.setLevel(level) - - return logger +def enable_logger() -> None: + """ + Enable logger. + """ + loguru.logger.enable(LOGGER_NAME) diff --git a/mypy_boto3_builder/main.py b/mypy_boto3_builder/main.py index 85e4e4ae..f3c096c7 100644 --- a/mypy_boto3_builder/main.py +++ b/mypy_boto3_builder/main.py @@ -25,7 +25,7 @@ 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 +from mypy_boto3_builder.logger import get_logger, setup_logger from mypy_boto3_builder.service_name import ServiceName from mypy_boto3_builder.type_defs import GeneratorKwargs from mypy_boto3_builder.utils.boto3_utils import get_available_service_names @@ -179,7 +179,8 @@ def run(args: CLINamespace) -> None: def _run_builder(args: CLINamespace) -> None: - logger = get_logger(level=args.log_level) + setup_logger(level=args.log_level) + logger = get_logger() available_service_names = get_available_service_names() initialize_jinja_manager() args.output_path.mkdir(exist_ok=True, parents=True) @@ -197,7 +198,7 @@ def _run_builder(args: CLINamespace) -> None: main_service_names = service_names if args.partial_overload else available_service_names for product in args.products: - logger.info(f"Generating {product} product") + logger.info(f"Generating {product} product", tags=(product,)) generate_product(product, args, service_names, main_service_names) logger.debug("Done!") diff --git a/mypy_boto3_builder/parsers/client.py b/mypy_boto3_builder/parsers/client.py index d8d46b64..168e74b5 100644 --- a/mypy_boto3_builder/parsers/client.py +++ b/mypy_boto3_builder/parsers/client.py @@ -32,10 +32,11 @@ def parse_client(service_name: ServiceName, shape_parser: ShapeParser) -> Client Client structure. """ logger = get_logger() - logger.debug("Parsing Client") + name = service_name.get_client_name() + logger.debug(f"Parsing {name}", tags=(name,)) result = Client( - name=service_name.get_client_name(), + name=name, service_name=service_name, ) diff --git a/mypy_boto3_builder/parsers/service_package_parser.py b/mypy_boto3_builder/parsers/service_package_parser.py index c14d2a74..a3a2bc96 100644 --- a/mypy_boto3_builder/parsers/service_package_parser.py +++ b/mypy_boto3_builder/parsers/service_package_parser.py @@ -97,7 +97,7 @@ def _parse_service_package(self) -> ServicePackage: def _parse_waiters(self) -> list[Waiter]: waiters: list[Waiter] = [] for waiter_name in self.shape_parser.get_waiter_names(): - self._logger.debug(f"Parsing Waiter {waiter_name}") + self._logger.debug(f"Parsing Waiter {waiter_name}", tags=(waiter_name,)) waiter = Waiter( name=f"{waiter_name}Waiter", waiter_name=waiter_name, @@ -113,7 +113,7 @@ def _parse_waiters(self) -> list[Waiter]: def _parse_paginators(self) -> list[Paginator]: result: list[Paginator] = [] for paginator_name in self.shape_parser.get_paginator_names(): - self._logger.debug(f"Parsing Paginator {paginator_name}") + self._logger.debug(f"Parsing Paginator {paginator_name}", tags=(paginator_name,)) operation_name = xform_name(paginator_name) paginator_record = Paginator( name=f"{paginator_name}Paginator", diff --git a/mypy_boto3_builder/parsers/service_resource_parser.py b/mypy_boto3_builder/parsers/service_resource_parser.py index 805a2a18..6e402b40 100644 --- a/mypy_boto3_builder/parsers/service_resource_parser.py +++ b/mypy_boto3_builder/parsers/service_resource_parser.py @@ -64,26 +64,24 @@ def parse_service_resource(self) -> ServiceResource | None: if not self.shape_parser.has_service_resource(): return None - self._logger.debug("Parsing ServiceResource") + name = self.service_name.get_service_resource_name() + self._logger.debug(f"Parsing {name}", tags=(name,)) result = ServiceResource( - name=self.service_name.get_service_resource_name(), + name=name, service_name=self.service_name, ) meta_attribute = self._get_meta_attribute(result.resource_meta_class.name) - self._logger.debug("Parsing ServiceResource methods") + self._logger.debug(f"Parsing {name} methods", tags=(name,)) method_map = self._parse_method_map() result.methods.extend(list(method_map.values())) - self._logger.debug("Parsing ServiceResource attributes") + self._logger.debug(f"Parsing {name} attributes and collections", tags=(name,)) collections = self._parse_collections() result.attributes.append(meta_attribute) result.attributes.extend(self._parse_attributes(collections)) - - self._logger.debug("Parsing ServiceResource collections") result.collections.extend(collections) - self._logger.debug("Parsing ServiceResource sub resources") sub_resources = self._parse_sub_resources(meta_attribute) result.sub_resources.extend(sub_resources) @@ -92,7 +90,10 @@ def parse_service_resource(self) -> ServiceResource | None: def _parse_sub_resources(self, meta_attribute: Attribute) -> list[ResourceRecord]: result: list[ResourceRecord] = [] for sub_resource_name in self.shape_parser.get_subresource_names(): - self._logger.debug(f"Parsing {sub_resource_name} sub resource") + self._logger.debug( + f"Parsing {sub_resource_name} sub resource", + tags=(sub_resource_name,), + ) resource_parser = ResourceParser( service_name=self.service_name, name=sub_resource_name, diff --git a/mypy_boto3_builder/parsers/shape_parser.py b/mypy_boto3_builder/parsers/shape_parser.py index e3592185..d3027f61 100644 --- a/mypy_boto3_builder/parsers/shape_parser.py +++ b/mypy_boto3_builder/parsers/shape_parser.py @@ -478,6 +478,7 @@ def _parse_shape_structure( if found_typed_dict and not typed_dict.is_same(found_typed_dict): self.logger.debug( f"Renaming conflicting {typed_dict.name} to {resource_typed_dict_name}", + tags=(typed_dict.name, resource_typed_dict_name), ) typed_dict.name = resource_typed_dict_name typed_dict_map[typed_dict.name] = typed_dict @@ -488,9 +489,10 @@ def _mark_typed_dict_as_total(self, typed_dict: TypeTypedDict) -> None: if is_required(self.service_name, typed_dict.name, attribute.name): attribute.mark_as_required() else: + attribute_rendered = attribute.get_type_annotation().render() self.logger.debug( - f"Leaving output {typed_dict.name}.{attribute.name}" - f" as {attribute.get_type_annotation().render()}", + f"Leaving output {typed_dict.name}.{attribute.name} as {attribute_rendered}", + tags=(typed_dict.name, attribute.name, attribute_rendered), ) def _add_response_metadata(self, typed_dict: TypeTypedDict) -> None: @@ -885,7 +887,8 @@ def get_service_resource_method_map(self) -> dict[str, Method]: if sub_resource_name not in existing_sub_resource_names: self.logger.debug( f"Skipping {sub_resource_name} sub resource" - " because it is not present in ServiceResource.has" + " because it is not present in ServiceResource.has", + tags=(sub_resource_name,), ) continue sub_resource_shape = self._get_resource_shape(sub_resource_name) @@ -1227,7 +1230,10 @@ def _get_non_clashing_typed_dict_name(self, typed_dict: TypeTypedDict, postfix: if clashing_typed_dict.is_same(temp_typed_dict): return new_typed_dict_name - self.logger.debug(f"Clashing typed dict name found: {new_typed_dict_name}") + self.logger.debug( + f"Clashing typed dict name found: {new_typed_dict_name}", + tags=(new_typed_dict_name,), + ) return self._get_non_clashing_typed_dict_name(typed_dict, "Extra" + postfix) def fix_typed_dict_names(self) -> None: @@ -1256,6 +1262,7 @@ def fix_typed_dict_names(self) -> None: self.logger.debug( "Fixing output TypedDict name clash" f" {old_typed_dict_name} -> {new_typed_dict_name}", + tags=(old_typed_dict_name, new_typed_dict_name), ) self._output_typed_dict_map.rename(output_typed_dict, new_typed_dict_name) @@ -1288,6 +1295,7 @@ def fix_typed_dict_names(self) -> None: self.logger.debug( "Fixing response TypedDict name clash" f" {old_typed_dict_name} -> {new_typed_dict_name}", + tags=(old_typed_dict_name, new_typed_dict_name), ) self._response_typed_dict_map.rename(response_typed_dict, new_typed_dict_name) @@ -1321,8 +1329,10 @@ def convert_input_arguments_to_unions(self, methods: Iterable[Method]) -> None: for parent in sorted(parents): if is_union(parent) and parent.name == union_name: continue + parent_rendered = parent.render() self.logger.debug( - f"Adding output shape to {parent.render()} type:" + f"Adding output shape to {parent_rendered} type:" f" {input_typed_dict.name} | {output_typed_dict.name}", + tags=(parent_rendered,), ) parent.replace_child(input_typed_dict, union_type_annotation) diff --git a/mypy_boto3_builder/writers/package_writer.py b/mypy_boto3_builder/writers/package_writer.py index 8b955a30..8d5d939f 100644 --- a/mypy_boto3_builder/writers/package_writer.py +++ b/mypy_boto3_builder/writers/package_writer.py @@ -179,7 +179,7 @@ def _write_template(self, path: Path, content: str) -> None: path.parent.mkdir(exist_ok=True, parents=True) path.write_text(content) - self.logger.debug(f"Rendered {print_path(path)}") + self.logger.debug(f"Rendered {print_path(path)}", tags=(print_path(path),)) def _render_docs_templates( self, @@ -216,7 +216,10 @@ def write_package( include_template_names -- Render only templates with these names exclude_template_names -- Do not render templates with these names """ - self.logger.debug(f"Writing {package.data.pypi_name} to {print_path(self.output_path)}") + self.logger.debug( + f"Writing {package.data.pypi_name} to {print_path(self.output_path)}", + tags=(package.data.pypi_name, print_path(self.output_path)), + ) template_renders: list[TemplateRender] = [ *self._get_setup_template_paths(package, template_path), *self._get_package_template_renders(package, template_path), diff --git a/pyproject.toml b/pyproject.toml index f6289d5f..ea7b9f9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ classifiers = [ ] dependencies = [ "jinja2", + "loguru", "mdformat", "packaging", "boto3", diff --git a/scripts/release.py b/scripts/release.py index 013b7113..a934feaf 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -6,6 +6,7 @@ # "setuptools", # "twine", # "wheel", +# "loguru", # ] # /// """ @@ -31,6 +32,7 @@ from typing import TYPE_CHECKING from unittest.mock import patch +from loguru import logger from requests.exceptions import ConnectionError as RequestsConnectionError from requests.exceptions import HTTPError from twine.commands.upload import upload @@ -64,29 +66,6 @@ class Config: """ max_retries: int = 10 - logger: logging.Logger - - -def setup_logging(level: int) -> logging.Logger: - """ - Get Logger instance. - - Returns: - Overriden Logger. - """ - logging.getLogger("twine").disabled = True - logging.getLogger("twine.commands.upload").disabled = True - - logger = logging.getLogger(LOGGER_NAME) - stream_handler = logging.StreamHandler() - formatter = logging.Formatter( - "%(asctime)s %(name)s %(levelname)-7s %(message)s", datefmt="%H:%M:%S" - ) - stream_handler.setFormatter(formatter) - stream_handler.setLevel(level) - logger.addHandler(stream_handler) - logger.setLevel(level) - return logger @dataclass @@ -141,7 +120,6 @@ def check_call(cmd: Sequence[str], *, print_error: bool = True) -> str: return subprocess.check_output(cmd, stderr=subprocess.STDOUT, encoding="utf-8") except subprocess.CalledProcessError as e: if print_error: - logger = Config.logger for line in e.output.splitlines(): logger.warning(line) raise @@ -182,7 +160,6 @@ def build(path: Path, max_retries: int = 10) -> Path: """ Build package. """ - logger = Config.logger attempt = 1 last_error = Exception("Unknown error") while attempt <= max_retries: @@ -211,7 +188,6 @@ def publish(paths: Sequence[Path]) -> Sequence[Path]: """ Publish packages from dist directory to PyPI. """ - logger = Config.logger for path in paths: attempt = 1 while attempt <= Config.max_retries: @@ -319,7 +295,6 @@ def publish_directories(args: CLINamespace) -> None: """ Build and publish packages from directories. """ - logger = Config.logger paths = [i for i in args.path.iterdir() if i.is_dir()] paths.sort(key=lambda x: x.name) if args.filter: @@ -404,7 +379,6 @@ def main() -> None: """ args = parse_args() Config.max_retries = args.retries - Config.logger = setup_logging(logging.DEBUG) publish_directories(args) publish_packages(args) diff --git a/tests/test_logger.py b/tests/test_logger.py deleted file mode 100644 index c9ce126b..00000000 --- a/tests/test_logger.py +++ /dev/null @@ -1,22 +0,0 @@ -from unittest.mock import MagicMock, patch - -from mypy_boto3_builder.logger import get_logger - - -class TestLogger: - @patch("mypy_boto3_builder.logger.logging") - def test_get_logger(self, logging_mock: MagicMock) -> None: - logger_mock = MagicMock() - logging_mock.getLogger.return_value = logger_mock - logger_mock.handlers = [] - result = get_logger(logging_mock.DEBUG) - assert result == logger_mock - logging_mock.StreamHandler.assert_called_with() - logger_mock.setLevel.assert_called_with(logging_mock.DEBUG) - - logging_mock.reset_mock() - handler_mock = MagicMock() - logger_mock.handlers = [handler_mock] - result = get_logger() - logging_mock.StreamHandler.assert_not_called() - logger_mock.setLevel.assert_not_called() diff --git a/uv.lock b/uv.lock index 26faa722..057d15e9 100644 --- a/uv.lock +++ b/uv.lock @@ -445,6 +445,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd", size = 39085 }, ] +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 }, +] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -597,6 +610,7 @@ dependencies = [ { name = "boto3" }, { name = "botocore" }, { name = "jinja2" }, + { name = "loguru" }, { name = "mdformat" }, { name = "packaging" }, { name = "prompt-toolkit" }, @@ -648,6 +662,7 @@ requires-dist = [ { name = "boto3-stubs", marker = "extra == 'check'" }, { name = "botocore" }, { name = "jinja2" }, + { name = "loguru" }, { name = "mdformat" }, { name = "packaging" }, { name = "prompt-toolkit" }, @@ -1267,6 +1282,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494 }, ] +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 }, +] + [[package]] name = "yarl" version = "1.18.3"