corehttp 1.0.0b5__py3-none-any.whl → 1.0.0b7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. corehttp/_version.py +1 -1
  2. corehttp/credentials.py +66 -25
  3. corehttp/exceptions.py +7 -6
  4. corehttp/instrumentation/__init__.py +9 -0
  5. corehttp/instrumentation/tracing/__init__.py +14 -0
  6. corehttp/instrumentation/tracing/_decorator.py +189 -0
  7. corehttp/instrumentation/tracing/_models.py +72 -0
  8. corehttp/instrumentation/tracing/_tracer.py +69 -0
  9. corehttp/instrumentation/tracing/opentelemetry.py +277 -0
  10. corehttp/instrumentation/tracing/utils.py +31 -0
  11. corehttp/paging.py +13 -0
  12. corehttp/rest/_aiohttp.py +21 -9
  13. corehttp/rest/_http_response_impl.py +9 -15
  14. corehttp/rest/_http_response_impl_async.py +2 -0
  15. corehttp/rest/_httpx.py +9 -9
  16. corehttp/rest/_requests_basic.py +17 -10
  17. corehttp/rest/_rest_py3.py +6 -10
  18. corehttp/runtime/pipeline/__init__.py +5 -9
  19. corehttp/runtime/pipeline/_base.py +3 -2
  20. corehttp/runtime/pipeline/_base_async.py +6 -8
  21. corehttp/runtime/pipeline/_tools.py +18 -2
  22. corehttp/runtime/pipeline/_tools_async.py +2 -4
  23. corehttp/runtime/policies/__init__.py +2 -0
  24. corehttp/runtime/policies/_authentication.py +76 -24
  25. corehttp/runtime/policies/_authentication_async.py +66 -21
  26. corehttp/runtime/policies/_distributed_tracing.py +169 -0
  27. corehttp/runtime/policies/_retry.py +8 -12
  28. corehttp/runtime/policies/_retry_async.py +5 -9
  29. corehttp/runtime/policies/_universal.py +15 -11
  30. corehttp/serialization.py +237 -3
  31. corehttp/settings.py +59 -0
  32. corehttp/transport/_base.py +1 -3
  33. corehttp/transport/_base_async.py +1 -3
  34. corehttp/transport/aiohttp/_aiohttp.py +41 -16
  35. corehttp/transport/requests/_bigger_block_size_http_adapters.py +1 -1
  36. corehttp/transport/requests/_requests_basic.py +33 -18
  37. corehttp/utils/_enum_meta.py +1 -1
  38. corehttp/utils/_utils.py +2 -1
  39. corehttp-1.0.0b7.dist-info/METADATA +196 -0
  40. corehttp-1.0.0b7.dist-info/RECORD +61 -0
  41. {corehttp-1.0.0b5.dist-info → corehttp-1.0.0b7.dist-info}/WHEEL +1 -1
  42. corehttp-1.0.0b5.dist-info/METADATA +0 -132
  43. corehttp-1.0.0b5.dist-info/RECORD +0 -52
  44. {corehttp-1.0.0b5.dist-info → corehttp-1.0.0b7.dist-info/licenses}/LICENSE +0 -0
  45. {corehttp-1.0.0b5.dist-info → corehttp-1.0.0b7.dist-info}/top_level.txt +0 -0
corehttp/_version.py CHANGED
@@ -9,4 +9,4 @@
9
9
  # regenerated.
10
10
  # --------------------------------------------------------------------------
11
11
 
12
- VERSION = "1.0.0b5"
12
+ VERSION = "1.0.0b7"
corehttp/credentials.py CHANGED
@@ -5,38 +5,74 @@
5
5
  # -------------------------------------------------------------------------
6
6
  from __future__ import annotations
7
7
  from types import TracebackType
8
- from typing import Any, NamedTuple, Optional, AsyncContextManager, Type
8
+ from typing import NamedTuple, Optional, AsyncContextManager, Type, TypedDict, ContextManager
9
9
  from typing_extensions import Protocol, runtime_checkable
10
10
 
11
11
 
12
- class AccessToken(NamedTuple):
13
- """Represents an OAuth access token."""
12
+ class AccessTokenInfo:
13
+ """Information about an OAuth access token.
14
+
15
+ :param str token: The token string.
16
+ :param int expires_on: The token's expiration time in Unix time.
17
+ :keyword str token_type: The type of access token. Defaults to 'Bearer'.
18
+ :keyword int refresh_on: Specifies the time, in Unix time, when the cached token should be proactively
19
+ refreshed. Optional.
20
+ """
14
21
 
