diff --git a/aws_lambda_powertools/tracing/extensions.py b/aws_lambda_powertools/tracing/extensions.py index 6c641238c98..3eafae6a651 100644 --- a/aws_lambda_powertools/tracing/extensions.py +++ b/aws_lambda_powertools/tracing/extensions.py @@ -3,6 +3,25 @@ def aiohttp_trace_config(): It expects you to have aiohttp as a dependency. + Example + ------- + + ```python + import asyncio + import aiohttp + + from aws_lambda_powertools import Tracer + from aws_lambda_powertools.tracing import aiohttp_trace_config + + tracer = Tracer() + + async def aiohttp_task(): + async with aiohttp.ClientSession(trace_configs=[aiohttp_trace_config()]) as session: + async with session.get("https://httpbin.org/json") as resp: + resp = await resp.json() + return resp + ``` + Returns ------- TraceConfig diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 5709b1956c2..0eced4c04c9 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -5,7 +5,7 @@ import logging import numbers import os -from typing import Any, Awaitable, Callable, Dict, Optional, Sequence, TypeVar, Union, cast, overload +from typing import Any, Callable, Dict, Optional, Sequence, TypeVar, Union, cast, overload from ..shared import constants from ..shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice @@ -16,126 +16,54 @@ logger = logging.getLogger(__name__) aws_xray_sdk = LazyLoader(constants.XRAY_SDK_MODULE, globals(), constants.XRAY_SDK_MODULE) -aws_xray_sdk.core = LazyLoader(constants.XRAY_SDK_CORE_MODULE, globals(), constants.XRAY_SDK_CORE_MODULE) +aws_xray_sdk.core = LazyLoader(constants.XRAY_SDK_CORE_MODULE, globals(), constants.XRAY_SDK_CORE_MODULE) # type: ignore # noqa: E501 AnyCallableT = TypeVar("AnyCallableT", bound=Callable[..., Any]) # noqa: VNE001 -AnyAwaitableT = TypeVar("AnyAwaitableT", bound=Awaitable) class Tracer: - """Tracer using AWS-XRay to provide decorators with known defaults for Lambda functions + """Tracer provides opinionated decorators to trace Lambda functions with AWS X-Ray - When running locally, it detects whether it's running via SAM CLI, - and if it is it returns dummy segments/subsegments instead. + By default, it patches all [available libraries supported by X-Ray SDK](https://amzn.to/36Jkkyo). - By default, it patches all available libraries supported by X-Ray SDK. Patching is - automatically disabled when running locally via SAM CLI or by any other means. \n - Ref: https://docs.aws.amazon.com/xray-sdk-for-python/latest/reference/thirdparty.html + When running locally, it disables itself whether it's running via SAM CLI or Chalice. - Tracer keeps a copy of its configuration as it can be instantiated more than once. This - is useful when you are using your own middlewares and want to utilize an existing Tracer. - Make sure to set `auto_patch=False` in subsequent Tracer instances to avoid double patching. + !!! note "Reusing Tracer across the codebase" + Tracer keeps a copy of its configuration after the first initialization and reuses it across instances. + + Additional instances can override configuration via the constructor. Environment variables --------------------- - POWERTOOLS_TRACE_DISABLED : str - disable tracer (e.g. `"true", "True", "TRUE"`) - POWERTOOLS_SERVICE_NAME : str - service name - POWERTOOLS_TRACER_CAPTURE_RESPONSE : str - disable auto-capture response as metadata (e.g. `"true", "True", "TRUE"`) - POWERTOOLS_TRACER_CAPTURE_ERROR : str - disable auto-capture error as metadata (e.g. `"true", "True", "TRUE"`) - - Parameters - ---------- - service: str - Service name that will be appended in all tracing metadata - auto_patch: bool - Patch existing imported modules during initialization, by default True - disabled: bool - Flag to explicitly disable tracing, useful when running/testing locally - `Env POWERTOOLS_TRACE_DISABLED="true"` - patch_modules: Optional[Sequence[str]] - Tuple of modules supported by tracing provider to patch, by default all modules are patched - provider: BaseProvider - Tracing provider, by default it is aws_xray_sdk.core.xray_recorder - - Returns - ------- - Tracer - Tracer instance with imported modules patched + + * `POWERTOOLS_TRACE_DISABLED`: disable tracer, default `true` + * `POWERTOOLS_SERVICE_NAME`: service name, default `payment` + * `POWERTOOLS_TRACER_CAPTURE_RESPONSE`: disable auto-capture response as metadata, default `true` + * `POWERTOOLS_TRACER_CAPTURE_ERROR`: disable auto-capture error as metadata, default `true` Example ------- - **A Lambda function using Tracer** - from aws_lambda_powertools import Tracer - tracer = Tracer(service="greeting") + **Reuse an existing instance of Tracer across the codebase** - @tracer.capture_method - def greeting(name: str) -> Dict: - return { - "name": name - } + ```python + # lambda_handler.py + from aws_lambda_powertools import Tracer - @tracer.capture_lambda_handler - def handler(event: dict, context: Any) -> Dict: - print("Received event from Lambda...") - response = greeting(name="Heitor") - return response + tracer = Tracer(service="booking") - **Booking Lambda function using Tracer that adds additional annotation/metadata** - - from aws_lambda_powertools import Tracer - tracer = Tracer(service="booking") + @tracer.capture_lambda_handler + def handler(event: dict, context: Any) -> Dict: ... - @tracer.capture_method - def confirm_booking(booking_id: str) -> Dict: - resp = add_confirmation(booking_id) + # utils.py + from aws_lambda_powertools import Tracer - tracer.put_annotation("BookingConfirmation", resp["requestId"]) - tracer.put_metadata("Booking confirmation", resp) + tracer = Tracer(service="booking") + ``` - return resp + ## Limitations - @tracer.capture_lambda_handler - def handler(event: dict, context: Any) -> Dict: - print("Received event from Lambda...") - booking_id = event.get("booking_id") - response = confirm_booking(booking_id=booking_id) - return response - - **A Lambda function using service name via POWERTOOLS_SERVICE_NAME** - - export POWERTOOLS_SERVICE_NAME="booking" - from aws_lambda_powertools import Tracer - tracer = Tracer() - - @tracer.capture_lambda_handler - def handler(event: dict, context: Any) -> Dict: - print("Received event from Lambda...") - response = greeting(name="Lessa") - return response - - **Reuse an existing instance of Tracer anywhere in the code** - - # lambda_handler.py - from aws_lambda_powertools import Tracer - tracer = Tracer() - - @tracer.capture_lambda_handler - def handler(event: dict, context: Any) -> Dict: - ... - - # utils.py - from aws_lambda_powertools import Tracer - tracer = Tracer() - ... - - Limitations - ----------- - * Async handler not supported + * Async Lambda handler not supported """ _default_config: Dict[str, Any] = { @@ -155,6 +83,21 @@ def __init__( patch_modules: Optional[Sequence[str]] = None, provider: Optional[BaseProvider] = None, ): + """Tracer constructor + + Parameters + ---------- + service: str + Service name to be appended across tracing metadata + auto_patch: bool + Patch existing imported modules during initialization, by default True + disabled: bool + Flag to explicitly disable tracing, useful when running/testing locally + patch_modules: Optional[Sequence[str]] + List of supported modules by the tracing provider, by default all modules are patched + provider: BaseProvider + Tracing provider, by default `aws_xray_sdk.core.xray_recorder` + """ self.__build_config( service=service, disabled=disabled, auto_patch=auto_patch, patch_modules=patch_modules, provider=provider ) @@ -186,10 +129,27 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]): Example ------- - Custom annotation for a pseudo service named payment - tracer = Tracer(service="payment") - tracer.put_annotation("PaymentStatus", "CONFIRMED") + ```python + from aws_lambda_powertools import Tracer + + tracer = Tracer(service="booking") + + @tracer.capture_method + def confirm_booking(booking_id: str) -> Dict: + resp = add_confirmation(booking_id) + tracer.put_annotation("BookingConfirmation", resp["requestId"]) + + return resp + + @tracer.capture_lambda_handler + def handler(event: dict, context: Any) -> Dict: + booking_id = event.get("booking_id", "") + tracer.put_annotation("BookingId", booking_id) + response = confirm_booking(booking_id=booking_id) + + return response + ``` """ if self.disabled: logger.debug("Tracing has been disabled, aborting put_annotation") @@ -207,16 +167,31 @@ def put_metadata(self, key: str, value: Any, namespace: Optional[str] = None): Metadata key value : any Value for metadata - namespace : str, optional - Namespace that metadata will lie under, by default None + namespace : Optional[str] + Namespace container to add tracing metadata Example ------- - Custom metadata for a pseudo service named payment - tracer = Tracer(service="payment") - response = collect_payment() - tracer.put_metadata("Payment collection", response) + ```python + from aws_lambda_powertools import Tracer + + tracer = Tracer(service="booking") + + @tracer.capture_method + def confirm_booking(booking_id: str) -> Dict: + resp = add_confirmation(booking_id) + tracer.put_metadata("Booking request metadata", resp["Metadata"]) + + return resp["booking"] + + @tracer.capture_lambda_handler + def handler(event: dict, context: Any) -> Dict: + booking_id = event.get("booking_id") + response = confirm_booking(booking_id=booking_id) + + return response + ``` """ if self.disabled: logger.debug("Tracing has been disabled, aborting put_metadata") @@ -227,14 +202,14 @@ def put_metadata(self, key: str, value: Any, namespace: Optional[str] = None): self.provider.put_metadata(key=key, value=value, namespace=namespace) def patch(self, modules: Optional[Sequence[str]] = None): - """Patch modules for instrumentation. + """Patch modules for instrumentation Patches all supported modules by default if none are given. Parameters ---------- modules : Optional[Sequence[str]] - List of modules to be patched, optional by default + List of modules to patch """ if self.disabled: logger.debug("Tracing has been disabled, aborting patch") @@ -251,40 +226,41 @@ def capture_lambda_handler( capture_response: Optional[bool] = None, capture_error: Optional[bool] = None, ): - """Decorator to create subsegment for lambda handlers + """Decorator to create subsegment for Lambda handlers - As Lambda follows (event, context) signature we can remove some of the boilerplate - and also capture any exception any Lambda function throws or its response as metadata + It automatically captures Lambda Handler's response or exception as metadata. Parameters ---------- lambda_handler : Callable - Method to annotate on - capture_response : bool, optional - Instructs tracer to not include handler's response as metadata - capture_error : bool, optional - Instructs tracer to not include handler's error as metadata, by default True + Lambda handler function + capture_response : Optional[bool] + Whether to capture handler's response as metadata, by default `True` + capture_error : Optional[bool] + Whether to capture handler's error as metadata, by default `True` Example ------- - **Lambda function using capture_lambda_handler decorator** - tracer = Tracer(service="payment") - @tracer.capture_lambda_handler - def handler(event, context): - ... + ```python + from aws_lambda_powertools import Tracer + + tracer = Tracer(service="booking") + + @tracer.capture_lambda_handler + def handler(event: dict, context: Any) -> Dict: ... + ``` **Preventing Tracer to log response as metadata** - tracer = Tracer(service="payment") - @tracer.capture_lambda_handler(capture_response=False) - def handler(event, context): - ... + ```python + tracer = Tracer(service="payment") + + @tracer.capture_lambda_handler(capture_response=False) - Raises - ------ - err - Exception raised by method + def handler(event, context): + return response_larger_than_64K_or_sensitive_data + ``` """ # If handler is None we've been called with parameters # Return a partial function with args filled @@ -354,151 +330,142 @@ def capture_method( ) -> AnyCallableT: """Decorator to create subsegment for arbitrary functions - It also captures both response and exceptions as metadata - and creates a subsegment named `## ` + It automatically captures response or exception as metadata. - When running [async functions concurrently](https://docs.python.org/3/library/asyncio-task.html#id6), - methods may impact each others subsegment, and can trigger - and AlreadyEndedException from X-Ray due to async nature. + !!! warning "Running [async functions concurrently](https://docs.python.org/3/library/asyncio-task.html#id6)" + Methods may impact each others subsegment and can trigger X-Ray `AlreadyEndedException` due to async nature. - For this use case, either use `capture_method` only where - `async.gather` is called, or use `in_subsegment_async` - context manager via our escape hatch mechanism - See examples. + For this use case, either use `capture_method` only where`async.gather` is called, + or use `in_subsegment_async` context manager via our escape hatch mechanism - See examples. Parameters ---------- method : Callable - Method to annotate on - capture_response : bool, optional - Instructs tracer to not include method's response as metadata - capture_error : bool, optional - Instructs tracer to not include handler's error as metadata, by default True + Any synchronous or asynchronous function + capture_response : Optional[bool] + Whether to capture function's response as metadata, by default `True` + capture_error : Optional[bool] + Whether to capture function's error as metadata, by default `True` Example ------- - **Custom function using capture_method decorator** - - tracer = Tracer(service="payment") - @tracer.capture_method - def some_function() - - **Custom async method using capture_method decorator** - from aws_lambda_powertools import Tracer - tracer = Tracer(service="booking") - - @tracer.capture_method - async def confirm_booking(booking_id: str) -> Dict: - resp = call_to_booking_service() - - tracer.put_annotation("BookingConfirmation", resp["requestId"]) - tracer.put_metadata("Booking confirmation", resp) - - return resp + ```python + from aws_lambda_powertools import Tracer + tracer = Tracer(service="greeting") - def lambda_handler(event: dict, context: Any) -> Dict: - booking_id = event.get("booking_id") - asyncio.run(confirm_booking(booking_id=booking_id)) + @tracer.capture_method + def greeting(name: str) -> Dict: + return { "name": name } - **Custom generator function using capture_method decorator** + @tracer.capture_lambda_handler + def handler(event: dict, context: Any) -> Dict: + response = greeting(name="Heitor") - from aws_lambda_powertools import Tracer - tracer = Tracer(service="booking") + return response + ``` - @tracer.capture_method - def bookings_generator(booking_id): - resp = call_to_booking_service() - yield resp[0] - yield resp[1] + **Tracing async method** - def lambda_handler(event: dict, context: Any) -> Dict: - gen = bookings_generator(booking_id=booking_id) - result = list(gen) + ```python + from aws_lambda_powertools import Tracer + tracer = Tracer(service="booking") - **Custom generator context manager using capture_method decorator** + @tracer.capture_method + async def confirm_booking(booking_id: str) -> Dict: + resp = call_to_booking_service() - from aws_lambda_powertools import Tracer - tracer = Tracer(service="booking") + tracer.put_annotation("BookingConfirmation", resp["requestId"]) + tracer.put_metadata("Booking confirmation", resp) - @tracer.capture_method - @contextlib.contextmanager - def booking_actions(booking_id): - resp = call_to_booking_service() - yield "example result" - cleanup_stuff() + return resp - def lambda_handler(event: dict, context: Any) -> Dict: - booking_id = event.get("booking_id") + def lambda_handler(event: dict, context: Any) -> Dict: + booking_id = event.get("booking_id") + asyncio.run(confirm_booking(booking_id=booking_id)) + ``` - with booking_actions(booking_id=booking_id) as booking: - result = booking + **Tracing generators** - **Tracing nested async calls** + ```python + from aws_lambda_powertools import Tracer + tracer = Tracer(service="booking") - from aws_lambda_powertools import Tracer - tracer = Tracer(service="booking") + @tracer.capture_method + def bookings_generator(booking_id): + resp = call_to_booking_service() + yield resp[0] + yield resp[1] - @tracer.capture_method - async def get_identity(): - ... + def lambda_handler(event: dict, context: Any) -> Dict: + gen = bookings_generator(booking_id=booking_id) + result = list(gen) + ``` - @tracer.capture_method - async def long_async_call(): - ... + **Tracing generator context managers** - @tracer.capture_method - async def async_tasks(): - await get_identity() - ret = await long_async_call() + ```python + from aws_lambda_powertools import Tracer + tracer = Tracer(service="booking") - return { "task": "done", **ret } + @tracer.capture_method + @contextlib.contextmanager + def booking_actions(booking_id): + resp = call_to_booking_service() + yield "example result" + cleanup_stuff() - **Safely tracing concurrent async calls with decorator** + def lambda_handler(event: dict, context: Any) -> Dict: + booking_id = event.get("booking_id") - This may not needed once [this bug is closed](https://github.com/aws/aws-xray-sdk-python/issues/164) + with booking_actions(booking_id=booking_id) as booking: + result = booking + ``` - from aws_lambda_powertools import Tracer - tracer = Tracer(service="booking") + **Tracing nested async calls** - async def get_identity(): - async with aioboto3.client("sts") as sts: - account = await sts.get_caller_identity() - return account + ```python + from aws_lambda_powertools import Tracer + tracer = Tracer(service="booking") - async def long_async_call(): - ... + @tracer.capture_method + async def get_identity(): + ... - @tracer.capture_method - async def async_tasks(): - _, ret = await asyncio.gather(get_identity(), long_async_call(), return_exceptions=True) + @tracer.capture_method + async def long_async_call(): + ... - return { "task": "done", **ret } + @tracer.capture_method + async def async_tasks(): + await get_identity() + ret = await long_async_call() - **Safely tracing each concurrent async calls with escape hatch** + return { "task": "done", **ret } + ``` - This may not needed once [this bug is closed](https://github.com/aws/aws-xray-sdk-python/issues/164) + **Safely tracing concurrent async calls with decorator** - from aws_lambda_powertools import Tracer - tracer = Tracer(service="booking") + > This may not be needed once [this bug is closed](https://github.com/aws/aws-xray-sdk-python/issues/164) - async def get_identity(): - async tracer.provider.in_subsegment_async("## get_identity"): - ... + ```python + from aws_lambda_powertools import Tracer + tracer = Tracer(service="booking") - async def long_async_call(): - async tracer.provider.in_subsegment_async("## long_async_call"): - ... + async def get_identity(): + async with aioboto3.client("sts") as sts: + account = await sts.get_caller_identity() + return account - @tracer.capture_method - async def async_tasks(): - _, ret = await asyncio.gather(get_identity(), long_async_call(), return_exceptions=True) + async def long_async_call(): + ... - return { "task": "done", **ret } + @tracer.capture_method + async def async_tasks(): + _, ret = await asyncio.gather(get_identity(), long_async_call(), return_exceptions=True) - Raises - ------ - err - Exception raised by method + return { "task": "done", **ret } + ``` """ # If method is None we've been called with parameters # Return a partial function with args filled @@ -539,9 +506,9 @@ async def async_tasks(): def _decorate_async_function( self, method: Callable, - capture_response: Optional[Union[bool, str]] = None, - capture_error: Optional[Union[bool, str]] = None, - method_name: Optional[str] = None, + capture_response: bool, + capture_error: bool, + method_name: str, ): @functools.wraps(method) async def decorate(*args, **kwargs): @@ -566,9 +533,9 @@ async def decorate(*args, **kwargs): def _decorate_generator_function( self, method: Callable, - capture_response: Optional[Union[bool, str]] = None, - capture_error: Optional[Union[bool, str]] = None, - method_name: Optional[str] = None, + capture_response: bool, + capture_error: bool, + method_name: str, ): @functools.wraps(method) def decorate(*args, **kwargs): @@ -593,9 +560,9 @@ def decorate(*args, **kwargs): def _decorate_generator_function_with_context_manager( self, method: Callable, - capture_response: Optional[Union[bool, str]] = None, - capture_error: Optional[Union[bool, str]] = None, - method_name: Optional[str] = None, + capture_response: bool, + capture_error: bool, + method_name: str, ): @functools.wraps(method) @contextlib.contextmanager @@ -621,9 +588,9 @@ def decorate(*args, **kwargs): def _decorate_sync_function( self, method: AnyCallableT, - capture_response: Optional[Union[bool, str]] = None, - capture_error: Optional[Union[bool, str]] = None, - method_name: Optional[str] = None, + capture_response: bool, + capture_error: bool, + method_name: str, ) -> AnyCallableT: @functools.wraps(method) def decorate(*args, **kwargs): @@ -659,13 +626,13 @@ def _add_response_as_metadata( Parameters ---------- - method_name : str, optional - method name to add as metadata key, by default None - data : Any, optional - data to add as subsegment metadata, by default None - subsegment : BaseSegment, optional - existing subsegment to add metadata on, by default None - capture_response : bool, optional + method_name : str + method name to add as metadata key, by default `None` + data : Any + data to add as subsegment metadata, by default `None` + subsegment : BaseSegment + existing subsegment to add metadata on, by default `None` + capture_response : Optional[bool] Do not include response as metadata """ if data is None or not capture_response or subsegment is None: @@ -685,12 +652,12 @@ def _add_full_exception_as_metadata( Parameters ---------- method_name : str - method name to add as metadata key, by default None + method name to add as metadata key, by default `None` error : Exception - error to add as subsegment metadata, by default None + error to add as subsegment metadata, by default `None` subsegment : BaseSegment - existing subsegment to add metadata on, by default None - capture_error : bool, optional + existing subsegment to add metadata on, by default `None` + capture_error : Optional[bool] Do not include error as metadata, by default True """ if not capture_error: diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 00000000000..a58e2f4e4bd --- /dev/null +++ b/docs/api.md @@ -0,0 +1,6 @@ +::: aws_lambda_powertools.tracing + selection: + filters: + - "!^_" + - "^__init__$" + - "!^base$" diff --git a/mkdocs.yml b/mkdocs.yml index 7ee0fd56236..6c3048f0d46 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,7 +8,7 @@ nav: - Homepage: index.md - Changelog: changelog.md - Roadmap: https://github.com/awslabs/aws-lambda-powertools-roadmap/projects/1" target="_blank - - API reference: api/" target="_blank + - API reference: api.md - Core utilities: - core/tracer.md - core/logger.md @@ -26,6 +26,7 @@ nav: - utilities/parser.md - utilities/idempotency.md + theme: name: material palette: @@ -73,6 +74,13 @@ copyright: Copyright © 2021 Amazon Web Services plugins: - git-revision-date - search + - mkdocstrings: + handlers: + python: + selection: + docstring_style: numpy + watch: + - aws_lambda_powertools extra_css: - stylesheets/extra.css diff --git a/poetry.lock b/poetry.lock index b289b51e5e2..bfe9934e584 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,6 +6,17 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "astunparse" +version = "1.6.3" +description = "An AST unparser for Python" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = ">=1.6.1,<2.0" + [[package]] name = "atomicwrites" version = "1.4.0" @@ -108,6 +119,14 @@ urllib3 = ">=1.25.4,<1.27" [package.extras] crt = ["awscrt (==0.11.24)"] +[[package]] +name = "cached-property" +version = "1.5.2" +description = "A decorator for caching properties in classes." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "certifi" version = "2020.12.5" @@ -181,6 +200,14 @@ idna = ["idna (>=2.1)"] curio = ["curio (>=1.2)", "sniffio (>=1.1)"] trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"] +[[package]] +name = "docstring-parser" +version = "0.7.3" +description = "" +category = "dev" +optional = false +python-versions = "~=3.5" + [[package]] name = "email-validator" version = "1.1.3" @@ -577,6 +604,18 @@ Markdown = ">=3.2.1" PyYAML = ">=3.10" tornado = ">=5.0" +[[package]] +name = "mkdocs-autorefs" +version = "0.2.1" +description = "Automatically link across pages in MkDocs." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +Markdown = ">=3.3,<4.0" +mkdocs = ">=1.1,<2.0" + [[package]] name = "mkdocs-git-revision-date-plugin" version = "0.3.1" @@ -617,6 +656,21 @@ python-versions = ">=3.5" mkdocs-material = ">=5.0.0" [[package]] +name = "mkdocstrings" +version = "0.15.2" +description = "Automatic documentation from sources, for MkDocs." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +Jinja2 = ">=2.11.1,<4.0" +Markdown = ">=3.3,<4.0" +MarkupSafe = ">=1.1,<3.0" +mkdocs = ">=1.1.1,<2.0.0" +mkdocs-autorefs = ">=0.1,<0.3" +pymdown-extensions = ">=6.3,<9.0" +pytkdocs = ">=0.2.0,<0.12.0" name = "mypy" version = "0.910" description = "Optional static typing for Python" @@ -861,6 +915,24 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" [package.dependencies] six = ">=1.5" +[[package]] +name = "pytkdocs" +version = "0.11.1" +description = "Load Python objects documentation." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0.0" + +[package.dependencies] +astunparse = {version = ">=1.6.3,<2.0.0", markers = "python_version < \"3.9\""} +cached-property = {version = ">=1.5.2,<2.0.0", markers = "python_version < \"3.8\""} +dataclasses = {version = ">=0.7,<0.9", markers = "python_version == \"3.6\""} +docstring_parser = {version = ">=0.7.3,<0.8.0", optional = true, markers = "extra == \"numpy-style\""} +typing-extensions = {version = ">=3.7.4.3,<4.0.0.0", markers = "python_version < \"3.8\""} + +[package.extras] +numpy-style = ["docstring_parser (>=0.7.3,<0.8.0)"] + [[package]] name = "pyyaml" version = "5.4.1" @@ -1091,6 +1163,10 @@ appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] +astunparse = [ + {file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"}, + {file = "astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872"}, +] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, @@ -1118,6 +1194,10 @@ botocore = [ {file = "botocore-1.21.1-py3-none-any.whl", hash = "sha256:b845220eb580d10f7714798a96e380eb8f94dca89905a41d8a3c35119c757b01"}, {file = "botocore-1.21.1.tar.gz", hash = "sha256:200887ce5f3b47d7499b7ded75dc65c4649abdaaddd06cebc118a3a954d6fd73"}, ] +cached-property = [ + {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, + {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, +] certifi = [ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, @@ -1196,6 +1276,9 @@ dnspython = [ {file = "dnspython-2.1.0-py3-none-any.whl", hash = "sha256:95d12f6ef0317118d2a1a6fc49aac65ffec7eb8087474158f42f26a639135216"}, {file = "dnspython-2.1.0.zip", hash = "sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"}, ] +docstring-parser = [ + {file = "docstring_parser-0.7.3.tar.gz", hash = "sha256:cde5fbf8b846433dfbde1e0f96b7f909336a634d5df34a38cb75050c7346734a"}, +] email-validator = [ {file = "email_validator-1.1.3-py2.py3-none-any.whl", hash = "sha256:5675c8ceb7106a37e40e2698a57c056756bf3f272cfa8682a4f87ebd95d8440b"}, {file = "email_validator-1.1.3.tar.gz", hash = "sha256:aa237a65f6f4da067119b7df3f13e89c25c051327b2b5b66dc075f33d62480d7"}, @@ -1356,6 +1439,10 @@ mkdocs = [ {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, ] +mkdocs-autorefs = [ + {file = "mkdocs-autorefs-0.2.1.tar.gz", hash = "sha256:b8156d653ed91356e71675ce1fa1186d2b2c2085050012522895c9aa98fca3e5"}, + {file = "mkdocs_autorefs-0.2.1-py3-none-any.whl", hash = "sha256:f301b983a34259df90b3fcf7edc234b5e6c7065bd578781e66fd90b8cfbe76be"}, +] mkdocs-git-revision-date-plugin = [ {file = "mkdocs-git-revision-date-plugin-0.3.1.tar.gz", hash = "sha256:4abaef720763a64c952bed6829dcc180f67c97c60dd73914e90715e05d1cfb23"}, {file = "mkdocs_git_revision_date_plugin-0.3.1-py3-none-any.whl", hash = "sha256:8ae50b45eb75d07b150a69726041860801615aae5f4adbd6b1cf4d51abaa03d5"}, @@ -1368,6 +1455,9 @@ mkdocs-material-extensions = [ {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, {file = "mkdocs_material_extensions-1.0.1-py3-none-any.whl", hash = "sha256:d90c807a88348aa6d1805657ec5c0b2d8d609c110e62b9dce4daf7fa981fa338"}, ] +mkdocstrings = [ + {file = "mkdocstrings-0.15.2-py3-none-any.whl", hash = "sha256:8d6cbe64c07ae66739010979ca01d49dd2f64d1a45009f089d217b9cd2a65e36"}, + {file = "mkdocstrings-0.15.2.tar.gz", hash = "sha256:c2fee9a3a644647c06eb2044fdfede1073adfd1a55bf6752005d3db10705fe73"}, mypy = [ {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, @@ -1488,6 +1578,10 @@ python-dateutil = [ {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, ] +pytkdocs = [ + {file = "pytkdocs-0.11.1-py3-none-any.whl", hash = "sha256:89ca4926d0acc266235beb24cb0b0591aa6bf7adedfae54bf9421d529d782c8d"}, + {file = "pytkdocs-0.11.1.tar.gz", hash = "sha256:1ec7e028fe8361acc1ce909ada4e6beabec28ef31e629618549109e1d58549f0"}, +] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, diff --git a/pyproject.toml b/pyproject.toml index f65572f7b33..e1766e39ec2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,8 @@ flake8-bugbear = "^21.3.2" mkdocs-material = "^7.1.10" mkdocs-git-revision-date-plugin = "^0.3.1" mike = "^0.6.0" +mkdocstrings = "^0.15.2" +pytkdocs = {extras = ["numpy-style"], version = "^0.11.1"} mypy = "^0.910"