1# --------------------------------------------------------------------------
2#
3# Copyright (c) Microsoft Corporation. All rights reserved.
4#
5# The MIT License (MIT)
6#
7# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and associated documentation files (the ""Software""), to
9# deal in the Software without restriction, including without limitation the
10# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11# sell copies of the Software, and to permit persons to whom the Software is
12# furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included in
15# all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23# IN THE SOFTWARE.
24#
25# --------------------------------------------------------------------------
26
27from typing import TypeVar, Generic, Dict, Any, Tuple, List, Optional, overload, TYPE_CHECKING, Union
28
29HTTPResponseType = TypeVar("HTTPResponseType", covariant=True) # pylint: disable=typevar-name-incorrect-variance
30HTTPRequestType = TypeVar("HTTPRequestType", covariant=True) # pylint: disable=typevar-name-incorrect-variance
31
32if TYPE_CHECKING:
33 from .transport import HttpTransport, AsyncHttpTransport
34
35 TransportType = Union[HttpTransport[Any, Any], AsyncHttpTransport[Any, Any]]
36
37
38class PipelineContext(Dict[str, Any]):
39 """A context object carried by the pipeline request and response containers.
40
41 This is transport specific and can contain data persisted between
42 pipeline requests (for example reusing an open connection pool or "session"),
43 as well as used by the SDK developer to carry arbitrary data through
44 the pipeline.
45
46 :param transport: The HTTP transport type.
47 :type transport: ~azure.core.pipeline.transport.HttpTransport or ~azure.core.pipeline.transport.AsyncHttpTransport
48 :param any kwargs: Developer-defined keyword arguments.
49 """
50
51 _PICKLE_CONTEXT = {"deserialized_data"}
52
53 def __init__(
54 self, transport: Optional["TransportType"], **kwargs: Any
55 ) -> None: # pylint: disable=super-init-not-called
56 self.transport: Optional["TransportType"] = transport
57 self.options = kwargs
58 self._protected = ["transport", "options"]
59
60 def __getstate__(self) -> Dict[str, Any]:
61 state = self.__dict__.copy()
62 # Remove the unpicklable entries.
63 del state["transport"]
64 return state
65
66 def __reduce__(self) -> Tuple[Any, ...]:
67 reduced = super(PipelineContext, self).__reduce__()
68 saved_context = {}
69 for key, value in self.items():
70 if key in self._PICKLE_CONTEXT:
71 saved_context[key] = value
72 # 1 is for from __reduce__ spec of pickle (generic args for recreation)
73 # 2 is how dict is implementing __reduce__ (dict specific)
74 # tuple are read-only, we use a list in the meantime
75 reduced_as_list: List[Any] = list(reduced)
76 dict_reduced_result = list(reduced_as_list[1])
77 dict_reduced_result[2] = saved_context
78 reduced_as_list[1] = tuple(dict_reduced_result)
79 return tuple(reduced_as_list)
80
81 def __setstate__(self, state: Dict[str, Any]) -> None:
82 self.__dict__.update(state)
83 # Re-create the unpickable entries
84 self.transport = None
85
86 def __setitem__(self, key: str, item: Any) -> None:
87 # If reloaded from pickle, _protected might not be here until restored by pickle
88 # this explains the hasattr test
89 if hasattr(self, "_protected") and key in self._protected:
90 raise ValueError("Context value {} cannot be overwritten.".format(key))
91 return super(PipelineContext, self).__setitem__(key, item)
92
93 def __delitem__(self, key: str) -> None:
94 if key in self._protected:
95 raise ValueError("Context value {} cannot be deleted.".format(key))
96 return super(PipelineContext, self).__delitem__(key)
97
98 def clear(self) -> None: # pylint: disable=docstring-missing-return, docstring-missing-rtype
99 """Context objects cannot be cleared.
100
101 :raises: TypeError
102 """
103 raise TypeError("Context objects cannot be cleared.")
104
105 def update( # pylint: disable=docstring-missing-return, docstring-missing-rtype, docstring-missing-param
106 self, *args: Any, **kwargs: Any
107 ) -> None:
108 """Context objects cannot be updated.
109
110 :raises: TypeError
111 """
112 raise TypeError("Context objects cannot be updated.")
113
114 @overload
115 def pop(self, __key: str) -> Any: ...
116
117 @overload
118 def pop(self, __key: str, __default: Optional[Any]) -> Any: ...
119
120 def pop(self, *args: Any) -> Any:
121 """Removes specified key and returns the value.
122
123 :param args: The key to remove.
124 :type args: str
125 :return: The value for this key.
126 :rtype: any
127 :raises: ValueError If the key is in the protected list.
128 """
129 if args and args[0] in self._protected:
130 raise ValueError("Context value {} cannot be popped.".format(args[0]))
131 return super(PipelineContext, self).pop(*args)
132
133
134class PipelineRequest(Generic[HTTPRequestType]):
135 """A pipeline request object.
136
137 Container for moving the HttpRequest through the pipeline.
138 Universal for all transports, both synchronous and asynchronous.
139
140 :param http_request: The request object.
141 :type http_request: ~azure.core.pipeline.transport.HttpRequest
142 :param context: Contains the context - data persisted between pipeline requests.
143 :type context: ~azure.core.pipeline.PipelineContext
144 """
145
146 def __init__(self, http_request: HTTPRequestType, context: PipelineContext) -> None:
147 self.http_request = http_request
148 self.context = context
149
150
151class PipelineResponse(Generic[HTTPRequestType, HTTPResponseType]):
152 """A pipeline response object.
153
154 The PipelineResponse interface exposes an HTTP response object as it returns through the pipeline of Policy objects.
155 This ensures that Policy objects have access to the HTTP response.
156
157 This also has a "context" object where policy can put additional fields.
158 Policy SHOULD update the "context" with additional post-processed field if they create them.
159 However, nothing prevents a policy to actually sub-class this class a return it instead of the initial instance.
160
161 :param http_request: The request object.
162 :type http_request: ~azure.core.pipeline.transport.HttpRequest
163 :param http_response: The response object.
164 :type http_response: ~azure.core.pipeline.transport.HttpResponse
165 :param context: Contains the context - data persisted between pipeline requests.
166 :type context: ~azure.core.pipeline.PipelineContext
167 """
168
169 def __init__(
170 self,
171 http_request: HTTPRequestType,
172 http_response: HTTPResponseType,
173 context: PipelineContext,
174 ) -> None:
175 self.http_request = http_request
176 self.http_response = http_response
177 self.context = context
178
179
180from ._base import Pipeline # pylint: disable=wrong-import-position
181from ._base_async import AsyncPipeline # pylint: disable=wrong-import-position
182
183__all__ = [
184 "Pipeline",
185 "PipelineRequest",
186 "PipelineResponse",
187 "PipelineContext",
188 "AsyncPipeline",
189]