15
22
  token: str
23
+ """The token string."""
16
24
  expires_on: int
25
+ """The token's expiration time in Unix time."""
26
+ token_type: str
27
+ """The type of access token."""
28
+ refresh_on: Optional[int]
29
+ """Specifies the time, in Unix time, when the cached token should be proactively refreshed. Optional."""
30
+
31
+ def __init__(
32
+ self, token: str, expires_on: int, *, token_type: str = "Bearer", refresh_on: Optional[int] = None
33
+ ) -> None:
34
+ self.token = token
35
+ self.expires_on = expires_on
36
+ self.token_type = token_type
37
+ self.refresh_on = refresh_on
17
38
 
39
+ def __repr__(self) -> str:
40
+ return "AccessTokenInfo(token='{}', expires_on={}, token_type='{}', refresh_on={})".format(
41
+ self.token, self.expires_on, self.token_type, self.refresh_on
42
+ )
18
43
 
19
- AccessToken.token.__doc__ = """The token string."""
20
- AccessToken.expires_on.__doc__ = """The token's expiration time in Unix time."""
21
44
 
45
+ class TokenRequestOptions(TypedDict, total=False):
46
+ """Options to use for access token requests. All parameters are optional."""
22
47
 
23
- @runtime_checkable
24
- class TokenCredential(Protocol):
25
- """Protocol for classes able to provide OAuth tokens."""
48
+ claims: str
49
+ """Additional claims required in the token, such as those returned in a resource provider's claims
50
+ challenge following an authorization failure."""
51
+ tenant_id: str
52
+ """The tenant ID to include in the token request."""
53
+
54
+
55
+ class TokenCredential(Protocol, ContextManager["TokenCredential"]):
56
+ """Protocol for classes able to provide OAuth access tokens."""
26
57
 
27
- def get_token(self, *scopes: str, claims: Optional[str] = None, **kwargs: Any) -> AccessToken:
58
+ def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] = None) -> AccessTokenInfo:
28
59
  """Request an access token for `scopes`.
29
60
 
30
61
  :param str scopes: The type of access needed.
62
+ :keyword options: A dictionary of options for the token request. Unknown options will be ignored. Optional.
63
+ :paramtype options: TokenRequestOptions
31
64
 
32
- :keyword str claims: Additional claims required in the token, such as those returned in a resource
33
- provider's claims challenge following an authorization failure.
65
+ :rtype: AccessTokenInfo
66
+ :return: An AccessTokenInfo instance containing information about the token.
67
+ """
68
+ ...
34
69
 
70
+ def close(self) -> None:
71
+ """Close the credential, releasing any resources it holds.
35
72
 
36
- :rtype: AccessToken
37
- :return: An AccessToken instance containing the token string and its expiration time in Unix time.
73
+ :return: None
74
+ :rtype: None
38
75
  """
39
- ...
40
76
 
41
77
 
42
78
  class ServiceNamedKey(NamedTuple):
@@ -47,10 +83,11 @@ class ServiceNamedKey(NamedTuple):
47
83
 
48
84
 
49
85
  __all__ = [
50
- "AccessToken",
86
+ "AccessTokenInfo",
51
87
  "ServiceKeyCredential",
52
88
  "ServiceNamedKeyCredential",
53
89
  "TokenCredential",
90
+ "TokenRequestOptions",
54
91
  "AsyncTokenCredential",
55
92
  ]
56
93
 
@@ -60,7 +97,7 @@ class ServiceKeyCredential:
60
97
  It provides the ability to update the key without creating a new client.
61
98
 
62
99
  :param str key: The key used to authenticate to a service
63
- :raises: TypeError
100
+ :raises TypeError: If the key is not a string.
64
101
  """
65
102
 
66
103
  def __init__(self, key: str) -> None:
@@ -84,7 +121,8 @@ class ServiceKeyCredential:
84
121
  to update long-lived clients.
85
122
 
86
123
  :param str key: The key used to authenticate to a service
87
- :raises: ValueError or TypeError
124
+ :raises ValueError: If the key is None or empty.
125
+ :raises TypeError: If the key is not a string.
88
126
  """
89
127
  if not key:
90
128
  raise ValueError("The key used for updating can not be None or empty")
