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"