diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 767c4413196c..05f2eb0560d8 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -244,6 +244,8 @@ "livekvtestgetkeyperfkey", "livekvtestsignperfkey", "livekvtestunwrapperfkey", + "lrobasepollingasynclrobasepolling", + "lropollerasynclropoller", "lmazuel", "logz", "LPCWSTR", @@ -264,6 +266,7 @@ "mydirectory", "myfile", "myvault", + "mytable", "nazsdk", "nbytes", "nbsp", diff --git a/doc/dev/customize_long_running_operation.md b/doc/dev/customize_long_running_operation.md index 32ce2d94c991..b8cda3d8c756 100644 --- a/doc/dev/customize_long_running_operation.md +++ b/doc/dev/customize_long_running_operation.md @@ -3,9 +3,9 @@ Operations that are started by an initial call, then need to be monitored for status until completion are often represented as long-running operations (LRO). The `azure-core` library provides the [LROPoller][lro_poller] (and [AsyncLROPoller][async_lro_poller]) protocols that expose methods to interact with the LRO such as waiting until the operation reaches a terminal state, checking its status, or -providing a callback to do work on the final result when it is ready. If the LRO follows the -[Azure REST API guidelines][rest_api_guidelines_lro], -it's likely that the generated client library code should _just work_. +providing a callback to do work on the final result when it is ready. If the LRO follows the +[Azure REST API guidelines][rest_api_guidelines_lro], +it's likely that the generated client library code should _just work_. In cases where the LRO diverges from the guidelines, you may need to customize your code to achieve the desired scenario. There are 3 options to customize the logic for LROs. @@ -19,19 +19,19 @@ There are 3 options to customize the logic for LROs. The "poller API" is what the user uses to interact with the LRO. Internally, the poller uses the "polling method" to run the polling loop which makes calls to the status monitor, controls delay, and determines when the LRO has reached a terminal state. -The "polling method" uses a "polling strategy" to determine how to extract the status information from the responses +The "polling method" uses a "polling strategy" to determine how to extract the status information from the responses returned by the status monitoring. ### Polling strategy - OperationResourcePolling, LocationPolling, StatusCheckPolling The `azure.core.polling` module provides three built-in strategies for polling -[OperationResourcePolling][operation_resource_polling], -[LocationPolling][location_polling], and [StatusCheckPolling][status_check_polling]. The type of polling needed will be -determined automatically using the response structure, unless otherwise specified by the client library developer. If -the LRO is determined not to fit either `OperationResourcePolling` or `LocationPolling`, `StatusCheckPolling` serves as +[LocationPolling][location_polling], and [StatusCheckPolling][status_check_polling]. The type of polling needed will be +determined automatically using the response structure, unless otherwise specified by the client library developer. If +the LRO is determined not to fit either `OperationResourcePolling` or `LocationPolling`, `StatusCheckPolling` serves as a fallback strategy which will not perform polling, but instead return a successful response for a 2xx status code. If you need to customize the polling strategy, choose a polling algorithm that closely represents what you need to do -or create your own that inherits from [azure.core.polling.base_polling.LongRunningOperation][long_running_operation] +or create your own that inherits from [azure.core.polling.base_polling.LongRunningOperation][long_running_operation] and implements the necessary methods. For our example, let's say that `OperationResourcePolling` closely resembles what the service does, but we @@ -52,7 +52,7 @@ class CustomOperationResourcePolling(OperationResourcePolling): def get_status(self, pipeline_response: "PipelineResponseType") -> str: """This method is called on the response for each polling request - and is used to extract and return the LRO status from that response. + and is used to extract and return the LRO status from that response. In the case that the operation has failed (i.e. a non-successful status), an exception should be raised. This will bring polling to an end and raise the failure to the listener. @@ -66,8 +66,8 @@ class CustomOperationResourcePolling(OperationResourcePolling): ``` You can then wrap a client method that was generated as an LRO, and pass the additional `polling` keyword argument. The `polling` -keyword argument takes an implementation of [azure.core.polling.PollingMethod][polling_method] -(e.g. [azure.core.polling.base_polling.LROBasePolling][lro_base_polling]) and allows for a custom strategy to be passed +keyword argument takes an implementation of [azure.core.polling.PollingMethod][polling_method] +(e.g. [azure.core.polling.base_polling.LROBasePolling][lro_base_polling]) and allows for a custom strategy to be passed in to the keyword argument `lro_algorithms`: ```python @@ -98,12 +98,12 @@ If you need to control the polling loop, then see the next section. ### Polling method - LROBasePolling/AsyncLROBasePolling -Built-in methods for polling are included in `azure-core` as both sync / async variants - [LROBasePolling][lro_base_polling] -and [AsyncLROBasePolling][async_lro_base_polling]. The polling method runs the polling loop and performs GET requests -to the status monitor to check if a terminal state is reached. In between polls it inserts delay based on +Built-in methods for polling are included in `azure-core` as both sync / async variants - [LROBasePolling][lro_base_polling] +and [AsyncLROBasePolling][async_lro_base_polling]. The polling method runs the polling loop and performs GET requests +to the status monitor to check if a terminal state is reached. In between polls it inserts delay based on 1) the service sent `retry-after` header, or 2) the given `polling_interval` if no retry-after header is present. -You can also use [azure.core.polling.NoPolling][no_polling](or [AsyncNoPolling][async_no_polling]) which will not +You can also use [azure.core.polling.NoPolling][no_polling](or [AsyncNoPolling][async_no_polling]) which will not initiate polling and simply return the deserialized initial response when called with `poller.result()`. To use `NoPolling`, you can pass `polling=False` to an operation generated as an LRO: @@ -126,7 +126,7 @@ class ServiceOperations: ``` To customize parts of the polling method, you can create a subclass which uses [LROBasePolling][lro_base_polling] and overrides necessary methods. -If significant customization is necessary, use [azure.core.polling.PollingMethod][polling_method] +If significant customization is necessary, use [azure.core.polling.PollingMethod][polling_method] (or [AsyncPollingMethod][async_polling_method])and implement all the necessary methods. #### Example: Create an LRO method which will poll for when a file gets uploaded successfully (greatly simplified) @@ -160,7 +160,7 @@ class CustomPollingStrategy(LongRunningOperation): def get_polling_url(self) -> str: """Return the polling URL. This is the URL for the status monitor and where the GET requests will be made during polling. - + For this example, we don't need to extract the URL from the initial response so it is not implemented. """ @@ -180,9 +180,9 @@ class CustomPollingStrategy(LongRunningOperation): def get_status(self, response: JSON) -> str: """Return the status based on this response. - Typically, this method extracts a status string from the + Typically, this method extracts a status string from the response. In this example, we determine status based on whether our - result is populated or not. + result is populated or not. """ if response is None: return "InProgress" @@ -225,7 +225,7 @@ class CustomPollingMethod(PollingMethod): self._kwargs = kwargs def initialize(self, client: Any, initial_response: PipelineResponse, deserialization_callback: Callable) -> None: - """Set the initial status of this LRO, verify that we can poll, and + """Set the initial status of this LRO, verify that we can poll, and initialize anything necessary for polling. :param client: An instance of a client. In this example, the generated client. @@ -236,7 +236,7 @@ class CustomPollingMethod(PollingMethod): # verify we have the information to poll if self._operation.can_poll(initial_response) is False: raise BadResponse("No file_id in response.") - + response = initial_response.http_response.json() # initialize @@ -246,17 +246,17 @@ class CustomPollingMethod(PollingMethod): self._deserialization_callback = deserialization_callback self._resource = None self._finished = False - + # sets our strategy self._operation = CustomPollingStrategy() - + # create the command which will be polled against as the status monitor self._command = functools.partial(self.client.get_upload_file, file_id=self.file_id, **self._kwargs) - + # set initial status self._status = self._operation.set_initial_status(initial_response) - + def status(self) -> str: """Should return the current status as a string. The initial status is set by the polling strategy with set_initial_status() and then subsequently set by @@ -272,7 +272,7 @@ class CustomPollingMethod(PollingMethod): """Is this polling finished? Controls whether the polling loop should continue to poll. - :returns: Return True if the operation has reached a terminal state + :returns: Return True if the operation has reached a terminal state or False if polling should continue. :rtype: bool """ @@ -282,15 +282,15 @@ class CustomPollingMethod(PollingMethod): """Return the built resource. This is what is returned when to the user when result() is called on the LROPoller. - This might include a deserialization callback (passed in initialize()) + This might include a deserialization callback (passed in initialize()) to transform or customize the final result, if necessary. """ return self._deserialization_callback(self._resource) def run(self) -> None: """The polling loop. - - The polling should call the status monitor, evaluate and set the current status, + + The polling should call the status monitor, evaluate and set the current status, insert delay between polls, and continue polling until a terminal state is reached. """ while not self.finished(): @@ -300,15 +300,15 @@ class CustomPollingMethod(PollingMethod): time.sleep(self._polling_interval) def update_status(self): - """Update the current status of the LRO by calling the status monitor + """Update the current status of the LRO by calling the status monitor and then using the polling strategy's get_status() to set the status.""" try: self._resource = self._command() except ResourceNotFoundError: pass - + self._status = self._operation.get_status(self._resource) - + def get_continuation_token(self) -> str: """Returns an opaque token which can be used by the user to rehydrate/restart the LRO. Saves the initial state of the LRO so that polling can be resumed from that context. @@ -330,7 +330,7 @@ class CustomPollingMethod(PollingMethod): @classmethod def from_continuation_token(cls, continuation_token: str, **kwargs: Any) -> Tuple[Any, PipelineResponse, Callable]: - """Deserializes the user-provided continuation_token to the initial response and returns + """Deserializes the user-provided continuation_token to the initial response and returns the context necessary to rebuild the LROPoller from its classmethod. """ try: @@ -344,7 +344,7 @@ class CustomPollingMethod(PollingMethod): raise ValueError( "Need kwarg 'deserialization_callback' to be recreated from continuation_token" ) - + import pickle initial_response = pickle.loads(base64.b64decode(continuation_token)) # nosec @@ -366,7 +366,7 @@ class ServiceOperations: def begin_upload(self, data: AnyStr, **kwargs) -> LROPoller[JSON]: continuation_token = kwargs.pop("continuation_token", None) polling_method = CustomPollingMethod(**kwargs) - + # if continuation_token is provided, we should rehydrate the LRO using the from_continuation_token method # which calls our implementation on the CustomPollingMethod method if continuation_token is not None: @@ -389,8 +389,8 @@ class ServiceOperations: ``` Note that we need to account for a `continuation_token` being passed by the user, in which case we should not make the -initial call again, but rather resume polling from the rehydrated state. Since passing `continuation_token` doesn't -require the user to provide the parameters for the initial call, it can be helpful to add overloads to the method to +initial call again, but rather resume polling from the rehydrated state. Since passing `continuation_token` doesn't +require the user to provide the parameters for the initial call, it can be helpful to add overloads to the method to clarify its usage, especially in cases where required parameters become non-required: ```python @@ -441,22 +441,22 @@ class ServiceOperations: ### Poller API - LROPoller/AsyncLROPoller The last option is if you need to customize the public interface of the `LROPoller` / `AsyncLROPoller`. -Reasons to do this might include exposing important attributes or metadata of the operation in progress, +Reasons to do this might include exposing important attributes or metadata of the operation in progress, or adding new features to interact with the operation, such as to pause/resume or cancel it. #### Example: I want to add a cancel method to my poller This example builds off the previous example and uses the custom polling method defined above. The custom polling -method gives us access to the client and `file_id` needed to make the `cancel` call. If you support rehydration of +method gives us access to the client and `file_id` needed to make the `cancel` call. If you support rehydration of the LRO via `continuation_token`, you must override the `from_continuation_token` method so that the custom poller is used. ```python from typing import TypeVar from azure.core.polling import LROPoller, PollingMethod -PollingReturnType = TypeVar("PollingReturnType") +PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) -class CustomLROPoller(LROPoller[PollingReturnType]): +class CustomLROPoller(LROPoller[PollingReturnType_co]): def cancel(self, **kwargs) -> None: """Cancel the upload""" @@ -464,8 +464,8 @@ class CustomLROPoller(LROPoller[PollingReturnType]): @classmethod def from_continuation_token( - cls, polling_method: PollingMethod[PollingReturnType], continuation_token: str, **kwargs - ) -> "CustomLROPoller[PollingReturnType]": + cls, polling_method: PollingMethod[PollingReturnType_co], continuation_token: str, **kwargs + ) -> "CustomLROPoller[PollingReturnType_co]": ( client, initial_response, @@ -504,7 +504,7 @@ class ServiceOperations: ``` Note that we updated the `begin_upload` return type to `CustomLROPoller`. You should only need to explicitly reference -the custom poller if a new public API has been added. The custom poller should additionally be added to the package +the custom poller if a new public API has been added. The custom poller should additionally be added to the package `__init__.py` so that the new public API will be properly documented. diff --git a/doc/dev/static_type_checking.md b/doc/dev/static_type_checking.md index db76343d996d..237d6b26186c 100644 --- a/doc/dev/static_type_checking.md +++ b/doc/dev/static_type_checking.md @@ -101,7 +101,7 @@ See [Types usable in annotations](#types-usable-in-annotations) for more informa When starting to add type hints to a client library, there are a few things to consider. First, Python has a "gradual type system". What this means is that typing is not an all-or-nothing task - you can gradually add types into your code and any code without type hints will just be ignored by the type checker. Typing is optional and this is a good -thing! Pushing to achieve full coverage of annotated code can lead to low signal-to-noise and sometimes is not practical +thing! Pushing to achieve full coverage of annotated code can lead to low signal-to-noise and sometimes is not practical given the expressiveness of Python as a language. So, in practice, what should you aim to type? 1) Add type hints to publicly exposed APIs in the client library. Type hints get shipped with our client libraries (both whl and sdist) @@ -273,7 +273,7 @@ ignore_me: int = 5 # type: ignore https://www.github.com/Azure/azure-sdk-for-p Note that it is possible to be specific in the error to ignore, instead of globally turning off type checking on that line. Syntax for this involves putting the specific error code in brackets of the ignore comment. Error codes -are found next to the type checking error. +are found next to the type checking error. *mypy:* ```python @@ -545,7 +545,7 @@ type checked (since there are no other typed parameters found in signature). ```python class KeyCredential: - + def __init__(self) -> None: ... ``` @@ -770,7 +770,7 @@ At import time, the default behavior in Python is to read in all type hints and `from __future__ import annotations` changes this such that type hints don't get evaluated at runtime and are preserved as string literals in the `__annotations__` dictionary. There is no difference in behavior for the type checkers with using this import. Note that `from __future__ import annotations` must be imported at the top of the file before any other imports. -It's also worth calling out that using this import also allows use of generic collection type hints like `dict` and `list` instead of `typing.Dict` and `typing.List`. +It's also worth calling out that using this import also allows use of generic collection type hints like `dict` and `list` instead of `typing.Dict` and `typing.List`. More details about this import and its behavior can be found in [PEP 563](https://peps.python.org/pep-0563/). Information about the PEP which may supersede this can be found in [PEP 649](https://peps.python.org/pep-0649/). @@ -909,7 +909,7 @@ main.py:39: note: Revealed type is "builtins.str" ``` An overload needs two or more variants + the actual implementation to be valid. Because the implementation is the only -function allowed to contain code, you must handle the different combinations of arguments at runtime yourself in order +function allowed to contain code, you must handle the different combinations of arguments at runtime yourself in order to create the desired behavior. ### Use typing.cast to help the type checker understand a type @@ -996,15 +996,15 @@ parameter `PollingReturnType` which can then be used to type throughout the impl ```python from typing import TypeVar, Generic, Callable, Any, Optional -PollingReturnType = TypeVar("PollingReturnType") +PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) -class LROPoller(Generic[PollingReturnType]): +class LROPoller(Generic[PollingReturnType_co]): def __init__(self, client: Any, initial_response: Any, deserialization_callback: Callable, - polling_method: PollingMethod[PollingReturnType]): + polling_method: PollingMethod[PollingReturnType_co]): ... - def result(self, timeout: Optional[int] = None) -> PollingReturnType: + def result(self, timeout: Optional[int] = None) -> PollingReturnType_co: ... ``` @@ -1186,7 +1186,7 @@ values: from typing_extensions import Literal doc_type = Literal["prebuilt-receipt"] -allowed_content_types = Literal["application/json", "text/plain", "image/png", "image/jpeg"] +allowed_content_types = Literal["application/json", "text/plain", "image/png", "image/jpeg"] ``` Literals can be useful with functions that behave differently based on an exact value that the caller specifies. @@ -1365,7 +1365,7 @@ Therefore, it is preferred to use `typing.Literal` in this situation to provide ### Use typing.NewType to restrict a type to a specific context -`NewType` can be useful to catch errors where a particular type is expected. `NewType` will take an existing type and +`NewType` can be useful to catch errors where a particular type is expected. `NewType` will take an existing type and create a brand new type in the same shape; however, these two will not be interchangeable. In the below example, a string should be passed into `print_log`, but that input string should be specifically the type `str` returned from the `sanitize` function. `NewType` creates a `Sanitized` type which is viewed as a distinct type for the type checker: diff --git a/doc/dev/static_type_checking_cheat_sheet.md b/doc/dev/static_type_checking_cheat_sheet.md index 84bc58f1cb27..b05fd3dfa798 100644 --- a/doc/dev/static_type_checking_cheat_sheet.md +++ b/doc/dev/static_type_checking_cheat_sheet.md @@ -1,13 +1,13 @@ # Static Type Checking Cheat Sheet -This cheat sheet details guidance for typing as it relates to the Python SDK. Use your own judgment to achieve the best balance of clarity and flexibility for your Python client library. +This cheat sheet details guidance for typing as it relates to the Python SDK. Use your own judgment to achieve the best balance of clarity and flexibility for your Python client library. ### General guidance - Do provide type annotations (per [PEP 484](https://peps.python.org/pep-0484/)) to public APIs in the client library. - You do not need to annotate internal functions in a client library, but you should provide type hints where unit tests are worth writing or where type annotations will assist in understanding of the code. - Do not use comment style type hints. Use inline, annotation style. - + ```python # No: def create_table(table_name): @@ -35,7 +35,7 @@ table_map[table_name] = create_table(table_name) - You do not need to annotate `self` or `cls`. - Do return `None` from a constructor. - + ```python # No: class KeyCredential: @@ -60,7 +60,7 @@ class Tree: :param str location: The location for the tree. :param int num_branches: The number of branches on the tree :param str kind: The kind of tree. - + Note: :ivar: docstrings are redundant since these vars/types are captured below """ @@ -72,7 +72,7 @@ class Tree: """The kind of tree.""" def __init__(self, *, location: str, num_branches: int, kind: Optional[str] = None) -> None: - if kind: + if kind: self.kind = kind self.location = location self.num_branches = num_branches @@ -91,7 +91,7 @@ from typing_extensions import TypedDict ### Importing types -- Do use only publicly exposed client library types in type hints. +- Do use only publicly exposed client library types in type hints. - Do import types from modules such as `typing`, `typing_extensions`, `collections`, and `collections.abc`(Note that indexing support for generic collection types from `collections.abc` is only supported on Python 3.9+). - Do not import regular type hints under a `typing.TYPE_CHECKING` block. You may use `TYPE_CHECKING` to fix a circular import or avoid importing a type only needed in type annotations that is otherwise costly to load at runtime. @@ -116,7 +116,7 @@ if TYPE_CHECKING: ```python # mypy ignores only the error code in brackets -ignore_me: int = 5 # type: ignore[misc] +ignore_me: int = 5 # type: ignore[misc] # pyright ignores only the error code in brackets ignored = result._private # pyright: ignore[reportPrivateUsage] @@ -134,7 +134,7 @@ class Foo(Any): # type: ignore ### Unions - Use `typing.Union` when a parameter can accept more than one type. - + ```python from typing import Union @@ -324,15 +324,15 @@ S = TypeVar("S", bound=str) # limited to str or any subtype of str from typing import TypeVar, Generic # No: -T = TypeVar("T") +T = TypeVar("T", covariant=True) -class LROPoller(Generic[T]): +class LROPoller(Generic["T"]): ... # Yes: -PollingReturnType = TypeVar("PollingReturnType") +PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) -class LROPoller(Generic[PollingReturnType]): +class LROPoller(Generic[PollingReturnType_co]): ... ``` @@ -385,7 +385,7 @@ CredentialTypes = Union[AzureKeyCredential, TokenCredential, AzureSasCredential, ### Literals - You can use `typing.Literal` when you want to restrict based on exact values. - + ```python from typing_extensions import Literal diff --git a/sdk/core/azure-core/azure/core/polling/_async_poller.py b/sdk/core/azure-core/azure/core/polling/_async_poller.py index c50222363531..115fb176f528 100644 --- a/sdk/core/azure-core/azure/core/polling/_async_poller.py +++ b/sdk/core/azure-core/azure/core/polling/_async_poller.py @@ -31,12 +31,12 @@ from ._poller import NoPolling as _NoPolling -PollingReturnType = TypeVar("PollingReturnType") +PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) _LOGGER = logging.getLogger(__name__) -class AsyncPollingMethod(Generic[PollingReturnType]): +class AsyncPollingMethod(Generic[PollingReturnType_co]): """ABC class for polling method.""" def initialize(self, client: Any, initial_response: Any, deserialization_callback: Any) -> None: @@ -51,7 +51,7 @@ def status(self) -> str: def finished(self) -> bool: raise NotImplementedError("This method needs to be implemented") - def resource(self) -> PollingReturnType: + def resource(self) -> PollingReturnType_co: raise NotImplementedError("This method needs to be implemented") def get_continuation_token(self) -> str: @@ -91,7 +91,7 @@ async def async_poller(client, initial_response, deserialization_callback, polli return await poller -class AsyncLROPoller(Generic[PollingReturnType], Awaitable): +class AsyncLROPoller(Generic[PollingReturnType_co], Awaitable): """Async poller for long running operations. :param client: A pipeline service client @@ -110,7 +110,7 @@ def __init__( client: Any, initial_response: Any, deserialization_callback: Callable, - polling_method: AsyncPollingMethod[PollingReturnType], + polling_method: AsyncPollingMethod[PollingReturnType_co], ): self._polling_method = polling_method self._done = False @@ -123,7 +123,7 @@ def __init__( self._polling_method.initialize(client, initial_response, deserialization_callback) - def polling_method(self) -> AsyncPollingMethod[PollingReturnType]: + def polling_method(self) -> AsyncPollingMethod[PollingReturnType_co]: """Return the polling method associated to this poller.""" return self._polling_method @@ -137,8 +137,8 @@ def continuation_token(self) -> str: @classmethod def from_continuation_token( - cls, polling_method: AsyncPollingMethod[PollingReturnType], continuation_token: str, **kwargs - ) -> "AsyncLROPoller[PollingReturnType]": + cls, polling_method: AsyncPollingMethod[PollingReturnType_co], continuation_token: str, **kwargs + ) -> "AsyncLROPoller[PollingReturnType_co]": ( client, initial_response, @@ -154,7 +154,7 @@ def status(self) -> str: """ return self._polling_method.status() - async def result(self) -> PollingReturnType: + async def result(self) -> PollingReturnType_co: """Return the result of the long running operation. :returns: The deserialized resource of the long running operation, if one is available. @@ -163,7 +163,7 @@ async def result(self) -> PollingReturnType: await self.wait() return self._polling_method.resource() - def __await__(self) -> Generator[Any, None, PollingReturnType]: + def __await__(self) -> Generator[Any, None, PollingReturnType_co]: return self.result().__await__() async def wait(self) -> None: diff --git a/sdk/core/azure-core/azure/core/polling/_poller.py b/sdk/core/azure-core/azure/core/polling/_poller.py index 84067f301392..ac3cc999ccab 100644 --- a/sdk/core/azure-core/azure/core/polling/_poller.py +++ b/sdk/core/azure-core/azure/core/polling/_poller.py @@ -33,12 +33,12 @@ from azure.core.tracing.common import with_current_context -PollingReturnType = TypeVar("PollingReturnType") +PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) _LOGGER = logging.getLogger(__name__) -class PollingMethod(Generic[PollingReturnType]): +class PollingMethod(Generic[PollingReturnType_co]): """ABC class for polling method.""" def initialize(self, client: Any, initial_response: Any, deserialization_callback: Any) -> None: @@ -53,7 +53,7 @@ def status(self) -> str: def finished(self) -> bool: raise NotImplementedError("This method needs to be implemented") - def resource(self) -> PollingReturnType: + def resource(self) -> PollingReturnType_co: raise NotImplementedError("This method needs to be implemented") def get_continuation_token(self) -> str: @@ -112,7 +112,7 @@ def from_continuation_token(cls, continuation_token: str, **kwargs) -> Tuple[Any return None, initial_response, deserialization_callback -class LROPoller(Generic[PollingReturnType]): +class LROPoller(Generic[PollingReturnType_co]): """Poller for long running operations. :param client: A pipeline service client @@ -131,7 +131,7 @@ def __init__( client: Any, initial_response: Any, deserialization_callback: Callable, - polling_method: PollingMethod[PollingReturnType], + polling_method: PollingMethod[PollingReturnType_co], ) -> None: self._callbacks: List[Callable] = [] self._polling_method = polling_method @@ -188,7 +188,7 @@ def _start(self): call(self._polling_method) callbacks, self._callbacks = self._callbacks, [] - def polling_method(self) -> PollingMethod[PollingReturnType]: + def polling_method(self) -> PollingMethod[PollingReturnType_co]: """Return the polling method associated to this poller.""" return self._polling_method @@ -202,8 +202,8 @@ def continuation_token(self) -> str: @classmethod def from_continuation_token( - cls, polling_method: PollingMethod[PollingReturnType], continuation_token: str, **kwargs - ) -> "LROPoller[PollingReturnType]": + cls, polling_method: PollingMethod[PollingReturnType_co], continuation_token: str, **kwargs + ) -> "LROPoller[PollingReturnType_co]": ( client, initial_response, @@ -219,7 +219,7 @@ def status(self) -> str: """ return self._polling_method.status() - def result(self, timeout: Optional[float] = None) -> PollingReturnType: + def result(self, timeout: Optional[float] = None) -> PollingReturnType_co: """Return the result of the long running operation, or the result available after the specified timeout. diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_polling.py b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_polling.py index d780a9e53834..ab524ea7536b 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_polling.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/_polling.py @@ -20,7 +20,7 @@ BadResponse, ) -PollingReturnType = TypeVar("PollingReturnType") +PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) def raise_error(response, errors, message): @@ -37,7 +37,7 @@ def parse_operation_id(location): @runtime_checkable -class DocumentModelAdministrationLROPoller(Protocol[PollingReturnType]): +class DocumentModelAdministrationLROPoller(Protocol[PollingReturnType_co]): """Implements a protocol followed by returned poller objects.""" @property @@ -48,7 +48,7 @@ def details( # pylint: disable=no-self-use, unused-argument def polling_method( # pylint: disable=no-self-use self, - ) -> PollingMethod[PollingReturnType]: + ) -> PollingMethod[PollingReturnType_co]: ... def continuation_token(self) -> str: # pylint: disable=no-self-use @@ -59,7 +59,7 @@ def status(self) -> str: # pylint: disable=no-self-use def result( # pylint: disable=no-self-use, unused-argument self, timeout: Optional[int] = None - ) -> PollingReturnType: + ) -> PollingReturnType_co: ... def wait(self, timeout: Optional[float] = None) -> None: # pylint: disable=no-self-use, unused-argument @@ -75,7 +75,7 @@ def remove_done_callback(self, func: Callable) -> None: # pylint: disable=no-se ... -class DocumentModelAdministrationClientLROPoller(LROPoller[PollingReturnType]): +class DocumentModelAdministrationClientLROPoller(LROPoller[PollingReturnType_co]): """Custom poller for model build operations. Call `result()` on the poller to return a :class:`~azure.ai.formrecognizer.DocumentModelDetails`. @@ -112,7 +112,7 @@ def details(self) -> Mapping[str, Any]: @classmethod def from_continuation_token( - cls, polling_method: PollingMethod[PollingReturnType], continuation_token: str, **kwargs: Any + cls, polling_method: PollingMethod[PollingReturnType_co], continuation_token: str, **kwargs: Any ) -> "DocumentModelAdministrationClientLROPoller": ( client, diff --git a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_async_polling.py b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_async_polling.py index 4e3a48498c8e..ef7aeedb6eea 100644 --- a/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_async_polling.py +++ b/sdk/formrecognizer/azure-ai-formrecognizer/azure/ai/formrecognizer/aio/_async_polling.py @@ -12,11 +12,11 @@ from azure.core.polling import AsyncLROPoller, AsyncPollingMethod from .._polling import parse_operation_id -PollingReturnType = TypeVar("PollingReturnType") +PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) @runtime_checkable -class AsyncDocumentModelAdministrationLROPoller(Protocol[PollingReturnType]): +class AsyncDocumentModelAdministrationLROPoller(Protocol[PollingReturnType_co]): """Implements a protocol followed by returned poller objects.""" @property @@ -27,7 +27,7 @@ def details( # pylint: disable=no-self-use, unused-argument def polling_method( # pylint: disable=no-self-use self, - ) -> AsyncPollingMethod[PollingReturnType]: + ) -> AsyncPollingMethod[PollingReturnType_co]: ... def continuation_token(self) -> str: # pylint: disable=no-self-use @@ -38,7 +38,7 @@ def status(self) -> str: # pylint: disable=no-self-use async def result( # pylint: disable=no-self-use, unused-argument self, - ) -> PollingReturnType: + ) -> PollingReturnType_co: ... async def wait(self) -> None: # pylint: disable=no-self-use, unused-argument @@ -48,7 +48,7 @@ def done(self) -> bool: # pylint: disable=no-self-use ... -class AsyncDocumentModelAdministrationClientLROPoller(AsyncLROPoller[PollingReturnType]): +class AsyncDocumentModelAdministrationClientLROPoller(AsyncLROPoller[PollingReturnType_co]): """Custom poller for model build operations. Call `result()` on the poller to return a :class:`~azure.ai.formrecognizer.DocumentModelInfo`. @@ -85,7 +85,7 @@ def details(self) -> Mapping[str, Any]: @classmethod def from_continuation_token( - cls, polling_method: AsyncPollingMethod[PollingReturnType], continuation_token: str, **kwargs: Any + cls, polling_method: AsyncPollingMethod[PollingReturnType_co], continuation_token: str, **kwargs: Any ) -> "AsyncDocumentModelAdministrationClientLROPoller": ( client, diff --git a/sdk/loadtesting/azure-developer-loadtesting/azure/developer/loadtesting/_generated/aio/operations/_patch.py b/sdk/loadtesting/azure-developer-loadtesting/azure/developer/loadtesting/_generated/aio/operations/_patch.py index 644aaddf29d2..ad612d4baeaf 100644 --- a/sdk/loadtesting/azure-developer-loadtesting/azure/developer/loadtesting/_generated/aio/operations/_patch.py +++ b/sdk/loadtesting/azure-developer-loadtesting/azure/developer/loadtesting/_generated/aio/operations/_patch.py @@ -17,7 +17,6 @@ from ._operations import AdministrationOperations as AdministrationOperationsGenerated, JSON from ._operations import TestRunOperations as TestRunOperationsGenerated -PollingReturnType = TypeVar("PollingReturnType") logger = logging.getLogger(__name__) diff --git a/sdk/loadtesting/azure-developer-loadtesting/azure/developer/loadtesting/_generated/operations/_patch.py b/sdk/loadtesting/azure-developer-loadtesting/azure/developer/loadtesting/_generated/operations/_patch.py index c8d1b957598b..f79e48ae6ffe 100644 --- a/sdk/loadtesting/azure-developer-loadtesting/azure/developer/loadtesting/_generated/operations/_patch.py +++ b/sdk/loadtesting/azure-developer-loadtesting/azure/developer/loadtesting/_generated/operations/_patch.py @@ -22,8 +22,6 @@ _SERIALIZER = Serializer() _SERIALIZER.client_side_validation = False -PollingReturnType = TypeVar("PollingReturnType") - logger = logging.getLogger(__name__) diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_lro.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_lro.py index 6993f101431a..2081e2e2f684 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_lro.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_lro.py @@ -25,7 +25,6 @@ _SUCCEEDED = frozenset(["succeeded", "partiallycompleted", "partiallysucceeded"]) -PollingReturnType = TypeVar("PollingReturnType") PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) @@ -266,7 +265,7 @@ def get_continuation_token(self) -> str: return base64.b64encode(pickle.dumps(self._initial_response)).decode('ascii') -class AnalyzeHealthcareEntitiesLROPoller(LROPoller[PollingReturnType]): +class AnalyzeHealthcareEntitiesLROPoller(LROPoller[PollingReturnType_co]): def polling_method(self) -> AnalyzeHealthcareEntitiesLROPollingMethod: """Return the polling method associated to this poller. @@ -456,7 +455,7 @@ def get_continuation_token(self) -> str: return base64.b64encode(pickle.dumps(self._initial_response)).decode('ascii') -class AnalyzeActionsLROPoller(LROPoller[PollingReturnType]): +class AnalyzeActionsLROPoller(LROPoller[PollingReturnType_co]): def polling_method(self) -> AnalyzeActionsLROPollingMethod: """Return the polling method associated to this poller. diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_lro_async.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_lro_async.py index 52bce01c0fd1..4af5111397dc 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_lro_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_lro_async.py @@ -20,7 +20,6 @@ _FAILED = frozenset(["failed"]) _SUCCEEDED = frozenset(["succeeded", "partiallycompleted", "partiallysucceeded"]) -PollingReturnType = TypeVar("PollingReturnType") PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) @@ -219,7 +218,7 @@ def get_continuation_token(self) -> str: return base64.b64encode(pickle.dumps(self._initial_response)).decode('ascii') -class AsyncAnalyzeHealthcareEntitiesLROPoller(AsyncLROPoller[PollingReturnType]): +class AsyncAnalyzeHealthcareEntitiesLROPoller(AsyncLROPoller[PollingReturnType_co]): def polling_method(self) -> AsyncAnalyzeHealthcareEntitiesLROPollingMethod: # type: ignore """Return the polling method associated to this poller. @@ -406,7 +405,7 @@ def get_continuation_token(self) -> str: return base64.b64encode(pickle.dumps(self._initial_response)).decode('ascii') -class AsyncAnalyzeActionsLROPoller(AsyncLROPoller[PollingReturnType]): +class AsyncAnalyzeActionsLROPoller(AsyncLROPoller[PollingReturnType_co]): def polling_method(self) -> AsyncAnalyzeActionsLROPollingMethod: # type: ignore """Return the polling method associated to this poller. diff --git a/sdk/translation/azure-ai-translation-document/azure/ai/translation/document/_polling.py b/sdk/translation/azure-ai-translation-document/azure/ai/translation/document/_polling.py index b60f59fee18a..2739ed5e9b37 100644 --- a/sdk/translation/azure-ai-translation-document/azure/ai/translation/document/_polling.py +++ b/sdk/translation/azure-ai-translation-document/azure/ai/translation/document/_polling.py @@ -28,13 +28,13 @@ ResponseType = Union[HttpResponse, AsyncHttpResponse] PipelineResponseType = PipelineResponse[HttpRequest, ResponseType] -PollingReturnType = TypeVar("PollingReturnType") +PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) _FINISHED = frozenset(["succeeded", "cancelled", "cancelling", "failed"]) _FAILED = frozenset(["validationfailed"]) -class DocumentTranslationLROPoller(LROPoller[PollingReturnType]): +class DocumentTranslationLROPoller(LROPoller[PollingReturnType_co]): """A custom poller implementation for Document Translation. Call `result()` on the poller to return a pageable of :class:`~azure.ai.translation.document.DocumentStatus`.""" diff --git a/sdk/translation/azure-ai-translation-document/azure/ai/translation/document/aio/_async_polling.py b/sdk/translation/azure-ai-translation-document/azure/ai/translation/document/aio/_async_polling.py index 921f142b5db8..b218604df34f 100644 --- a/sdk/translation/azure-ai-translation-document/azure/ai/translation/document/aio/_async_polling.py +++ b/sdk/translation/azure-ai-translation-document/azure/ai/translation/document/aio/_async_polling.py @@ -13,12 +13,12 @@ from .._generated.models import TranslationStatus as _TranslationStatus from .._models import TranslationStatus -PollingReturnType = TypeVar("PollingReturnType") +PollingReturnType_co = TypeVar("PollingReturnType_co", covariant=True) _FINISHED = frozenset(["succeeded", "cancelled", "cancelling", "failed"]) _FAILED = frozenset(["validationfailed"]) -class AsyncDocumentTranslationLROPoller(AsyncLROPoller[PollingReturnType]): +class AsyncDocumentTranslationLROPoller(AsyncLROPoller[PollingReturnType_co]): """An async custom poller implementation for Document Translation. Call `result()` on the poller to return a pageable of :class:`~azure.ai.translation.document.DocumentStatus`."""