@@ -99,7 +137,7 @@ class ServiceNamedKeyCredential:
99
137
 
100
138
  :param str name: The name of the credential used to authenticate to a service.
101
139
  :param str key: The key used to authenticate to a service.
102
- :raises: TypeError
140
+ :raises TypeError: If the name or key is not a string.
103
141
  """
104
142
 
105
143
  def __init__(self, name: str, key: str) -> None:
@@ -134,21 +172,24 @@ class ServiceNamedKeyCredential:
134
172
  class AsyncTokenCredential(Protocol, AsyncContextManager["AsyncTokenCredential"]):
135
173
  """Protocol for classes able to provide OAuth tokens."""
136
174
 
137
- async def get_token(self, *scopes: str, claims: Optional[str] = None, **kwargs: Any) -> AccessToken:
175
+ async def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] = None) -> AccessTokenInfo:
138
176
  """Request an access token for `scopes`.
139
177
 
140
178
  :param str scopes: The type of access needed.
179
+ :keyword options: A dictionary of options for the token request. Unknown options will be ignored. Optional.
180
+ :paramtype options: TokenRequestOptions
141
181
 
142
- :keyword str claims: Additional claims required in the token, such as those returned in a resource
143
- provider's claims challenge following an authorization failure.
144
-
145
- :rtype: AccessToken
146
- :return: An AccessToken instance containing the token string and its expiration time in Unix time.
182
+ :rtype: AccessTokenInfo
183
+ :return: An AccessTokenInfo instance containing the token string and its expiration time in Unix time.
147
184
  """
148
185
  ...
149
186
 
150
187
  async def close(self) -> None:
151
- pass
188
+ """Close the credential, releasing any resources.
189
+
190
+ :return: None
191
+ :rtype: None
192
+ """
152
193
 
153
194
  async def __aexit__(
154
195
  self,
corehttp/exceptions.py CHANGED
@@ -72,15 +72,12 @@ class _HttpResponseCommonAPI(Protocol):
72
72
  """
73
73
 
74
74
  @property
75
- def reason(self) -> Optional[str]:
76
- ...
75
+ def reason(self) -> Optional[str]: ...
77
76
 
78
77
  @property
79
- def status_code(self) -> Optional[int]:
80
- ...
78
+ def status_code(self) -> Optional[int]: ...
81
79
 
82
- def text(self) -> str:
83
- ...
80
+ def text(self) -> str: ...
84
81
 
85
82
  @property
86
83
  def request(self) -> object: # object as type, since all we need is str() on it
@@ -190,6 +187,8 @@ class HttpResponseError(BaseError):
190
187
  :vartype status_code: int
191
188
  :ivar response: The response that triggered the exception.
192
189
  :vartype response: ~corehttp.rest.HttpResponse or ~corehttp.rest.AsyncHttpResponse
