betterproto2-compiler 0.0.3__py3-none-any.whl → 0.1.1__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.
- betterproto2_compiler/compile/importing.py +11 -15
- betterproto2_compiler/lib/google/protobuf/__init__.py +0 -1
- betterproto2_compiler/lib/google/protobuf/compiler/__init__.py +229 -1
- betterproto2_compiler/plugin/compiler.py +5 -2
- betterproto2_compiler/plugin/models.py +83 -81
- betterproto2_compiler/plugin/module_validation.py +2 -7
- betterproto2_compiler/plugin/parser.py +30 -34
- betterproto2_compiler/plugin/typing_compiler.py +8 -12
- betterproto2_compiler/templates/header.py.j2 +2 -0
- betterproto2_compiler/templates/template.py.j2 +8 -1
- {betterproto2_compiler-0.0.3.dist-info → betterproto2_compiler-0.1.1.dist-info}/METADATA +3 -3
- betterproto2_compiler-0.1.1.dist-info/RECORD +26 -0
- {betterproto2_compiler-0.0.3.dist-info → betterproto2_compiler-0.1.1.dist-info}/WHEEL +1 -1
- betterproto2_compiler-0.1.1.dist-info/entry_points.txt +3 -0
- betterproto2_compiler/_types.py +0 -13
- betterproto2_compiler/enum.py +0 -180
- betterproto2_compiler/grpc/__init__.py +0 -0
- betterproto2_compiler/grpc/grpclib_client.py +0 -172
- betterproto2_compiler/grpc/grpclib_server.py +0 -32
- betterproto2_compiler/grpc/util/__init__.py +0 -0
- betterproto2_compiler/grpc/util/async_channel.py +0 -190
- betterproto2_compiler/lib/pydantic/__init__.py +0 -0
- betterproto2_compiler/lib/pydantic/google/__init__.py +0 -0
- betterproto2_compiler/lib/pydantic/google/protobuf/__init__.py +0 -2690
- betterproto2_compiler/lib/pydantic/google/protobuf/compiler/__init__.py +0 -209
- betterproto2_compiler/lib/std/__init__.py +0 -0
- betterproto2_compiler/lib/std/google/__init__.py +0 -0
- betterproto2_compiler/lib/std/google/protobuf/__init__.py +0 -2521
- betterproto2_compiler/lib/std/google/protobuf/compiler/__init__.py +0 -197
- betterproto2_compiler-0.0.3.dist-info/RECORD +0 -41
- betterproto2_compiler-0.0.3.dist-info/entry_points.txt +0 -3
- {betterproto2_compiler-0.0.3.dist-info → betterproto2_compiler-0.1.1.dist-info}/LICENSE.md +0 -0
betterproto2_compiler/enum.py
DELETED
@@ -1,180 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from enum import (
|
4
|
-
EnumMeta,
|
5
|
-
IntEnum,
|
6
|
-
)
|
7
|
-
from types import MappingProxyType
|
8
|
-
from typing import (
|
9
|
-
TYPE_CHECKING,
|
10
|
-
Any,
|
11
|
-
Dict,
|
12
|
-
Optional,
|
13
|
-
Tuple,
|
14
|
-
)
|
15
|
-
|
16
|
-
if TYPE_CHECKING:
|
17
|
-
from collections.abc import (
|
18
|
-
Generator,
|
19
|
-
Mapping,
|
20
|
-
)
|
21
|
-
|
22
|
-
from typing_extensions import (
|
23
|
-
Never,
|
24
|
-
Self,
|
25
|
-
)
|
26
|
-
|
27
|
-
|
28
|
-
def _is_descriptor(obj: object) -> bool:
|
29
|
-
return hasattr(obj, "__get__") or hasattr(obj, "__set__") or hasattr(obj, "__delete__")
|
30
|
-
|
31
|
-
|
32
|
-
class EnumType(EnumMeta if TYPE_CHECKING else type):
|
33
|
-
_value_map_: Mapping[int, Enum]
|
34
|
-
_member_map_: Mapping[str, Enum]
|
35
|
-
|
36
|
-
def __new__(mcs, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any]) -> Self:
|
37
|
-
value_map = {}
|
38
|
-
member_map = {}
|
39
|
-
|
40
|
-
new_mcs = type(
|
41
|
-
f"{name}Type",
|
42
|
-
tuple(
|
43
|
-
dict.fromkeys([base.__class__ for base in bases if base.__class__ is not type] + [EnumType, type])
|
44
|
-
), # reorder the bases so EnumType and type are last to avoid conflicts
|
45
|
-
{"_value_map_": value_map, "_member_map_": member_map},
|
46
|
-
)
|
47
|
-
|
48
|
-
members = {
|
49
|
-
name: value for name, value in namespace.items() if not _is_descriptor(value) and not name.startswith("__")
|
50
|
-
}
|
51
|
-
|
52
|
-
cls = type.__new__(
|
53
|
-
new_mcs,
|
54
|
-
name,
|
55
|
-
bases,
|
56
|
-
{key: value for key, value in namespace.items() if key not in members},
|
57
|
-
)
|
58
|
-
# this allows us to disallow member access from other members as
|
59
|
-
# members become proper class variables
|
60
|
-
|
61
|
-
for name, value in members.items():
|
62
|
-
member = value_map.get(value)
|
63
|
-
if member is None:
|
64
|
-
member = cls.__new__(cls, name=name, value=value) # type: ignore
|
65
|
-
value_map[value] = member
|
66
|
-
member_map[name] = member
|
67
|
-
type.__setattr__(new_mcs, name, member)
|
68
|
-
|
69
|
-
return cls
|
70
|
-
|
71
|
-
if not TYPE_CHECKING:
|
72
|
-
|
73
|
-
def __call__(cls, value: int) -> Enum:
|
74
|
-
try:
|
75
|
-
return cls._value_map_[value]
|
76
|
-
except (KeyError, TypeError):
|
77
|
-
raise ValueError(f"{value!r} is not a valid {cls.__name__}") from None
|
78
|
-
|
79
|
-
def __iter__(cls) -> Generator[Enum, None, None]:
|
80
|
-
yield from cls._member_map_.values()
|
81
|
-
|
82
|
-
def __reversed__(cls) -> Generator[Enum, None, None]:
|
83
|
-
yield from reversed(cls._member_map_.values())
|
84
|
-
|
85
|
-
def __getitem__(cls, key: str) -> Enum:
|
86
|
-
return cls._member_map_[key]
|
87
|
-
|
88
|
-
@property
|
89
|
-
def __members__(cls) -> MappingProxyType[str, Enum]:
|
90
|
-
return MappingProxyType(cls._member_map_)
|
91
|
-
|
92
|
-
def __repr__(cls) -> str:
|
93
|
-
return f"<enum {cls.__name__!r}>"
|
94
|
-
|
95
|
-
def __len__(cls) -> int:
|
96
|
-
return len(cls._member_map_)
|
97
|
-
|
98
|
-
def __setattr__(cls, name: str, value: Any) -> Never:
|
99
|
-
raise AttributeError(f"{cls.__name__}: cannot reassign Enum members.")
|
100
|
-
|
101
|
-
def __delattr__(cls, name: str) -> Never:
|
102
|
-
raise AttributeError(f"{cls.__name__}: cannot delete Enum members.")
|
103
|
-
|
104
|
-
def __contains__(cls, member: object) -> bool:
|
105
|
-
return isinstance(member, cls) and member.name in cls._member_map_
|
106
|
-
|
107
|
-
|
108
|
-
class Enum(IntEnum if TYPE_CHECKING else int, metaclass=EnumType):
|
109
|
-
"""
|
110
|
-
The base class for protobuf enumerations, all generated enumerations will
|
111
|
-
inherit from this. Emulates `enum.IntEnum`.
|
112
|
-
"""
|
113
|
-
|
114
|
-
name: Optional[str]
|
115
|
-
value: int
|
116
|
-
|
117
|
-
if not TYPE_CHECKING:
|
118
|
-
|
119
|
-
def __new__(cls, *, name: Optional[str], value: int) -> Self:
|
120
|
-
self = super().__new__(cls, value)
|
121
|
-
super().__setattr__(self, "name", name)
|
122
|
-
super().__setattr__(self, "value", value)
|
123
|
-
return self
|
124
|
-
|
125
|
-
def __str__(self) -> str:
|
126
|
-
return self.name or "None"
|
127
|
-
|
128
|
-
def __repr__(self) -> str:
|
129
|
-
return f"{self.__class__.__name__}.{self.name}"
|
130
|
-
|
131
|
-
def __setattr__(self, key: str, value: Any) -> Never:
|
132
|
-
raise AttributeError(f"{self.__class__.__name__} Cannot reassign a member's attributes.")
|
133
|
-
|
134
|
-
def __delattr__(self, item: Any) -> Never:
|
135
|
-
raise AttributeError(f"{self.__class__.__name__} Cannot delete a member's attributes.")
|
136
|
-
|
137
|
-
def __copy__(self) -> Self:
|
138
|
-
return self
|
139
|
-
|
140
|
-
def __deepcopy__(self, memo: Any) -> Self:
|
141
|
-
return self
|
142
|
-
|
143
|
-
@classmethod
|
144
|
-
def try_value(cls, value: int = 0) -> Self:
|
145
|
-
"""Return the value which corresponds to the value.
|
146
|
-
|
147
|
-
Parameters
|
148
|
-
-----------
|
149
|
-
value: :class:`int`
|
150
|
-
The value of the enum member to get.
|
151
|
-
|
152
|
-
Returns
|
153
|
-
-------
|
154
|
-
:class:`Enum`
|
155
|
-
The corresponding member or a new instance of the enum if
|
156
|
-
``value`` isn't actually a member.
|
157
|
-
"""
|
158
|
-
try:
|
159
|
-
return cls._value_map_[value]
|
160
|
-
except (KeyError, TypeError):
|
161
|
-
return cls.__new__(cls, name=None, value=value)
|
162
|
-
|
163
|
-
@classmethod
|
164
|
-
def from_string(cls, name: str) -> Self:
|
165
|
-
"""Return the value which corresponds to the string name.
|
166
|
-
|
167
|
-
Parameters
|
168
|
-
-----------
|
169
|
-
name: :class:`str`
|
170
|
-
The name of the enum member to get.
|
171
|
-
|
172
|
-
Raises
|
173
|
-
-------
|
174
|
-
:exc:`ValueError`
|
175
|
-
The member was not found in the Enum.
|
176
|
-
"""
|
177
|
-
try:
|
178
|
-
return cls._member_map_[name]
|
179
|
-
except KeyError as e:
|
180
|
-
raise ValueError(f"Unknown value {name} for enum {cls.__name__}") from e
|
File without changes
|
@@ -1,172 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
from abc import ABC
|
3
|
-
from typing import (
|
4
|
-
TYPE_CHECKING,
|
5
|
-
AsyncIterable,
|
6
|
-
AsyncIterator,
|
7
|
-
Collection,
|
8
|
-
Iterable,
|
9
|
-
Mapping,
|
10
|
-
Optional,
|
11
|
-
Tuple,
|
12
|
-
Type,
|
13
|
-
Union,
|
14
|
-
)
|
15
|
-
|
16
|
-
import grpclib.const
|
17
|
-
|
18
|
-
if TYPE_CHECKING:
|
19
|
-
from grpclib.client import Channel
|
20
|
-
from grpclib.metadata import Deadline
|
21
|
-
|
22
|
-
from .._types import (
|
23
|
-
IProtoMessage,
|
24
|
-
T,
|
25
|
-
)
|
26
|
-
|
27
|
-
|
28
|
-
Value = Union[str, bytes]
|
29
|
-
MetadataLike = Union[Mapping[str, Value], Collection[Tuple[str, Value]]]
|
30
|
-
MessageSource = Union[Iterable["IProtoMessage"], AsyncIterable["IProtoMessage"]]
|
31
|
-
|
32
|
-
|
33
|
-
class ServiceStub(ABC):
|
34
|
-
"""
|
35
|
-
Base class for async gRPC clients.
|
36
|
-
"""
|
37
|
-
|
38
|
-
def __init__(
|
39
|
-
self,
|
40
|
-
channel: "Channel",
|
41
|
-
*,
|
42
|
-
timeout: Optional[float] = None,
|
43
|
-
deadline: Optional["Deadline"] = None,
|
44
|
-
metadata: Optional[MetadataLike] = None,
|
45
|
-
) -> None:
|
46
|
-
self.channel = channel
|
47
|
-
self.timeout = timeout
|
48
|
-
self.deadline = deadline
|
49
|
-
self.metadata = metadata
|
50
|
-
|
51
|
-
def __resolve_request_kwargs(
|
52
|
-
self,
|
53
|
-
timeout: Optional[float],
|
54
|
-
deadline: Optional["Deadline"],
|
55
|
-
metadata: Optional[MetadataLike],
|
56
|
-
):
|
57
|
-
return {
|
58
|
-
"timeout": self.timeout if timeout is None else timeout,
|
59
|
-
"deadline": self.deadline if deadline is None else deadline,
|
60
|
-
"metadata": self.metadata if metadata is None else metadata,
|
61
|
-
}
|
62
|
-
|
63
|
-
async def _unary_unary(
|
64
|
-
self,
|
65
|
-
route: str,
|
66
|
-
request: "IProtoMessage",
|
67
|
-
response_type: Type["T"],
|
68
|
-
*,
|
69
|
-
timeout: Optional[float] = None,
|
70
|
-
deadline: Optional["Deadline"] = None,
|
71
|
-
metadata: Optional[MetadataLike] = None,
|
72
|
-
) -> "T":
|
73
|
-
"""Make a unary request and return the response."""
|
74
|
-
async with self.channel.request(
|
75
|
-
route,
|
76
|
-
grpclib.const.Cardinality.UNARY_UNARY,
|
77
|
-
type(request),
|
78
|
-
response_type,
|
79
|
-
**self.__resolve_request_kwargs(timeout, deadline, metadata),
|
80
|
-
) as stream:
|
81
|
-
await stream.send_message(request, end=True)
|
82
|
-
response = await stream.recv_message()
|
83
|
-
assert response is not None
|
84
|
-
return response
|
85
|
-
|
86
|
-
async def _unary_stream(
|
87
|
-
self,
|
88
|
-
route: str,
|
89
|
-
request: "IProtoMessage",
|
90
|
-
response_type: Type["T"],
|
91
|
-
*,
|
92
|
-
timeout: Optional[float] = None,
|
93
|
-
deadline: Optional["Deadline"] = None,
|
94
|
-
metadata: Optional[MetadataLike] = None,
|
95
|
-
) -> AsyncIterator["T"]:
|
96
|
-
"""Make a unary request and return the stream response iterator."""
|
97
|
-
async with self.channel.request(
|
98
|
-
route,
|
99
|
-
grpclib.const.Cardinality.UNARY_STREAM,
|
100
|
-
type(request),
|
101
|
-
response_type,
|
102
|
-
**self.__resolve_request_kwargs(timeout, deadline, metadata),
|
103
|
-
) as stream:
|
104
|
-
await stream.send_message(request, end=True)
|
105
|
-
async for message in stream:
|
106
|
-
yield message
|
107
|
-
|
108
|
-
async def _stream_unary(
|
109
|
-
self,
|
110
|
-
route: str,
|
111
|
-
request_iterator: MessageSource,
|
112
|
-
request_type: Type["IProtoMessage"],
|
113
|
-
response_type: Type["T"],
|
114
|
-
*,
|
115
|
-
timeout: Optional[float] = None,
|
116
|
-
deadline: Optional["Deadline"] = None,
|
117
|
-
metadata: Optional[MetadataLike] = None,
|
118
|
-
) -> "T":
|
119
|
-
"""Make a stream request and return the response."""
|
120
|
-
async with self.channel.request(
|
121
|
-
route,
|
122
|
-
grpclib.const.Cardinality.STREAM_UNARY,
|
123
|
-
request_type,
|
124
|
-
response_type,
|
125
|
-
**self.__resolve_request_kwargs(timeout, deadline, metadata),
|
126
|
-
) as stream:
|
127
|
-
await stream.send_request()
|
128
|
-
await self._send_messages(stream, request_iterator)
|
129
|
-
response = await stream.recv_message()
|
130
|
-
assert response is not None
|
131
|
-
return response
|
132
|
-
|
133
|
-
async def _stream_stream(
|
134
|
-
self,
|
135
|
-
route: str,
|
136
|
-
request_iterator: MessageSource,
|
137
|
-
request_type: Type["IProtoMessage"],
|
138
|
-
response_type: Type["T"],
|
139
|
-
*,
|
140
|
-
timeout: Optional[float] = None,
|
141
|
-
deadline: Optional["Deadline"] = None,
|
142
|
-
metadata: Optional[MetadataLike] = None,
|
143
|
-
) -> AsyncIterator["T"]:
|
144
|
-
"""
|
145
|
-
Make a stream request and return an AsyncIterator to iterate over response
|
146
|
-
messages.
|
147
|
-
"""
|
148
|
-
async with self.channel.request(
|
149
|
-
route,
|
150
|
-
grpclib.const.Cardinality.STREAM_STREAM,
|
151
|
-
request_type,
|
152
|
-
response_type,
|
153
|
-
**self.__resolve_request_kwargs(timeout, deadline, metadata),
|
154
|
-
) as stream:
|
155
|
-
await stream.send_request()
|
156
|
-
sending_task = asyncio.ensure_future(self._send_messages(stream, request_iterator))
|
157
|
-
try:
|
158
|
-
async for response in stream:
|
159
|
-
yield response
|
160
|
-
except:
|
161
|
-
sending_task.cancel()
|
162
|
-
raise
|
163
|
-
|
164
|
-
@staticmethod
|
165
|
-
async def _send_messages(stream, messages: MessageSource):
|
166
|
-
if isinstance(messages, AsyncIterable):
|
167
|
-
async for message in messages:
|
168
|
-
await stream.send_message(message)
|
169
|
-
else:
|
170
|
-
for message in messages:
|
171
|
-
await stream.send_message(message)
|
172
|
-
await stream.end()
|
@@ -1,32 +0,0 @@
|
|
1
|
-
from abc import ABC
|
2
|
-
from collections.abc import AsyncIterable
|
3
|
-
from typing import (
|
4
|
-
Any,
|
5
|
-
Callable,
|
6
|
-
)
|
7
|
-
|
8
|
-
import grpclib
|
9
|
-
import grpclib.server
|
10
|
-
|
11
|
-
|
12
|
-
class ServiceBase(ABC):
|
13
|
-
"""
|
14
|
-
Base class for async gRPC servers.
|
15
|
-
"""
|
16
|
-
|
17
|
-
async def _call_rpc_handler_server_stream(
|
18
|
-
self,
|
19
|
-
handler: Callable,
|
20
|
-
stream: grpclib.server.Stream,
|
21
|
-
request: Any,
|
22
|
-
) -> None:
|
23
|
-
response_iter = handler(request)
|
24
|
-
# check if response is actually an AsyncIterator
|
25
|
-
# this might be false if the method just returns without
|
26
|
-
# yielding at least once
|
27
|
-
# in that case, we just interpret it as an empty iterator
|
28
|
-
if isinstance(response_iter, AsyncIterable):
|
29
|
-
async for response_message in response_iter:
|
30
|
-
await stream.send_message(response_message)
|
31
|
-
else:
|
32
|
-
response_iter.close()
|
File without changes
|
@@ -1,190 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
from typing import (
|
3
|
-
AsyncIterable,
|
4
|
-
AsyncIterator,
|
5
|
-
Iterable,
|
6
|
-
Optional,
|
7
|
-
TypeVar,
|
8
|
-
Union,
|
9
|
-
)
|
10
|
-
|
11
|
-
T = TypeVar("T")
|
12
|
-
|
13
|
-
|
14
|
-
class ChannelClosed(Exception):
|
15
|
-
"""
|
16
|
-
An exception raised on an attempt to send through a closed channel
|
17
|
-
"""
|
18
|
-
|
19
|
-
|
20
|
-
class ChannelDone(Exception):
|
21
|
-
"""
|
22
|
-
An exception raised on an attempt to send receive from a channel that is both closed
|
23
|
-
and empty.
|
24
|
-
"""
|
25
|
-
|
26
|
-
|
27
|
-
class AsyncChannel(AsyncIterable[T]):
|
28
|
-
"""
|
29
|
-
A buffered async channel for sending items between coroutines with FIFO ordering.
|
30
|
-
|
31
|
-
This makes decoupled bidirectional steaming gRPC requests easy if used like:
|
32
|
-
|
33
|
-
.. code-block:: python
|
34
|
-
client = GeneratedStub(grpclib_chan)
|
35
|
-
request_channel = await AsyncChannel()
|
36
|
-
# We can start be sending all the requests we already have
|
37
|
-
await request_channel.send_from([RequestObject(...), RequestObject(...)])
|
38
|
-
async for response in client.rpc_call(request_channel):
|
39
|
-
# The response iterator will remain active until the connection is closed
|
40
|
-
...
|
41
|
-
# More items can be sent at any time
|
42
|
-
await request_channel.send(RequestObject(...))
|
43
|
-
...
|
44
|
-
# The channel must be closed to complete the gRPC connection
|
45
|
-
request_channel.close()
|
46
|
-
|
47
|
-
Items can be sent through the channel by either:
|
48
|
-
- providing an iterable to the send_from method
|
49
|
-
- passing them to the send method one at a time
|
50
|
-
|
51
|
-
Items can be received from the channel by either:
|
52
|
-
- iterating over the channel with a for loop to get all items
|
53
|
-
- calling the receive method to get one item at a time
|
54
|
-
|
55
|
-
If the channel is empty then receivers will wait until either an item appears or the
|
56
|
-
channel is closed.
|
57
|
-
|
58
|
-
Once the channel is closed then subsequent attempt to send through the channel will
|
59
|
-
fail with a ChannelClosed exception.
|
60
|
-
|
61
|
-
When th channel is closed and empty then it is done, and further attempts to receive
|
62
|
-
from it will fail with a ChannelDone exception
|
63
|
-
|
64
|
-
If multiple coroutines receive from the channel concurrently, each item sent will be
|
65
|
-
received by only one of the receivers.
|
66
|
-
|
67
|
-
:param source:
|
68
|
-
An optional iterable will items that should be sent through the channel
|
69
|
-
immediately.
|
70
|
-
:param buffer_limit:
|
71
|
-
Limit the number of items that can be buffered in the channel, A value less than
|
72
|
-
1 implies no limit. If the channel is full then attempts to send more items will
|
73
|
-
result in the sender waiting until an item is received from the channel.
|
74
|
-
:param close:
|
75
|
-
If set to True then the channel will automatically close after exhausting source
|
76
|
-
or immediately if no source is provided.
|
77
|
-
"""
|
78
|
-
|
79
|
-
def __init__(self, *, buffer_limit: int = 0, close: bool = False):
|
80
|
-
self._queue: asyncio.Queue[T] = asyncio.Queue(buffer_limit)
|
81
|
-
self._closed = False
|
82
|
-
self._waiting_receivers: int = 0
|
83
|
-
# Track whether flush has been invoked so it can only happen once
|
84
|
-
self._flushed = False
|
85
|
-
|
86
|
-
def __aiter__(self) -> AsyncIterator[T]:
|
87
|
-
return self
|
88
|
-
|
89
|
-
async def __anext__(self) -> T:
|
90
|
-
if self.done():
|
91
|
-
raise StopAsyncIteration
|
92
|
-
self._waiting_receivers += 1
|
93
|
-
try:
|
94
|
-
result = await self._queue.get()
|
95
|
-
if result is self.__flush:
|
96
|
-
raise StopAsyncIteration
|
97
|
-
return result
|
98
|
-
finally:
|
99
|
-
self._waiting_receivers -= 1
|
100
|
-
self._queue.task_done()
|
101
|
-
|
102
|
-
def closed(self) -> bool:
|
103
|
-
"""
|
104
|
-
Returns True if this channel is closed and no-longer accepting new items
|
105
|
-
"""
|
106
|
-
return self._closed
|
107
|
-
|
108
|
-
def done(self) -> bool:
|
109
|
-
"""
|
110
|
-
Check if this channel is done.
|
111
|
-
|
112
|
-
:return: True if this channel is closed and and has been drained of items in
|
113
|
-
which case any further attempts to receive an item from this channel will raise
|
114
|
-
a ChannelDone exception.
|
115
|
-
"""
|
116
|
-
# After close the channel is not yet done until there is at least one waiting
|
117
|
-
# receiver per enqueued item.
|
118
|
-
return self._closed and self._queue.qsize() <= self._waiting_receivers
|
119
|
-
|
120
|
-
async def send_from(self, source: Union[Iterable[T], AsyncIterable[T]], close: bool = False) -> "AsyncChannel[T]":
|
121
|
-
"""
|
122
|
-
Iterates the given [Async]Iterable and sends all the resulting items.
|
123
|
-
If close is set to True then subsequent send calls will be rejected with a
|
124
|
-
ChannelClosed exception.
|
125
|
-
:param source: an iterable of items to send
|
126
|
-
:param close:
|
127
|
-
if True then the channel will be closed after the source has been exhausted
|
128
|
-
|
129
|
-
"""
|
130
|
-
if self._closed:
|
131
|
-
raise ChannelClosed("Cannot send through a closed channel")
|
132
|
-
if isinstance(source, AsyncIterable):
|
133
|
-
async for item in source:
|
134
|
-
await self._queue.put(item)
|
135
|
-
else:
|
136
|
-
for item in source:
|
137
|
-
await self._queue.put(item)
|
138
|
-
if close:
|
139
|
-
# Complete the closing process
|
140
|
-
self.close()
|
141
|
-
return self
|
142
|
-
|
143
|
-
async def send(self, item: T) -> "AsyncChannel[T]":
|
144
|
-
"""
|
145
|
-
Send a single item over this channel.
|
146
|
-
:param item: The item to send
|
147
|
-
"""
|
148
|
-
if self._closed:
|
149
|
-
raise ChannelClosed("Cannot send through a closed channel")
|
150
|
-
await self._queue.put(item)
|
151
|
-
return self
|
152
|
-
|
153
|
-
async def receive(self) -> Optional[T]:
|
154
|
-
"""
|
155
|
-
Returns the next item from this channel when it becomes available,
|
156
|
-
or None if the channel is closed before another item is sent.
|
157
|
-
:return: An item from the channel
|
158
|
-
"""
|
159
|
-
if self.done():
|
160
|
-
raise ChannelDone("Cannot receive from a closed channel")
|
161
|
-
self._waiting_receivers += 1
|
162
|
-
try:
|
163
|
-
result = await self._queue.get()
|
164
|
-
if result is self.__flush:
|
165
|
-
return None
|
166
|
-
return result
|
167
|
-
finally:
|
168
|
-
self._waiting_receivers -= 1
|
169
|
-
self._queue.task_done()
|
170
|
-
|
171
|
-
def close(self):
|
172
|
-
"""
|
173
|
-
Close this channel to new items
|
174
|
-
"""
|
175
|
-
self._closed = True
|
176
|
-
asyncio.ensure_future(self._flush_queue())
|
177
|
-
|
178
|
-
async def _flush_queue(self):
|
179
|
-
"""
|
180
|
-
To be called after the channel is closed. Pushes a number of self.__flush
|
181
|
-
objects to the queue to ensure no waiting consumers get deadlocked.
|
182
|
-
"""
|
183
|
-
if not self._flushed:
|
184
|
-
self._flushed = True
|
185
|
-
deadlocked_receivers = max(0, self._waiting_receivers - self._queue.qsize())
|
186
|
-
for _ in range(deadlocked_receivers):
|
187
|
-
await self._queue.put(self.__flush)
|
188
|
-
|
189
|
-
# A special signal object for flushing the queue when the channel is closed
|
190
|
-
__flush = object()
|
File without changes
|
File without changes
|