polaris-python-ai 0.0.1.dev2__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.
- polaris_ai/__init__.py +34 -0
- polaris_ai/_async.py +39 -0
- polaris_ai/_version.py +18 -0
- polaris_ai/a2a/__init__.py +31 -0
- polaris_ai/a2a/_card.py +44 -0
- polaris_ai/a2a/registry.py +240 -0
- polaris_ai/a2a/resolver.py +198 -0
- polaris_ai/a2a/server.py +92 -0
- polaris_ai/agentscope/__init__.py +76 -0
- polaris_ai/agentscope/a2a.py +276 -0
- polaris_ai/agentscope/cards.py +106 -0
- polaris_ai/agentscope/config.py +221 -0
- polaris_ai/agentscope/context.py +195 -0
- polaris_ai/agentscope/mcp.py +598 -0
- polaris_ai/agentscope/middleware.py +103 -0
- polaris_ai/agentscope/runtime.py +291 -0
- polaris_ai/agentscope/skill.py +464 -0
- polaris_ai/langgraph/__init__.py +50 -0
- polaris_ai/langgraph/config.py +160 -0
- polaris_ai/langgraph/context.py +152 -0
- polaris_ai/langgraph/registry.py +258 -0
- polaris_ai/langgraph/remote.py +315 -0
- polaris_ai/langgraph/resolver.py +226 -0
- polaris_ai/log.py +408 -0
- polaris_ai/metrics.py +154 -0
- polaris_python_ai-0.0.1.dev2.dist-info/METADATA +351 -0
- polaris_python_ai-0.0.1.dev2.dist-info/RECORD +30 -0
- polaris_python_ai-0.0.1.dev2.dist-info/WHEEL +5 -0
- polaris_python_ai-0.0.1.dev2.dist-info/licenses/LICENSE +27 -0
- polaris_python_ai-0.0.1.dev2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making polaris-python-ai available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2026 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the BSD 3-Clause License (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://opensource.org/licenses/BSD-3-Clause
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software distributed
|
|
12
|
+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
13
|
+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
14
|
+
# specific language governing permissions and limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""Application-side configuration for Polaris AgentScope integrations."""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _bool_env(value: str, *, default: bool) -> bool:
|
|
25
|
+
lowered = value.strip().lower()
|
|
26
|
+
if lowered in ("1", "true", "yes", "on"):
|
|
27
|
+
return True
|
|
28
|
+
if lowered in ("0", "false", "no", "off"):
|
|
29
|
+
return False
|
|
30
|
+
if not lowered:
|
|
31
|
+
return default
|
|
32
|
+
raise ValueError(f"Invalid boolean environment value: {value!r}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _text_env(value: str) -> str:
|
|
36
|
+
return value.strip()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _list_env(value: str) -> tuple[str, ...]:
|
|
40
|
+
return tuple(item.strip() for item in value.split(",") if item.strip())
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _int_env(value: str, name: str, default: int) -> int:
|
|
44
|
+
if not value:
|
|
45
|
+
return default
|
|
46
|
+
try:
|
|
47
|
+
return int(value)
|
|
48
|
+
except ValueError as error:
|
|
49
|
+
raise ValueError(f"{name} must be an integer") from error
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _float_env(value: str, name: str, default: float) -> float:
|
|
53
|
+
if not value:
|
|
54
|
+
return default
|
|
55
|
+
try:
|
|
56
|
+
return float(value)
|
|
57
|
+
except ValueError as error:
|
|
58
|
+
raise ValueError(f"{name} must be a number") from error
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _headers_env(value: str) -> tuple[tuple[str, str], ...]:
|
|
62
|
+
result = []
|
|
63
|
+
for item in _list_env(value):
|
|
64
|
+
name, separator, header_value = item.partition("=")
|
|
65
|
+
if not separator or not name.strip():
|
|
66
|
+
raise ValueError("POLARIS_AI_MCP_HEADERS items must be name=value")
|
|
67
|
+
result.append((name.strip(), header_value.strip()))
|
|
68
|
+
return tuple(result)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True)
|
|
72
|
+
class PolarisSkillConfig:
|
|
73
|
+
"""Read-only Skill repository options."""
|
|
74
|
+
|
|
75
|
+
enabled: bool = True
|
|
76
|
+
addresses: tuple[str, ...] = ()
|
|
77
|
+
names: tuple[str, ...] = ()
|
|
78
|
+
version: str = ""
|
|
79
|
+
list_limit: int = 50
|
|
80
|
+
max_skills: int = 100
|
|
81
|
+
refresh_interval: float = 30.0
|
|
82
|
+
mounted_enabled: bool = False
|
|
83
|
+
mounted_service_name: str = ""
|
|
84
|
+
attach_to_agent: bool = True
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class PolarisA2ARegistryConfig:
|
|
89
|
+
enabled: bool = True
|
|
90
|
+
ttl: int = 5
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass(frozen=True)
|
|
94
|
+
class PolarisA2ADiscoveryConfig:
|
|
95
|
+
enabled: bool = True
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass(frozen=True)
|
|
99
|
+
class PolarisA2AConfig:
|
|
100
|
+
enabled: bool = True
|
|
101
|
+
registry: PolarisA2ARegistryConfig = field(default_factory=PolarisA2ARegistryConfig)
|
|
102
|
+
discovery: PolarisA2ADiscoveryConfig = field(default_factory=PolarisA2ADiscoveryConfig)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class PolarisMCPConfig:
|
|
107
|
+
enabled: bool = False
|
|
108
|
+
protocol_version: str = ""
|
|
109
|
+
scheme: str = ""
|
|
110
|
+
timeout: float = 30.0
|
|
111
|
+
filter_contract: bool = True
|
|
112
|
+
headers: tuple[tuple[str, str], ...] = ()
|
|
113
|
+
caller_service: str = ""
|
|
114
|
+
labels: str = ""
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass(frozen=True)
|
|
118
|
+
class PolarisAgentConfig:
|
|
119
|
+
"""Code-first options for an application; not part of polaris.yaml."""
|
|
120
|
+
|
|
121
|
+
enabled: bool = True
|
|
122
|
+
namespace: str = "default"
|
|
123
|
+
addresses: tuple[str, ...] = ("127.0.0.1:8091",)
|
|
124
|
+
connector_token: str = ""
|
|
125
|
+
service_token: str = ""
|
|
126
|
+
skill: PolarisSkillConfig = field(default_factory=PolarisSkillConfig)
|
|
127
|
+
a2a: PolarisA2AConfig = field(default_factory=PolarisA2AConfig)
|
|
128
|
+
mcp: PolarisMCPConfig = field(default_factory=PolarisMCPConfig)
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def from_env(
|
|
132
|
+
cls,
|
|
133
|
+
environ: dict[str, str] | None = None,
|
|
134
|
+
*,
|
|
135
|
+
agent_name: str = "",
|
|
136
|
+
) -> "PolarisAgentConfig":
|
|
137
|
+
"""Load application options with explicit code and SDK env fallbacks.
|
|
138
|
+
|
|
139
|
+
`POLARIS_AI_*` always takes precedence. Existing `POLARIS_*` variables are
|
|
140
|
+
read only as fallbacks so this helper can coexist with SDK examples.
|
|
141
|
+
"""
|
|
142
|
+
env = os.environ if environ is None else environ
|
|
143
|
+
|
|
144
|
+
def value(name: str, fallback: str = "") -> str:
|
|
145
|
+
return env.get(name, env.get(fallback, "")).strip()
|
|
146
|
+
|
|
147
|
+
addresses = _list_env(
|
|
148
|
+
value("POLARIS_AI_ADDRESS", "POLARIS_SERVER_ADDRESS")
|
|
149
|
+
or value("POLARIS_AI_ADDRESSES", "POLARIS_SERVER_ADDRESSES")
|
|
150
|
+
) or ("127.0.0.1:8091",)
|
|
151
|
+
skill_addresses = _list_env(
|
|
152
|
+
value("POLARIS_AI_SKILL_ADDRESS", "POLARIS_SKILL_SERVER_ADDRESS")
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
mounted_service = value("POLARIS_AI_MOUNTED_SKILL_SERVICE")
|
|
156
|
+
if not mounted_service:
|
|
157
|
+
mounted_service = value("POLARIS_AI_A2A_CARD_NAME")
|
|
158
|
+
if not mounted_service:
|
|
159
|
+
mounted_service = value("POLARIS_AI_AGENT_NAME") or agent_name.strip()
|
|
160
|
+
|
|
161
|
+
skill = PolarisSkillConfig(
|
|
162
|
+
enabled=_bool_env(env.get("POLARIS_AI_SKILL_ENABLED", ""), default=True),
|
|
163
|
+
addresses=skill_addresses,
|
|
164
|
+
names=_list_env(value("POLARIS_AI_SKILL_NAMES")),
|
|
165
|
+
version=value("POLARIS_AI_SKILL_VERSION"),
|
|
166
|
+
list_limit=_int_env(
|
|
167
|
+
value("POLARIS_AI_SKILL_LIST_LIMIT"), "POLARIS_AI_SKILL_LIST_LIMIT", 50
|
|
168
|
+
),
|
|
169
|
+
max_skills=_int_env(
|
|
170
|
+
value("POLARIS_AI_SKILL_MAX_SKILLS"), "POLARIS_AI_SKILL_MAX_SKILLS", 100
|
|
171
|
+
),
|
|
172
|
+
refresh_interval=_float_env(
|
|
173
|
+
value("POLARIS_AI_SKILL_LIST_REFRESH_INTERVAL_SECONDS"),
|
|
174
|
+
"POLARIS_AI_SKILL_LIST_REFRESH_INTERVAL_SECONDS",
|
|
175
|
+
30.0,
|
|
176
|
+
),
|
|
177
|
+
mounted_enabled=_bool_env(
|
|
178
|
+
env.get("POLARIS_AI_MOUNTED_SKILL_ENABLED", ""), default=False
|
|
179
|
+
),
|
|
180
|
+
mounted_service_name=mounted_service,
|
|
181
|
+
attach_to_agent=_bool_env(
|
|
182
|
+
env.get("POLARIS_AI_SKILL_ATTACH_TO_AGENT", ""), default=True
|
|
183
|
+
),
|
|
184
|
+
)
|
|
185
|
+
a2a = PolarisA2AConfig(
|
|
186
|
+
enabled=_bool_env(env.get("POLARIS_AI_A2A_ENABLED", ""), default=True),
|
|
187
|
+
registry=PolarisA2ARegistryConfig(
|
|
188
|
+
enabled=_bool_env(
|
|
189
|
+
env.get("POLARIS_AI_A2A_REGISTRY_ENABLED", ""), default=True
|
|
190
|
+
),
|
|
191
|
+
ttl=_int_env(value("POLARIS_AI_A2A_TTL"), "POLARIS_AI_A2A_TTL", 5),
|
|
192
|
+
),
|
|
193
|
+
discovery=PolarisA2ADiscoveryConfig(
|
|
194
|
+
enabled=_bool_env(
|
|
195
|
+
env.get("POLARIS_AI_A2A_DISCOVERY_ENABLED", ""), default=True
|
|
196
|
+
),
|
|
197
|
+
),
|
|
198
|
+
)
|
|
199
|
+
headers = _headers_env(value("POLARIS_AI_MCP_HEADERS"))
|
|
200
|
+
mcp = PolarisMCPConfig(
|
|
201
|
+
enabled=_bool_env(env.get("POLARIS_AI_MCP_ENABLED", ""), default=False),
|
|
202
|
+
protocol_version=value("POLARIS_AI_MCP_PROTOCOL_VERSION"),
|
|
203
|
+
scheme=value("POLARIS_AI_MCP_SCHEME"),
|
|
204
|
+
timeout=_float_env(value("POLARIS_AI_MCP_TIMEOUT"), "POLARIS_AI_MCP_TIMEOUT", 30.0),
|
|
205
|
+
filter_contract=_bool_env(
|
|
206
|
+
env.get("POLARIS_AI_MCP_FILTER_CONTRACT", ""), default=True
|
|
207
|
+
),
|
|
208
|
+
headers=headers,
|
|
209
|
+
caller_service=value("POLARIS_AI_MCP_CALLER_SERVICE"),
|
|
210
|
+
labels=value("POLARIS_AI_MCP_LABELS"),
|
|
211
|
+
)
|
|
212
|
+
return cls(
|
|
213
|
+
enabled=_bool_env(env.get("POLARIS_AI_ENABLED", ""), default=True),
|
|
214
|
+
namespace=value("POLARIS_AI_NAMESPACE", "default") or "default",
|
|
215
|
+
addresses=addresses,
|
|
216
|
+
connector_token=value("POLARIS_AI_CONNECTOR_TOKEN", "POLARIS_TOKEN"),
|
|
217
|
+
service_token=value("POLARIS_AI_SERVICE_TOKEN", "POLARIS_SERVICE_TOKEN"),
|
|
218
|
+
skill=skill,
|
|
219
|
+
a2a=a2a,
|
|
220
|
+
mcp=mcp,
|
|
221
|
+
)
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making polaris-python-ai available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2026 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the BSD 3-Clause License (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://opensource.org/licenses/BSD-3-Clause
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software distributed
|
|
12
|
+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
13
|
+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
14
|
+
# specific language governing permissions and limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""Shared Polaris connection factory for AgentScope applications."""
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
from collections.abc import Iterable
|
|
20
|
+
|
|
21
|
+
from polaris.api.config import init_context_by_config
|
|
22
|
+
from polaris.api.consumer import ConsumerAPI
|
|
23
|
+
from polaris.api.log import LogOptions
|
|
24
|
+
from polaris.api.provider import ProviderAPI
|
|
25
|
+
from polaris.api.skill import SkillAPI
|
|
26
|
+
from polaris.pkg.config.api import Configuration
|
|
27
|
+
|
|
28
|
+
from polaris_ai._async import await_completion
|
|
29
|
+
|
|
30
|
+
DEFAULT_DISCOVERY_ADDRESS = "127.0.0.1:8091"
|
|
31
|
+
SKILL_ADDRESS_PORT = 8094
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _validate_address(address: str) -> str:
|
|
35
|
+
if not isinstance(address, str) or not address.strip():
|
|
36
|
+
raise ValueError("Polaris addresses must be nonempty host:port values")
|
|
37
|
+
address = address.strip()
|
|
38
|
+
host, separator, port_text = address.rpartition(":")
|
|
39
|
+
if not separator or not host:
|
|
40
|
+
raise ValueError(f"Invalid Polaris address {address!r}: a port is required")
|
|
41
|
+
try:
|
|
42
|
+
port = int(port_text)
|
|
43
|
+
except ValueError as error:
|
|
44
|
+
raise ValueError(f"Invalid Polaris address {address!r}: port must be an integer") from error
|
|
45
|
+
if not 0 < port < 65536:
|
|
46
|
+
raise ValueError(f"Invalid Polaris address {address!r}: port must be in [1, 65535]")
|
|
47
|
+
if host.startswith("[") and host.endswith("]"):
|
|
48
|
+
host = host[1:-1]
|
|
49
|
+
if not host:
|
|
50
|
+
raise ValueError(f"Invalid Polaris address {address!r}: empty IPv6 host")
|
|
51
|
+
elif host.count(":") > 0:
|
|
52
|
+
raise ValueError(f"Invalid Polaris address {address!r}: IPv6 hosts require brackets")
|
|
53
|
+
if not host.strip():
|
|
54
|
+
raise ValueError(f"Invalid Polaris address {address!r}: host is empty")
|
|
55
|
+
return f"[{host}]:{port}" if ":" in host else f"{host}:{port}"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def derive_skill_addresses(addresses: Iterable[str]) -> list[str]:
|
|
59
|
+
"""Map discovery endpoints to the standard Skill endpoint on each host."""
|
|
60
|
+
result = []
|
|
61
|
+
for address in addresses:
|
|
62
|
+
normalized = _validate_address(address)
|
|
63
|
+
host, _, _ = normalized.rpartition(":")
|
|
64
|
+
skill_address = f"{host}:{SKILL_ADDRESS_PORT}"
|
|
65
|
+
if skill_address not in result:
|
|
66
|
+
result.append(skill_address)
|
|
67
|
+
if not result:
|
|
68
|
+
raise ValueError("At least one discovery address is required")
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PolarisAgentContext:
|
|
73
|
+
"""Expose shared Provider, Consumer and Skill APIs over one SDK context.
|
|
74
|
+
|
|
75
|
+
A supplied context is borrowed and is never modified or destroyed by this
|
|
76
|
+
factory. When no context is supplied, the factory owns the context it
|
|
77
|
+
creates, disables stat reporting, and derives Skill addresses from the
|
|
78
|
+
discovery addresses. Like Java's PolarisContextManager, it also disables
|
|
79
|
+
the provider lossless lifecycle for this owned context.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
namespace: str = "default",
|
|
85
|
+
*,
|
|
86
|
+
address: str | Iterable[str] = DEFAULT_DISCOVERY_ADDRESS,
|
|
87
|
+
connector_token: str = "",
|
|
88
|
+
skill_address: str | Iterable[str] = "",
|
|
89
|
+
log_options: LogOptions | None = None,
|
|
90
|
+
sdk_context=None,
|
|
91
|
+
):
|
|
92
|
+
if sdk_context is not None:
|
|
93
|
+
if sdk_context.is_destroy():
|
|
94
|
+
raise ValueError("An active SDKContext is required")
|
|
95
|
+
self._owns_context = False
|
|
96
|
+
self._context = sdk_context
|
|
97
|
+
else:
|
|
98
|
+
if isinstance(address, str):
|
|
99
|
+
addresses = [address]
|
|
100
|
+
else:
|
|
101
|
+
addresses = list(address)
|
|
102
|
+
if not addresses:
|
|
103
|
+
raise ValueError("At least one discovery address is required")
|
|
104
|
+
addresses = [_validate_address(item) for item in addresses]
|
|
105
|
+
if isinstance(skill_address, str):
|
|
106
|
+
skill_addresses = [] if not skill_address else [skill_address]
|
|
107
|
+
else:
|
|
108
|
+
skill_addresses = list(skill_address)
|
|
109
|
+
skill_addresses = [_validate_address(item) for item in skill_addresses]
|
|
110
|
+
if not skill_addresses:
|
|
111
|
+
skill_addresses = derive_skill_addresses(addresses)
|
|
112
|
+
|
|
113
|
+
config = Configuration()
|
|
114
|
+
config.set_default()
|
|
115
|
+
config.get_global().get_server_connector().addresses = addresses
|
|
116
|
+
config.get_global().get_server_connector().token = connector_token
|
|
117
|
+
config.get_global().get_stat_reporter().enable = False
|
|
118
|
+
config.get_provider().get_lossless().enable = False
|
|
119
|
+
config.get_ai().get_skill().get_server_connector().addresses = skill_addresses
|
|
120
|
+
self._owns_context = True
|
|
121
|
+
self._context = None
|
|
122
|
+
try:
|
|
123
|
+
self._context = init_context_by_config(config, log_options=log_options)
|
|
124
|
+
except BaseException:
|
|
125
|
+
if self._context is not None:
|
|
126
|
+
self._context.destroy()
|
|
127
|
+
raise
|
|
128
|
+
|
|
129
|
+
self.namespace = namespace.strip() or "default"
|
|
130
|
+
self._closed = False
|
|
131
|
+
try:
|
|
132
|
+
self.provider = ProviderAPI(self._context)
|
|
133
|
+
self.consumer = ConsumerAPI(self._context)
|
|
134
|
+
self.skill = SkillAPI(self._context)
|
|
135
|
+
except BaseException:
|
|
136
|
+
self.close()
|
|
137
|
+
raise
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def from_address(
|
|
141
|
+
cls,
|
|
142
|
+
address: str | Iterable[str] = DEFAULT_DISCOVERY_ADDRESS,
|
|
143
|
+
namespace: str = "default",
|
|
144
|
+
*,
|
|
145
|
+
connector_token: str = "",
|
|
146
|
+
skill_address: str | Iterable[str] = "",
|
|
147
|
+
log_options: LogOptions | None = None,
|
|
148
|
+
) -> "PolarisAgentContext":
|
|
149
|
+
return cls(
|
|
150
|
+
namespace,
|
|
151
|
+
address=address,
|
|
152
|
+
connector_token=connector_token,
|
|
153
|
+
skill_address=skill_address,
|
|
154
|
+
log_options=log_options,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def context(self):
|
|
159
|
+
return self._context
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def owns_context(self) -> bool:
|
|
163
|
+
return self._owns_context
|
|
164
|
+
|
|
165
|
+
def _check_open(self):
|
|
166
|
+
if self._closed or self._context is None or self._context.is_destroy():
|
|
167
|
+
raise RuntimeError("PolarisAgentContext is closed")
|
|
168
|
+
|
|
169
|
+
def close(self):
|
|
170
|
+
"""Close owned resources; borrowed contexts remain untouched."""
|
|
171
|
+
if self._closed:
|
|
172
|
+
return
|
|
173
|
+
self._closed = True
|
|
174
|
+
self.provider = None
|
|
175
|
+
self.consumer = None
|
|
176
|
+
self.skill = None
|
|
177
|
+
if self._owns_context and self._context is not None:
|
|
178
|
+
self._context.destroy()
|
|
179
|
+
|
|
180
|
+
async def async_close(self):
|
|
181
|
+
await await_completion(asyncio.to_thread(self.close))
|
|
182
|
+
|
|
183
|
+
def __enter__(self):
|
|
184
|
+
self._check_open()
|
|
185
|
+
return self
|
|
186
|
+
|
|
187
|
+
def __exit__(self, *_):
|
|
188
|
+
self.close()
|
|
189
|
+
|
|
190
|
+
async def __aenter__(self):
|
|
191
|
+
self._check_open()
|
|
192
|
+
return self
|
|
193
|
+
|
|
194
|
+
async def __aexit__(self, *_):
|
|
195
|
+
await self.async_close()
|