190
+ :ivar model: The response body model
191
+ :vartype model: Any
193
192
  """
194
193
 
195
194
  def __init__(
@@ -203,6 +202,8 @@ class HttpResponseError(BaseError):
203
202
  self.reason = response.reason
204
203
  self.status_code = response.status_code
205
204
 
205
+ self.model: Optional[Any] = kwargs.pop("model", None)
206
+
206
207
  # By priority, message is:
207
208
  # - parameter "message", OR
208
209
  # - generic message using "reason"
@@ -0,0 +1,9 @@
1
+ # ------------------------------------
2
+ # Copyright (c) Microsoft Corporation.
3
+ # Licensed under the MIT License.
4
+ # ------------------------------------
5
+ from .tracing._tracer import get_tracer
6
+
7
+ __all__ = [
8
+ "get_tracer",
9
+ ]
@@ -0,0 +1,14 @@
1
+ # ------------------------------------
2
+ # Copyright (c) Microsoft Corporation.
3
+ # Licensed under the MIT License.
4
+ # ------------------------------------
5
+ from ._models import SpanKind, Link, TracingOptions
6
+ from ._decorator import distributed_trace, distributed_trace_async
7
+
8
+ __all__ = [
9
+ "Link",
10
+ "SpanKind",
11
+ "TracingOptions",
12
+ "distributed_trace",
13
+ "distributed_trace_async",
14
+ ]
@@ -0,0 +1,189 @@
1
+ # ------------------------------------
2
+ # Copyright (c) Microsoft Corporation.
3
+ # Licensed under the MIT License.
4
+ # ------------------------------------
5
+ """The decorator to apply if you want the given method traced."""
6
+ from contextvars import ContextVar
7
+ import functools
8
+ from typing import Awaitable, Any, TypeVar, overload, Optional, Callable, TYPE_CHECKING
9
+ from typing_extensions import ParamSpec
10
+
11
+ from ._models import SpanKind
12
+ from ._tracer import get_tracer
13
+ from ...settings import settings
14
+
15
+ if TYPE_CHECKING:
16
+ from ._models import TracingOptions
17
+
18
+
19
+ P = ParamSpec("P")
20
+ T = TypeVar("T")
21
+
22
+
23
+ # This context variable is used to determine if we are already in the span context of a decorated function.
24
+ _in_span_context = ContextVar("in_span_context", default=False)
25
+
26
+
27
+ @overload
28
+ def distributed_trace(__func: Callable[P, T]) -> Callable[P, T]:
29
+ pass
30
+
31
+
32
+ @overload
33
+ def distributed_trace(**kwargs: Any) -> Callable[[Callable[P, T]], Callable[P, T]]:
34
+ pass
35
+
36
+
37
+ def distributed_trace(__func: Optional[Callable[P, T]] = None, **kwargs: Any) -> Any: # pylint: disable=unused-argument
38
+ """Decorator to apply to an SDK method to have it traced automatically.
39
+
40
+ Span will use the method's qualified name.
41
+
42
+ Note: This decorator SHOULD NOT be used by application developers. It's intended to be called by client
43
+ libraries only. Application developers should use OpenTelemetry directly to instrument their applications.
44
+
45
+ :param callable __func: A function to decorate
46
+
47
+ :return: The decorated function
48
+ :rtype: Any
49
+ """
50
+
51
+ def decorator(func: Callable[P, T]) -> Callable[P, T]:
52
+
53
+ @functools.wraps(func)
54
+ def wrapper_use_tracer(*args: Any, **kwargs: Any) -> T:
55
+ # If we are already in the span context of a decorated function, don't trace.
56
+ if _in_span_context.get():
57
+ return func(*args, **kwargs)
58
+
59
+ # This will be popped in the pipeline or transport runner.
60
+ tracing_options: TracingOptions = kwargs.get("tracing_options", {})
61
+
62
+ # User can explicitly disable tracing for this call
63
+ user_enabled = tracing_options.get("enabled")
64
+ if user_enabled is False:
65
+ return func(*args, **kwargs)
66
+
67
+ # If tracing is disabled globally and user didn't explicitly enable it, don't trace.
68
+ if not settings.tracing_enabled and user_enabled is None:
69
+ return func(*args, **kwargs)
70
+
71
+ config = {}
72
+ if args and hasattr(args[0], "_instrumentation_config"):
73
+ config = args[0]._instrumentation_config # pylint: disable=protected-access
74
+
75
+ method_tracer = get_tracer(
76
+ library_name=config.get("library_name"),
77
+ library_version=config.get("library_version"),
78
+ schema_url=config.get("schema_url"),
79
+ attributes=config.get("attributes"),
80
+ )
81
+ if not method_tracer:
82
+ return func(*args, **kwargs)
83
+
84
+ name = func.__qualname__
85
+ span_suppression_token = _in_span_context.set(True)
86
+ try:
87
+ with method_tracer.start_as_current_span(
88
+ name=name,
89
+ kind=SpanKind.INTERNAL,
90
+ attributes=tracing_options.get("attributes"),
91
+ ) as span:
92
+ try:
93
+ return func(*args, **kwargs)
94
+ except Exception as err: # pylint: disable=broad-except
95
+ ex_type = type(err)
96
+ module = ex_type.__module__ if ex_type.__module__ != "builtins" else ""
97
+ error_type = f"{module}.{ex_type.__qualname__}" if module else ex_type.__qualname__
98
+ span.set_attribute("error.type", error_type)
99
+ raise
100
+ finally:
101
+ _in_span_context.reset(span_suppression_token)
102
+
103
+ return wrapper_use_tracer
104
+
105
+ return decorator if __func is None else decorator(__func)
106
+
107
+
108
+ @overload
109
+ def distributed_trace_async(__func: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
110
+ pass
111
+
112
+
113
+ @overload
114
+ def distributed_trace_async(**kwargs: Any) -> Callable[[Callable[P, Awaitable[T]]], Callable[P, Awaitable[T]]]:
115
+ pass
116
+
117
+
118
+ def distributed_trace_async( # pylint: disable=unused-argument
119
+ __func: Optional[Callable[P, Awaitable[T]]] = None,
120
+ **kwargs: Any,
121
+ ) -> Any:
122
+ """Decorator to apply to an SDK method to have it traced automatically.
123
+
124
+ Span will use the method's qualified name.
125
+
126
+ Note: This decorator SHOULD NOT be used by application developers. It's intended to be called by client
127
+ libraries only. Application developers should use OpenTelemetry directly to instrument their applications.
128
+
129
+ :param callable __func: A function to decorate
130
+
131
+ :return: The decorated function
132
+ :rtype: Any
133
+ """
134
+
135
+ def decorator(func: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
136
+
137
+ @functools.wraps(func)
138
+ async def wrapper_use_tracer(*args: Any, **kwargs: Any) -> T:
139
+ # If we are already in the span context of a decorated function, don't trace.
140
+ if _in_span_context.get():
141
+ return await func(*args, **kwargs)
142
+
143
+ # This will be popped in the pipeline or transport runner.
144
+ tracing_options: TracingOptions = kwargs.get("tracing_options", {})
145
+
146
+ # User can explicitly disable tracing for this call
147
+ user_enabled = tracing_options.get("enabled")
148
+ if user_enabled is False:
149
+ return await func(*args, **kwargs)
150
+
151
+ # If tracing is disabled globally and user didn't explicitly enable it, don't trace.
152
+ if not settings.tracing_enabled and user_enabled is None:
153
+ return await func(*args, **kwargs)
154
+
155
+ config = {}
156
+ if args and hasattr(args[0], "_instrumentation_config"):
157
+ config = args[0]._instrumentation_config # pylint: disable=protected-access
158
+
159
+ method_tracer = get_tracer(
160
+ library_name=config.get("library_name"),
161
+ library_version=config.get("library_version"),
162
+ schema_url=config.get("schema_url"),
163
+ attributes=config.get("attributes"),
164
+ )
165
+ if not method_tracer:
166
+ return await func(*args, **kwargs)
167
+
168
+ name = func.__qualname__
169
+ span_suppression_token = _in_span_context.set(True)
170
+ try:
171
+ with method_tracer.start_as_current_span(
172
+ name=name,
173
+ kind=SpanKind.INTERNAL,
174
+ attributes=tracing_options.get("attributes"),
175
+ ) as span:
176
+ try:
177
+ return await func(*args, **kwargs)
178
+ except Exception as err: # pylint: disable=broad-except
179
+ ex_type = type(err)
180
+ module = ex_type.__module__ if ex_type.__module__ != "builtins" else ""
181
+ error_type = f"{module}.{ex_type.__qualname__}" if module else ex_type.__qualname__
182
+ span.set_attribute("error.type", error_type)
183
+ raise
184
+ finally:
185
+ _in_span_context.reset(span_suppression_token)
186
+
187
+ return wrapper_use_tracer
188
+
189
+ return decorator if __func is None else decorator(__func)
@@ -0,0 +1,72 @@
1
+ # ------------------------------------
2
+ # Copyright (c) Microsoft Corporation.
3
+ # Licensed under the MIT License.
4
+ # ------------------------------------
5
+ from __future__ import annotations
6
+ from enum import Enum
7
+ from typing import Dict, Mapping, Optional, Union, Sequence, TypedDict
8
+
9
+ from ...utils import CaseInsensitiveEnumMeta
10
+
11
+
12
+ AttributeValue = Union[
13
+ str,
14
+ bool,
15
+ int,
16
+ float,
17
+ Sequence[str],
18
+ Sequence[bool],
19
+ Sequence[int],
20
+ Sequence[float],
21
+ ]
22
+ Attributes = Mapping[str, AttributeValue]
23
+
24
+
25
+ class SpanKind(Enum, metaclass=CaseInsensitiveEnumMeta):
26
+ """Describes the role or kind of a span within a distributed trace.
27
+
28
+ This helps to categorize spans based on their relationship to other spans and the type
29
+ of operation they represent.
30
+ """
31
+
32
+ UNSPECIFIED = 1
33
+ """Unspecified span kind."""
34
+
35
+ SERVER = 2
36
+ """Indicates that the span describes an operation that handles a remote request."""
37
+
38
+ CLIENT = 3
39
+ """Indicates that the span describes a request to some remote service."""
40
+
41
+ PRODUCER = 4
42
+ """Indicates that the span describes the initiation or scheduling of a local or remote operation."""
43
+
44
+ CONSUMER = 5
45
+ """Indicates that the span represents the processing of an operation initiated by a producer."""
46
+
47
+ INTERNAL = 6
48
+ """Indicates that the span is used internally in the application."""
49
+
50
+
51
+ class Link:
52
+ """Represents a reference from one span to another span.
53
+
54
+ :param headers: A dictionary of the request header as key value pairs.
55
+ :type headers: dict
56
+ :param attributes: Any additional attributes that should be added to link
57
+ :type attributes: dict
58
+ """
59
+
60
+ def __init__(self, headers: Dict[str, str], attributes: Optional[Attributes] = None) -> None:
61
+ self.headers = headers
62
+ self.attributes = attributes
63
+
64
+
65
+ class TracingOptions(TypedDict, total=False):
66
+ """Options to configure tracing behavior for operations."""
67
+
68
+ enabled: bool
69
+ """Whether tracing is enabled for the operation. By default, if the global setting is enabled, tracing is
70
+ enabled for all operations. This option can be used to override the global setting for a specific operation."""
71
+ attributes: Attributes
72
+ """Attributes to include in the spans emitted for the operation."""
@@ -0,0 +1,69 @@
1
+ # ------------------------------------
2
+ # Copyright (c) Microsoft Corporation.
3
+ # Licensed under the MIT License.
4
+ # ------------------------------------
5
+ from typing import Optional, Union, Mapping, TYPE_CHECKING
6
+ from functools import lru_cache
7
+
8
+ if TYPE_CHECKING:
9
+ try:
10
+ from .opentelemetry import OpenTelemetryTracer
11
+ except ImportError:
12
+ pass
13
+
14
+
15
+ def _get_tracer_impl():
16
+ # Check if OpenTelemetry is available/installed.
17
+ try:
18
+ from .opentelemetry import OpenTelemetryTracer
19
+
20
+ return OpenTelemetryTracer
21
+ except ImportError:
22
+ return None
23
+
24
+
25
+ @lru_cache
26
+ def _get_tracer_cached(
27
+ library_name: Optional[str],
28
+ library_version: Optional[str],
29
+ schema_url: Optional[str],
30
+ attributes_key: Optional[frozenset],
31
+ ) -> Optional["OpenTelemetryTracer"]:
32
+ tracer_impl = _get_tracer_impl()
33
+ if tracer_impl:
34
+ # Convert attributes_key back to dict if needed
35
+ attributes = dict(attributes_key) if attributes_key else None
36
+ return tracer_impl(
37
+ library_name=library_name,
38
+ library_version=library_version,
39
+ schema_url=schema_url,
40
+ attributes=attributes,
41
+ )
42
+ return None
43
+
44
+
45
+ def get_tracer(
46
+ *,
47
+ library_name: Optional[str] = None,
48
+ library_version: Optional[str] = None,
49
+ schema_url: Optional[str] = None,
50
+ attributes: Optional[Mapping[str, Union[str, bool, int, float]]] = None,
51
+ ) -> Optional["OpenTelemetryTracer"]:
52
+ """Get the OpenTelemetry tracer instance if available.
53
+
54
+ If OpenTelemetry is not available, this method will return None. This method caches
55
+ the tracer instance for each unique set of parameters.
56
+
57
+ :keyword library_name: The name of the library to use in the tracer.
58
+ :paramtype library_name: str
59
+ :keyword library_version: The version of the library to use in the tracer.
60
+ :paramtype library_version: str
61
+ :keyword schema_url: Specifies the Schema URL of the emitted spans.
62
+ :paramtype schema_url: str
63
+ :keyword attributes: Attributes to add to the emitted spans.
64
+ :paramtype attributes: Mapping[str, Union[str, bool, int, float]]
65
+ :return: The OpenTelemetry tracer instance if available.
66
+ :rtype: Optional[~corehttp.instrumentation.tracing.opentelemetry.OpenTelemetryTracer]
67
+ """
68
+ attributes_key = frozenset(attributes.items()) if attributes else None
69
+ return _get_tracer_cached(library_name, library_version, schema_url, attributes_key)