Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/grpc/aio/_metadata.py: 36%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright 2020 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Implementation of the metadata abstraction for gRPC Asyncio Python."""
15from __future__ import annotations
17from collections import OrderedDict
18from collections.abc import (
19 Collection,
20 ItemsView,
21 Iterable,
22 Iterator,
23 KeysView,
24 Sequence,
25 ValuesView,
26)
27from typing import Any, Optional, Tuple, Union
29from typing_extensions import Self
31MetadataKey = str
32MetadataValue = Union[str, bytes]
33MetadatumType = Tuple[MetadataKey, MetadataValue]
34MetadataType = Union["Metadata", Sequence[MetadatumType]]
37class Metadata(Collection[MetadatumType]): # noqa: PLW1641
38 """Metadata abstraction for the asynchronous calls and interceptors.
40 The metadata is a mapping from str -> List[str]
42 Traits
43 * Multiple entries are allowed for the same key
44 * The order of the values by key is preserved
45 * Getting by an element by key, retrieves the first mapped value
46 * Supports an immutable view of the data
47 * Allows partial mutation on the data without recreating the new object from scratch.
48 """
50 _metadata: OrderedDict[MetadataKey, list[MetadataValue]]
52 def __init__(self, *args: MetadatumType) -> None:
53 self._metadata = OrderedDict()
54 for md_key, md_value in args:
55 self.add(md_key, md_value)
57 @classmethod
58 def from_tuple(cls, raw_metadata: Iterable[MetadatumType]) -> Self:
59 # Note: We unintentionally support non-tuple arguments here. We plan
60 # to emit a DeprecationWarning when a non-tuple type is used.
61 if raw_metadata:
62 return cls(*raw_metadata)
63 return cls()
65 @classmethod
66 def _create(
67 cls,
68 raw_metadata: Union[None, Self, Iterable[MetadatumType]],
69 ) -> Self:
70 # TODO(asheshvidyut): Make this method public and encourage people to use it instead
71 # of `from_tuple` to create metadata from non-tuple types.
72 if raw_metadata is None:
73 return cls()
74 if isinstance(raw_metadata, cls):
75 return raw_metadata
76 if raw_metadata:
77 return cls(*raw_metadata)
78 return cls()
80 def add(self, key: MetadataKey, value: MetadataValue) -> None:
81 self._metadata.setdefault(key, [])
82 self._metadata[key].append(value)
84 def __len__(self) -> int:
85 """Return the total number of elements that there are in the metadata,
86 including multiple values for the same key.
87 """
88 return sum(map(len, self._metadata.values()))
90 def __getitem__(self, key: MetadataKey) -> MetadataValue:
91 """When calling <metadata>[<key>], the first element of all those
92 mapped for <key> is returned.
93 """
94 try:
95 return self._metadata[key][0]
96 except (ValueError, IndexError) as e:
97 error_msg = f"{key!r}"
98 raise KeyError(error_msg) from e
100 def __setitem__(self, key: MetadataKey, value: MetadataValue) -> None:
101 """Calling metadata[<key>] = <value>
102 Maps <value> to the first instance of <key>.
103 """
104 if key not in self:
105 self._metadata[key] = [value]
106 else:
107 current_values = self.get_all(key)
108 self._metadata[key] = [value, *current_values[1:]]
110 def __delitem__(self, key: MetadataKey) -> None:
111 """``del metadata[<key>]`` deletes the first mapping for <key>."""
112 # get_all() returns the live list stored in _metadata, not a copy,
113 # so mutating it in place here also updates _metadata directly.
114 values = self.get_all(key)
115 if not values:
116 raise KeyError(repr(key))
117 del values[0]
118 if not values:
119 del self._metadata[key]
121 def delete_all(self, key: MetadataKey) -> None:
122 """Delete all mappings for <key>."""
123 del self._metadata[key]
125 def __iter__(self) -> Iterator[Tuple[MetadataKey, MetadataValue]]:
126 for key, values in self._metadata.items():
127 for value in values:
128 yield (key, value)
130 def keys(self) -> KeysView[MetadataKey]:
131 return KeysView(self._metadata)
133 def values(self) -> ValuesView[list[MetadataValue]]:
134 return ValuesView(self._metadata)
136 def items(self) -> ItemsView[MetadataKey, list[MetadataValue]]:
137 return ItemsView(self._metadata)
139 def get(
140 self, key: MetadataKey, default: Optional[MetadataValue] = None
141 ) -> Optional[MetadataValue]:
142 try:
143 return self[key]
144 except KeyError:
145 return default
147 def get_all(self, key: MetadataKey) -> list[MetadataValue]:
148 """For compatibility with other Metadata abstraction objects (like in Java),
149 this would return all items under the desired <key>.
150 """
151 return self._metadata.get(key, [])
153 def set_all(self, key: MetadataKey, values: list[MetadataValue]) -> None:
154 self._metadata[key] = values
156 def __contains__(self, key: object) -> bool:
157 if not isinstance(key, MetadataKey):
158 return False
159 return key in self._metadata
161 def __eq__(self, other: object) -> bool:
162 if isinstance(other, self.__class__):
163 return self._metadata == other._metadata
164 if isinstance(other, tuple):
165 return tuple(self) == other
166 return NotImplemented # pytype: disable=bad-return-type
168 def __add__(self, other: Any) -> "Metadata":
169 if isinstance(other, self.__class__):
170 return Metadata(*(tuple(self) + tuple(other)))
171 if isinstance(other, tuple):
172 return Metadata(*(tuple(self) + other))
173 return NotImplemented # pytype: disable=bad-return-type
175 def __repr__(self) -> str:
176 view = tuple(self)
177 return "{0}({1!r})".format(self.__class__.__name__, view)