boxfab 0.1.0a1__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.
- boxfab/__init__.py +331 -0
- boxfab/api/__init__.py +301 -0
- boxfab/api/client/__init__.py +8 -0
- boxfab/api/client/api/__init__.py +1 -0
- boxfab/api/client/api/sandboxes/__init__.py +1 -0
- boxfab/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +173 -0
- boxfab/api/client/api/sandboxes/get_sandboxes.py +188 -0
- boxfab/api/client/api/sandboxes/get_sandboxes_metrics.py +185 -0
- boxfab/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +175 -0
- boxfab/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +211 -0
- boxfab/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +224 -0
- boxfab/api/client/api/sandboxes/get_v2_sandboxes.py +296 -0
- boxfab/api/client/api/sandboxes/get_v_2_sandboxes_sandbox_id_logs.py +266 -0
- boxfab/api/client/api/sandboxes/post_sandboxes.py +196 -0
- boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +221 -0
- boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_fork.py +226 -0
- boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +203 -0
- boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +191 -0
- boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +213 -0
- boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_snapshots.py +205 -0
- boxfab/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +203 -0
- boxfab/api/client/api/sandboxes/post_v2_sandboxes.py +227 -0
- boxfab/api/client/api/sandboxes/post_v_2_sandboxes_sandbox_id_connect.py +221 -0
- boxfab/api/client/api/sandboxes/put_sandboxes_sandbox_id_network.py +211 -0
- boxfab/api/client/api/secrets/__init__.py +1 -0
- boxfab/api/client/api/secrets/delete_secrets_secret_id.py +197 -0
- boxfab/api/client/api/secrets/get_secrets.py +223 -0
- boxfab/api/client/api/secrets/get_secrets_secret_id.py +199 -0
- boxfab/api/client/api/secrets/post_secrets.py +208 -0
- boxfab/api/client/api/secrets/post_secrets_secret_id.py +221 -0
- boxfab/api/client/api/snapshots/__init__.py +1 -0
- boxfab/api/client/api/snapshots/get_snapshots.py +229 -0
- boxfab/api/client/api/tags/__init__.py +1 -0
- boxfab/api/client/api/tags/delete_templates_tags.py +186 -0
- boxfab/api/client/api/tags/get_templates_template_id_tags.py +184 -0
- boxfab/api/client/api/tags/get_templates_template_id_tags_count.py +183 -0
- boxfab/api/client/api/tags/get_templates_template_id_tags_exists.py +208 -0
- boxfab/api/client/api/tags/get_templates_template_id_tags_groups.py +293 -0
- boxfab/api/client/api/tags/get_templates_template_id_tags_tag_assignments.py +236 -0
- boxfab/api/client/api/tags/post_templates_tags.py +188 -0
- boxfab/api/client/api/templates/__init__.py +1 -0
- boxfab/api/client/api/templates/delete_templates_template_id.py +169 -0
- boxfab/api/client/api/templates/get_default_templates.py +172 -0
- boxfab/api/client/api/templates/get_templates.py +188 -0
- boxfab/api/client/api/templates/get_templates_aliases_alias.py +179 -0
- boxfab/api/client/api/templates/get_templates_template_id.py +207 -0
- boxfab/api/client/api/templates/get_templates_template_id_builds_build_id_logs.py +284 -0
- boxfab/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +244 -0
- boxfab/api/client/api/templates/get_templates_template_id_files_hash.py +192 -0
- boxfab/api/client/api/templates/get_v2_templates.py +226 -0
- boxfab/api/client/api/templates/patch_templates_template_id.py +195 -0
- boxfab/api/client/api/templates/patch_v_2_templates_template_id.py +197 -0
- boxfab/api/client/api/templates/post_v3_templates.py +192 -0
- boxfab/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +212 -0
- boxfab/api/client/api/volumes/__init__.py +1 -0
- boxfab/api/client/api/volumes/delete_volumes_volume_id.py +173 -0
- boxfab/api/client/api/volumes/get_volumes.py +152 -0
- boxfab/api/client/api/volumes/get_volumes_volume_id.py +175 -0
- boxfab/api/client/api/volumes/post_volumes.py +184 -0
- boxfab/api/client/client.py +286 -0
- boxfab/api/client/errors.py +16 -0
- boxfab/api/client/models/__init__.py +183 -0
- boxfab/api/client/models/assign_template_tags_request.py +67 -0
- boxfab/api/client/models/assigned_template_tags.py +68 -0
- boxfab/api/client/models/aws_registry.py +85 -0
- boxfab/api/client/models/aws_registry_type.py +8 -0
- boxfab/api/client/models/build_log_entry.py +89 -0
- boxfab/api/client/models/build_status_reason.py +95 -0
- boxfab/api/client/models/connect_sandbox.py +74 -0
- boxfab/api/client/models/connect_sandbox_v2.py +73 -0
- boxfab/api/client/models/delete_template_tags_request.py +67 -0
- boxfab/api/client/models/error.py +83 -0
- boxfab/api/client/models/gcp_registry.py +69 -0
- boxfab/api/client/models/gcp_registry_type.py +8 -0
- boxfab/api/client/models/general_registry.py +77 -0
- boxfab/api/client/models/general_registry_type.py +8 -0
- boxfab/api/client/models/get_templates_template_id_tags_groups_sort.py +11 -0
- boxfab/api/client/models/listed_sandbox.py +179 -0
- boxfab/api/client/models/log_level.py +11 -0
- boxfab/api/client/models/logs_direction.py +9 -0
- boxfab/api/client/models/logs_source.py +9 -0
- boxfab/api/client/models/mcp_type_0.py +44 -0
- boxfab/api/client/models/new_sandbox.py +244 -0
- boxfab/api/client/models/new_sandbox_v2.py +236 -0
- boxfab/api/client/models/new_secret.py +95 -0
- boxfab/api/client/models/new_volume.py +59 -0
- boxfab/api/client/models/order_direction.py +9 -0
- boxfab/api/client/models/resumed_sandbox.py +81 -0
- boxfab/api/client/models/sandbox.py +145 -0
- boxfab/api/client/models/sandbox_auto_resume_config.py +60 -0
- boxfab/api/client/models/sandbox_detail.py +287 -0
- boxfab/api/client/models/sandbox_egress_proxy_config_type_0.py +133 -0
- boxfab/api/client/models/sandbox_egress_proxy_tls_config_type_0.py +87 -0
- boxfab/api/client/models/sandbox_fork_request.py +70 -0
- boxfab/api/client/models/sandbox_fork_result.py +92 -0
- boxfab/api/client/models/sandbox_iam.py +73 -0
- boxfab/api/client/models/sandbox_iam_token.py +67 -0
- boxfab/api/client/models/sandbox_iam_tokens.py +59 -0
- boxfab/api/client/models/sandbox_lifecycle.py +70 -0
- boxfab/api/client/models/sandbox_log.py +70 -0
- boxfab/api/client/models/sandbox_log_entry.py +93 -0
- boxfab/api/client/models/sandbox_log_entry_fields.py +44 -0
- boxfab/api/client/models/sandbox_logs.py +91 -0
- boxfab/api/client/models/sandbox_logs_v2_response.py +73 -0
- boxfab/api/client/models/sandbox_metric.py +126 -0
- boxfab/api/client/models/sandbox_network_config.py +210 -0
- boxfab/api/client/models/sandbox_network_config_rules.py +76 -0
- boxfab/api/client/models/sandbox_network_rule.py +74 -0
- boxfab/api/client/models/sandbox_network_transform.py +79 -0
- boxfab/api/client/models/sandbox_network_transform_headers.py +47 -0
- boxfab/api/client/models/sandbox_network_update_config.py +179 -0
- boxfab/api/client/models/sandbox_network_update_config_rules.py +78 -0
- boxfab/api/client/models/sandbox_on_timeout.py +9 -0
- boxfab/api/client/models/sandbox_pause_mode.py +9 -0
- boxfab/api/client/models/sandbox_pause_request.py +62 -0
- boxfab/api/client/models/sandbox_refresh_request.py +59 -0
- boxfab/api/client/models/sandbox_snapshot_request.py +72 -0
- boxfab/api/client/models/sandbox_state.py +9 -0
- boxfab/api/client/models/sandbox_timeout_request.py +59 -0
- boxfab/api/client/models/sandbox_volume_mount.py +67 -0
- boxfab/api/client/models/sandboxes_with_metrics.py +59 -0
- boxfab/api/client/models/secret.py +109 -0
- boxfab/api/client/models/secret_metadata.py +47 -0
- boxfab/api/client/models/secret_update.py +86 -0
- boxfab/api/client/models/snapshot_info.py +70 -0
- boxfab/api/client/models/team_user.py +74 -0
- boxfab/api/client/models/template.py +246 -0
- boxfab/api/client/models/template_alias_response.py +67 -0
- boxfab/api/client/models/template_build.py +139 -0
- boxfab/api/client/models/template_build_file_upload.py +97 -0
- boxfab/api/client/models/template_build_file_upload_headers.py +44 -0
- boxfab/api/client/models/template_build_info.py +126 -0
- boxfab/api/client/models/template_build_logs_response.py +73 -0
- boxfab/api/client/models/template_build_request_v3.py +119 -0
- boxfab/api/client/models/template_build_start_v2.py +185 -0
- boxfab/api/client/models/template_build_status.py +11 -0
- boxfab/api/client/models/template_request_response_v3.py +99 -0
- boxfab/api/client/models/template_step.py +91 -0
- boxfab/api/client/models/template_tag.py +78 -0
- boxfab/api/client/models/template_tag_assignment.py +111 -0
- boxfab/api/client/models/template_tag_assignments_response.py +87 -0
- boxfab/api/client/models/template_tag_exists_response.py +67 -0
- boxfab/api/client/models/template_tag_group.py +89 -0
- boxfab/api/client/models/template_tag_groups_response.py +88 -0
- boxfab/api/client/models/template_tags_count_response.py +59 -0
- boxfab/api/client/models/template_update_request.py +59 -0
- boxfab/api/client/models/template_update_response.py +59 -0
- boxfab/api/client/models/template_with_builds.py +264 -0
- boxfab/api/client/models/volume.py +67 -0
- boxfab/api/client/models/volume_and_token.py +89 -0
- boxfab/api/client/py.typed +1 -0
- boxfab/api/client/types.py +54 -0
- boxfab/api/client_async/__init__.py +232 -0
- boxfab/api/client_sync/__init__.py +237 -0
- boxfab/api/metadata.py +14 -0
- boxfab/client.py +92 -0
- boxfab/connection_config.py +473 -0
- boxfab/envd/__init__.py +1 -0
- boxfab/envd/api.py +204 -0
- boxfab/envd/client_async/__init__.py +149 -0
- boxfab/envd/client_shared.py +143 -0
- boxfab/envd/client_sync/__init__.py +97 -0
- boxfab/envd/filesystem/__init__.py +1 -0
- boxfab/envd/filesystem/filesystem_connect.py +690 -0
- boxfab/envd/filesystem/filesystem_pb.py +851 -0
- boxfab/envd/interceptors.py +275 -0
- boxfab/envd/process/__init__.py +1 -0
- boxfab/envd/process/process_connect.py +641 -0
- boxfab/envd/process/process_pb.py +938 -0
- boxfab/envd/rpc.py +247 -0
- boxfab/envd/utils.py +57 -0
- boxfab/envd/versions.py +11 -0
- boxfab/exceptions.py +269 -0
- boxfab/io_utils.py +57 -0
- boxfab/paginator.py +50 -0
- boxfab/py.typed +0 -0
- boxfab/retry.py +229 -0
- boxfab/sandbox/_git/__init__.py +85 -0
- boxfab/sandbox/_git/args.py +363 -0
- boxfab/sandbox/_git/auth.py +132 -0
- boxfab/sandbox/_git/config.py +32 -0
- boxfab/sandbox/_git/parse.py +222 -0
- boxfab/sandbox/_git/types.py +156 -0
- boxfab/sandbox/commands/command_handle.py +89 -0
- boxfab/sandbox/commands/main.py +39 -0
- boxfab/sandbox/commands/stream_input.py +37 -0
- boxfab/sandbox/filesystem/filesystem.py +585 -0
- boxfab/sandbox/filesystem/watch_handle.py +70 -0
- boxfab/sandbox/iam.py +93 -0
- boxfab/sandbox/main.py +232 -0
- boxfab/sandbox/mcp.py +2538 -0
- boxfab/sandbox/network.py +8 -0
- boxfab/sandbox/sandbox_api.py +1398 -0
- boxfab/sandbox/signature.py +47 -0
- boxfab/sandbox/utils.py +34 -0
- boxfab/sandbox_async/commands/command.py +582 -0
- boxfab/sandbox_async/commands/command_handle.py +324 -0
- boxfab/sandbox_async/commands/command_input_handle.py +131 -0
- boxfab/sandbox_async/commands/pty.py +283 -0
- boxfab/sandbox_async/filesystem/filesystem.py +785 -0
- boxfab/sandbox_async/filesystem/watch_handle.py +100 -0
- boxfab/sandbox_async/git.py +1102 -0
- boxfab/sandbox_async/main.py +1250 -0
- boxfab/sandbox_async/paginator.py +159 -0
- boxfab/sandbox_async/sandbox_api.py +533 -0
- boxfab/sandbox_async/utils.py +7 -0
- boxfab/sandbox_domains.py +13 -0
- boxfab/sandbox_sync/commands/command.py +576 -0
- boxfab/sandbox_sync/commands/command_handle.py +260 -0
- boxfab/sandbox_sync/commands/pty.py +286 -0
- boxfab/sandbox_sync/filesystem/filesystem.py +731 -0
- boxfab/sandbox_sync/filesystem/watch_handle.py +103 -0
- boxfab/sandbox_sync/git.py +1079 -0
- boxfab/sandbox_sync/main.py +1246 -0
- boxfab/sandbox_sync/paginator.py +159 -0
- boxfab/sandbox_sync/sandbox_api.py +520 -0
- boxfab/secret/__init__.py +11 -0
- boxfab/secret/base.py +84 -0
- boxfab/secret/secret_async.py +270 -0
- boxfab/secret/secret_sync.py +270 -0
- boxfab/secret/types.py +34 -0
- boxfab/template/consts.py +33 -0
- boxfab/template/dockerfile_parser.py +634 -0
- boxfab/template/logger.py +232 -0
- boxfab/template/main.py +1315 -0
- boxfab/template/readycmd.py +144 -0
- boxfab/template/types.py +194 -0
- boxfab/template/utils.py +535 -0
- boxfab/template_async/build_api.py +449 -0
- boxfab/template_async/main.py +551 -0
- boxfab/template_sync/build_api.py +445 -0
- boxfab/template_sync/main.py +553 -0
- boxfab/volume/client/__init__.py +8 -0
- boxfab/volume/client/api/__init__.py +1 -0
- boxfab/volume/client/api/volumes/__init__.py +1 -0
- boxfab/volume/client/api/volumes/delete_volumecontent_volume_id_path.py +174 -0
- boxfab/volume/client/api/volumes/get_volumecontent_volume_id_dir.py +204 -0
- boxfab/volume/client/api/volumes/get_volumecontent_volume_id_file.py +179 -0
- boxfab/volume/client/api/volumes/get_volumecontent_volume_id_path.py +176 -0
- boxfab/volume/client/api/volumes/patch_volumecontent_volume_id_path.py +203 -0
- boxfab/volume/client/api/volumes/post_volumecontent_volume_id_dir.py +239 -0
- boxfab/volume/client/api/volumes/put_volumecontent_volume_id_file.py +259 -0
- boxfab/volume/client/client.py +286 -0
- boxfab/volume/client/errors.py +16 -0
- boxfab/volume/client/models/__init__.py +13 -0
- boxfab/volume/client/models/error.py +67 -0
- boxfab/volume/client/models/patch_volumecontent_volume_id_path_body.py +77 -0
- boxfab/volume/client/models/volume_entry_stat.py +145 -0
- boxfab/volume/client/models/volume_entry_stat_type.py +11 -0
- boxfab/volume/client/py.typed +1 -0
- boxfab/volume/client/types.py +54 -0
- boxfab/volume/client_async/__init__.py +94 -0
- boxfab/volume/client_sync/__init__.py +92 -0
- boxfab/volume/connection_config.py +166 -0
- boxfab/volume/types.py +66 -0
- boxfab/volume/utils.py +56 -0
- boxfab/volume/volume_async.py +719 -0
- boxfab/volume/volume_sync.py +691 -0
- boxfab-0.1.0a1.dist-info/METADATA +186 -0
- boxfab-0.1.0a1.dist-info/RECORD +263 -0
- boxfab-0.1.0a1.dist-info/WHEEL +4 -0
- boxfab-0.1.0a1.dist-info/licenses/LICENSE +13 -0
- boxfab-0.1.0a1.dist-info/licenses/LICENSE-E2B +9 -0
boxfab/__init__.py
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Secure sandboxed cloud environments made for AI agents and AI apps.
|
|
3
|
+
|
|
4
|
+
Check docs [here](https://e2b.dev/docs).
|
|
5
|
+
|
|
6
|
+
E2B Sandbox is a secure cloud sandbox environment made for AI agents and AI
|
|
7
|
+
apps.
|
|
8
|
+
Sandboxes allow AI agents and apps to have long running cloud secure environments.
|
|
9
|
+
In these environments, large language models can use the same tools as humans do.
|
|
10
|
+
|
|
11
|
+
E2B Python SDK supports both sync and async API:
|
|
12
|
+
|
|
13
|
+
```py
|
|
14
|
+
from boxfab import Sandbox
|
|
15
|
+
|
|
16
|
+
# Create sandbox
|
|
17
|
+
sandbox = Sandbox.create()
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```py
|
|
21
|
+
from boxfab import AsyncSandbox
|
|
22
|
+
|
|
23
|
+
# Create sandbox
|
|
24
|
+
sandbox = await AsyncSandbox.create()
|
|
25
|
+
```
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from .api import (
|
|
29
|
+
ApiClient,
|
|
30
|
+
client,
|
|
31
|
+
)
|
|
32
|
+
from .connection_config import (
|
|
33
|
+
ApiParams,
|
|
34
|
+
ConnectionConfig,
|
|
35
|
+
HttpVersion,
|
|
36
|
+
ProxyTypes,
|
|
37
|
+
Username,
|
|
38
|
+
)
|
|
39
|
+
from .volume.connection_config import VolumeApiParams, VolumeConnectionConfig
|
|
40
|
+
from .exceptions import (
|
|
41
|
+
AuthenticationException,
|
|
42
|
+
FileNotFoundException,
|
|
43
|
+
GitAuthException,
|
|
44
|
+
GitUpstreamException,
|
|
45
|
+
BuildException,
|
|
46
|
+
FileUploadException,
|
|
47
|
+
InvalidArgumentException,
|
|
48
|
+
NotEnoughSpaceException,
|
|
49
|
+
NotFoundException,
|
|
50
|
+
RateLimitException,
|
|
51
|
+
ServiceBusyException,
|
|
52
|
+
SandboxException,
|
|
53
|
+
SandboxNotFoundException,
|
|
54
|
+
SandboxNotRunningException,
|
|
55
|
+
SandboxUnreachableException,
|
|
56
|
+
TemplateException,
|
|
57
|
+
TimeoutException,
|
|
58
|
+
VolumeException,
|
|
59
|
+
VolumeNotFoundException,
|
|
60
|
+
VolumePathNotFoundException,
|
|
61
|
+
SecretException,
|
|
62
|
+
SecretNotFoundException,
|
|
63
|
+
)
|
|
64
|
+
from .sandbox.commands.command_handle import (
|
|
65
|
+
CommandExitException,
|
|
66
|
+
CommandResult,
|
|
67
|
+
PtyOutput,
|
|
68
|
+
PtySize,
|
|
69
|
+
Stderr,
|
|
70
|
+
Stdout,
|
|
71
|
+
)
|
|
72
|
+
from .sandbox.commands.main import ProcessInfo
|
|
73
|
+
from .sandbox.filesystem.filesystem import (
|
|
74
|
+
AsyncWriteData,
|
|
75
|
+
EntryInfo,
|
|
76
|
+
FileType,
|
|
77
|
+
WriteData,
|
|
78
|
+
WriteInfo,
|
|
79
|
+
)
|
|
80
|
+
from .sandbox.filesystem.watch_handle import (
|
|
81
|
+
FilesystemEvent,
|
|
82
|
+
FilesystemEventType,
|
|
83
|
+
)
|
|
84
|
+
from .sandbox._git import GitBranches, GitFileStatus, GitResetMode, GitStatus
|
|
85
|
+
from .sandbox_sync.git import Git
|
|
86
|
+
from .sandbox.network import ALL_TRAFFIC
|
|
87
|
+
from .sandbox.signature import get_signature
|
|
88
|
+
from .sandbox.sandbox_api import (
|
|
89
|
+
GitHubMcpServer,
|
|
90
|
+
GitHubMcpServerConfig,
|
|
91
|
+
McpServer,
|
|
92
|
+
SandboxEgressProxyInfo,
|
|
93
|
+
SandboxEgressProxyOpts,
|
|
94
|
+
SandboxEgressProxyTLSInfo,
|
|
95
|
+
SandboxEgressProxyTLSOpts,
|
|
96
|
+
SandboxIamOpts,
|
|
97
|
+
SandboxIamToken,
|
|
98
|
+
SandboxIamTokenType,
|
|
99
|
+
SandboxInfo,
|
|
100
|
+
SandboxInfoLifecycle,
|
|
101
|
+
SandboxMetrics,
|
|
102
|
+
SandboxLifecycle,
|
|
103
|
+
SandboxOnTimeout,
|
|
104
|
+
SandboxNetworkInfo,
|
|
105
|
+
SandboxNetworkOpts,
|
|
106
|
+
SandboxNetworkRule,
|
|
107
|
+
SandboxNetworkRuleInfo,
|
|
108
|
+
SandboxNetworkRules,
|
|
109
|
+
SandboxNetworkSelector,
|
|
110
|
+
SandboxNetworkSelectorContext,
|
|
111
|
+
SandboxNetworkTransform,
|
|
112
|
+
SandboxNetworkTransformContext,
|
|
113
|
+
SandboxNetworkTransformResolver,
|
|
114
|
+
SandboxNetworkUpdate,
|
|
115
|
+
SandboxOnResume,
|
|
116
|
+
SandboxListOrder,
|
|
117
|
+
SandboxQuery,
|
|
118
|
+
SandboxState,
|
|
119
|
+
SnapshotInfo,
|
|
120
|
+
SnapshotMode,
|
|
121
|
+
)
|
|
122
|
+
from .sandbox_async.commands.command_handle import AsyncCommandHandle
|
|
123
|
+
from .sandbox_async.commands.command_input_handle import AsyncCommandInputHandle
|
|
124
|
+
from .sandbox_async.filesystem.watch_handle import AsyncWatchHandle
|
|
125
|
+
from .sandbox_async.main import AsyncSandbox
|
|
126
|
+
from .sandbox_async.paginator import AsyncSandboxPaginator, AsyncSnapshotPaginator
|
|
127
|
+
from .sandbox_async.utils import OutputHandler
|
|
128
|
+
from .secret import (
|
|
129
|
+
AsyncSecret,
|
|
130
|
+
AsyncSecretPaginator,
|
|
131
|
+
Secret,
|
|
132
|
+
SecretInfo,
|
|
133
|
+
SecretPaginator,
|
|
134
|
+
)
|
|
135
|
+
from .sandbox_sync.commands.command_handle import CommandHandle
|
|
136
|
+
from .sandbox_sync.filesystem.watch_handle import WatchHandle
|
|
137
|
+
from .sandbox_sync.main import Sandbox
|
|
138
|
+
from .sandbox_sync.paginator import SandboxPaginator, SnapshotPaginator
|
|
139
|
+
from .template.logger import (
|
|
140
|
+
LogEntry,
|
|
141
|
+
LogEntryEnd,
|
|
142
|
+
LogEntryLevel,
|
|
143
|
+
LogEntryStart,
|
|
144
|
+
default_build_logger,
|
|
145
|
+
)
|
|
146
|
+
from .template.dockerfile_parser import DockerfileSyntaxError
|
|
147
|
+
from .template.main import TemplateBase, TemplateClass
|
|
148
|
+
from .template.readycmd import (
|
|
149
|
+
ReadyCmd,
|
|
150
|
+
wait_for_file,
|
|
151
|
+
wait_for_port,
|
|
152
|
+
wait_for_process,
|
|
153
|
+
wait_for_timeout,
|
|
154
|
+
wait_for_url,
|
|
155
|
+
)
|
|
156
|
+
from .template.types import (
|
|
157
|
+
BuildInfo,
|
|
158
|
+
BuildStatusReason,
|
|
159
|
+
CopyItem,
|
|
160
|
+
TemplateBuildStatus,
|
|
161
|
+
TemplateBuildStatusResponse,
|
|
162
|
+
TemplateTag,
|
|
163
|
+
TemplateTagInfo,
|
|
164
|
+
)
|
|
165
|
+
from .template_async.main import AsyncTemplate
|
|
166
|
+
from .template_sync.main import Template
|
|
167
|
+
|
|
168
|
+
from .volume.volume_sync import Volume
|
|
169
|
+
from .volume.volume_async import AsyncVolume
|
|
170
|
+
from .client import BoxFab, BoxFabClientParams, E2B, E2BClientParams
|
|
171
|
+
from .volume.types import (
|
|
172
|
+
VolumeInfo,
|
|
173
|
+
VolumeAndToken,
|
|
174
|
+
VolumeEntryStat,
|
|
175
|
+
VolumeFileType,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
__all__ = [
|
|
179
|
+
# Client
|
|
180
|
+
"BoxFab",
|
|
181
|
+
"BoxFabClientParams",
|
|
182
|
+
"E2B",
|
|
183
|
+
"E2BClientParams",
|
|
184
|
+
# API
|
|
185
|
+
"ApiClient",
|
|
186
|
+
"client",
|
|
187
|
+
# Connection config
|
|
188
|
+
"ConnectionConfig",
|
|
189
|
+
"VolumeConnectionConfig",
|
|
190
|
+
"HttpVersion",
|
|
191
|
+
"ProxyTypes",
|
|
192
|
+
"ApiParams",
|
|
193
|
+
"VolumeApiParams",
|
|
194
|
+
"Username",
|
|
195
|
+
# Exceptions
|
|
196
|
+
"SandboxException",
|
|
197
|
+
"ServiceBusyException",
|
|
198
|
+
"TimeoutException",
|
|
199
|
+
"NotFoundException",
|
|
200
|
+
"FileNotFoundException",
|
|
201
|
+
"SandboxNotFoundException",
|
|
202
|
+
"SandboxNotRunningException",
|
|
203
|
+
"SandboxUnreachableException",
|
|
204
|
+
"AuthenticationException",
|
|
205
|
+
"GitAuthException",
|
|
206
|
+
"GitUpstreamException",
|
|
207
|
+
"InvalidArgumentException",
|
|
208
|
+
"NotEnoughSpaceException",
|
|
209
|
+
"TemplateException",
|
|
210
|
+
"DockerfileSyntaxError",
|
|
211
|
+
"BuildException",
|
|
212
|
+
"FileUploadException",
|
|
213
|
+
"RateLimitException",
|
|
214
|
+
"VolumeException",
|
|
215
|
+
"VolumeNotFoundException",
|
|
216
|
+
"VolumePathNotFoundException",
|
|
217
|
+
# Sandbox API
|
|
218
|
+
"SandboxInfo",
|
|
219
|
+
"SandboxInfoLifecycle",
|
|
220
|
+
"SandboxMetrics",
|
|
221
|
+
"ProcessInfo",
|
|
222
|
+
"SandboxListOrder",
|
|
223
|
+
"SandboxQuery",
|
|
224
|
+
"SandboxState",
|
|
225
|
+
"SandboxMetrics",
|
|
226
|
+
"GitStatus",
|
|
227
|
+
"GitBranches",
|
|
228
|
+
"GitFileStatus",
|
|
229
|
+
"GitResetMode",
|
|
230
|
+
# Command handle
|
|
231
|
+
"CommandResult",
|
|
232
|
+
"Stderr",
|
|
233
|
+
"Stdout",
|
|
234
|
+
"CommandExitException",
|
|
235
|
+
"PtyOutput",
|
|
236
|
+
"PtySize",
|
|
237
|
+
# Filesystem
|
|
238
|
+
"FilesystemEvent",
|
|
239
|
+
"FilesystemEventType",
|
|
240
|
+
"EntryInfo",
|
|
241
|
+
"WriteInfo",
|
|
242
|
+
"WriteData",
|
|
243
|
+
"AsyncWriteData",
|
|
244
|
+
"FileType",
|
|
245
|
+
# Network
|
|
246
|
+
"SandboxEgressProxyOpts",
|
|
247
|
+
"SandboxEgressProxyInfo",
|
|
248
|
+
"SandboxEgressProxyTLSOpts",
|
|
249
|
+
"SandboxEgressProxyTLSInfo",
|
|
250
|
+
"SandboxNetworkOpts",
|
|
251
|
+
"SandboxNetworkInfo",
|
|
252
|
+
"SandboxNetworkSelector",
|
|
253
|
+
"SandboxNetworkSelectorContext",
|
|
254
|
+
"SandboxNetworkRule",
|
|
255
|
+
"SandboxNetworkRuleInfo",
|
|
256
|
+
"SandboxNetworkRules",
|
|
257
|
+
"SandboxNetworkTransform",
|
|
258
|
+
"SandboxNetworkTransformContext",
|
|
259
|
+
"SandboxNetworkTransformResolver",
|
|
260
|
+
"SandboxNetworkUpdate",
|
|
261
|
+
"SandboxLifecycle",
|
|
262
|
+
"SandboxOnTimeout",
|
|
263
|
+
"SandboxOnResume",
|
|
264
|
+
"ALL_TRAFFIC",
|
|
265
|
+
# IAM
|
|
266
|
+
"SandboxIamOpts",
|
|
267
|
+
"SandboxIamToken",
|
|
268
|
+
"SandboxIamTokenType",
|
|
269
|
+
"Secret",
|
|
270
|
+
"AsyncSecret",
|
|
271
|
+
"SecretInfo",
|
|
272
|
+
"SecretPaginator",
|
|
273
|
+
"AsyncSecretPaginator",
|
|
274
|
+
"SecretException",
|
|
275
|
+
"SecretNotFoundException",
|
|
276
|
+
# Snapshot
|
|
277
|
+
"SnapshotInfo",
|
|
278
|
+
"SnapshotMode",
|
|
279
|
+
"SnapshotPaginator",
|
|
280
|
+
"AsyncSnapshotPaginator",
|
|
281
|
+
# Signature
|
|
282
|
+
"get_signature",
|
|
283
|
+
# Sync sandbox
|
|
284
|
+
"Sandbox",
|
|
285
|
+
"SandboxPaginator",
|
|
286
|
+
"WatchHandle",
|
|
287
|
+
"CommandHandle",
|
|
288
|
+
# Async sandbox
|
|
289
|
+
"OutputHandler",
|
|
290
|
+
"AsyncSandboxPaginator",
|
|
291
|
+
"AsyncSandbox",
|
|
292
|
+
"AsyncWatchHandle",
|
|
293
|
+
"AsyncCommandHandle",
|
|
294
|
+
"AsyncCommandInputHandle",
|
|
295
|
+
# Template
|
|
296
|
+
"Template",
|
|
297
|
+
"AsyncTemplate",
|
|
298
|
+
"TemplateBase",
|
|
299
|
+
"TemplateClass",
|
|
300
|
+
"CopyItem",
|
|
301
|
+
"BuildInfo",
|
|
302
|
+
"BuildStatusReason",
|
|
303
|
+
"TemplateBuildStatus",
|
|
304
|
+
"TemplateBuildStatusResponse",
|
|
305
|
+
"TemplateTag",
|
|
306
|
+
"TemplateTagInfo",
|
|
307
|
+
"ReadyCmd",
|
|
308
|
+
"wait_for_file",
|
|
309
|
+
"wait_for_url",
|
|
310
|
+
"wait_for_port",
|
|
311
|
+
"wait_for_process",
|
|
312
|
+
"wait_for_timeout",
|
|
313
|
+
"LogEntry",
|
|
314
|
+
"LogEntryStart",
|
|
315
|
+
"LogEntryEnd",
|
|
316
|
+
"LogEntryLevel",
|
|
317
|
+
"default_build_logger",
|
|
318
|
+
# MCP
|
|
319
|
+
"McpServer",
|
|
320
|
+
"GitHubMcpServer",
|
|
321
|
+
"GitHubMcpServerConfig",
|
|
322
|
+
# Git
|
|
323
|
+
"Git",
|
|
324
|
+
# Volume
|
|
325
|
+
"Volume",
|
|
326
|
+
"AsyncVolume",
|
|
327
|
+
"VolumeInfo",
|
|
328
|
+
"VolumeAndToken",
|
|
329
|
+
"VolumeEntryStat",
|
|
330
|
+
"VolumeFileType",
|
|
331
|
+
]
|
boxfab/api/__init__.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from types import TracebackType
|
|
6
|
+
from typing import (
|
|
7
|
+
NamedTuple,
|
|
8
|
+
Optional,
|
|
9
|
+
Protocol,
|
|
10
|
+
Tuple,
|
|
11
|
+
Union,
|
|
12
|
+
)
|
|
13
|
+
from urllib.parse import quote
|
|
14
|
+
|
|
15
|
+
import httpx
|
|
16
|
+
from httpx import AsyncBaseTransport, BaseTransport, Timeout
|
|
17
|
+
from pyqwest import Proxy
|
|
18
|
+
|
|
19
|
+
from boxfab.api.client.client import AuthenticatedClient
|
|
20
|
+
from boxfab.api.client.types import Response
|
|
21
|
+
from boxfab.api.metadata import default_headers
|
|
22
|
+
from boxfab.connection_config import ConnectionConfig, ProxyTypes
|
|
23
|
+
from boxfab.exceptions import (
|
|
24
|
+
AuthenticationException,
|
|
25
|
+
InvalidArgumentException,
|
|
26
|
+
RateLimitException,
|
|
27
|
+
ServiceBusyException,
|
|
28
|
+
SandboxException,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def encode_path_param(value: str) -> str:
|
|
33
|
+
"""
|
|
34
|
+
Percent-encode a template ID, name, or alias for use as one URL path segment.
|
|
35
|
+
|
|
36
|
+
Endpoints that take a template ID also accept a name, which may be
|
|
37
|
+
namespaced (``namespace/name``) and carry a ``:tag``. Escaping those
|
|
38
|
+
separators keeps the value inside a single segment instead of splitting the
|
|
39
|
+
route, matching the JS SDK's ``encodeURIComponent``.
|
|
40
|
+
"""
|
|
41
|
+
return quote(value, safe="")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def make_logging_event_hooks(log: Optional[logging.Logger]) -> dict:
|
|
45
|
+
"""Build synchronous httpx ``event_hooks`` that log requests and responses
|
|
46
|
+
to the given ``logging.Logger``. Requests log at ``INFO``, successful
|
|
47
|
+
responses at ``INFO`` and responses with status >= 400 at ``ERROR``.
|
|
48
|
+
|
|
49
|
+
Returns no hooks when ``log`` is ``None`` so that nothing is logged unless a
|
|
50
|
+
logger was explicitly supplied."""
|
|
51
|
+
if log is None:
|
|
52
|
+
return {}
|
|
53
|
+
|
|
54
|
+
def on_request(request) -> None:
|
|
55
|
+
log.info(f"Request {request.method} {request.url}")
|
|
56
|
+
|
|
57
|
+
def on_response(response: Response) -> None:
|
|
58
|
+
if response.status_code >= 400:
|
|
59
|
+
log.error(f"Response {response.status_code}")
|
|
60
|
+
else:
|
|
61
|
+
log.info(f"Response {response.status_code}")
|
|
62
|
+
|
|
63
|
+
return {"request": [on_request], "response": [on_response]}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def make_async_logging_event_hooks(log: Optional[logging.Logger]) -> dict:
|
|
67
|
+
"""Asynchronous counterpart of :func:`make_logging_event_hooks`."""
|
|
68
|
+
if log is None:
|
|
69
|
+
return {}
|
|
70
|
+
|
|
71
|
+
async def on_request(request) -> None:
|
|
72
|
+
log.info(f"Request {request.method} {request.url}")
|
|
73
|
+
|
|
74
|
+
async def on_response(response: Response) -> None:
|
|
75
|
+
if response.status_code >= 400:
|
|
76
|
+
log.error(f"Response {response.status_code}")
|
|
77
|
+
else:
|
|
78
|
+
log.info(f"Response {response.status_code}")
|
|
79
|
+
|
|
80
|
+
return {"request": [on_request], "response": [on_response]}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
connection_retries = int(os.getenv("BOXFAB_CONNECTION_RETRIES") or "3")
|
|
84
|
+
|
|
85
|
+
# Pool tuning for the pyqwest transports, shared by the REST API, envd RPC,
|
|
86
|
+
# and envd HTTP API stacks. `pool_max_idle_per_host` is per host rather than
|
|
87
|
+
# the global idle cap the httpx transports took, which suits both: API traffic
|
|
88
|
+
# goes to a single host and each sandbox is its own host.
|
|
89
|
+
pool_idle_timeout = float(os.getenv("BOXFAB_KEEPALIVE_EXPIRY") or "300")
|
|
90
|
+
pool_max_idle_per_host = int(os.getenv("BOXFAB_MAX_KEEPALIVE_CONNECTIONS") or "20")
|
|
91
|
+
|
|
92
|
+
# pyqwest spreads an origin's HTTP/2 streams over as many connections as the
|
|
93
|
+
# server's concurrent-stream limit requires (envd's edge, which all sandboxes
|
|
94
|
+
# share, advertises 100), dialing another once every connection is full, so
|
|
95
|
+
# long-running commands never queue behind one connection. `max_connections`
|
|
96
|
+
# caps the connections to each address an origin resolves to; past it, a
|
|
97
|
+
# request waits on the least loaded connection for a stream to free up.
|
|
98
|
+
max_connections = max(1, int(os.getenv("BOXFAB_MAX_CONNECTIONS") or "200"))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ProxyConfig(NamedTuple):
|
|
102
|
+
"""The ``proxy`` connection option in the shape pyqwest transports take.
|
|
103
|
+
|
|
104
|
+
A tuple so it can key the transport caches directly: it is hashable and
|
|
105
|
+
compares by value, where a ``pyqwest.Proxy`` compares by identity and
|
|
106
|
+
would hand every call its own connection pool."""
|
|
107
|
+
|
|
108
|
+
url: str
|
|
109
|
+
auth: Optional[Tuple[str, str]] = None
|
|
110
|
+
headers: Tuple[Tuple[str, str], ...] = ()
|
|
111
|
+
|
|
112
|
+
def to_pyqwest(self) -> Proxy:
|
|
113
|
+
"""The ``pyqwest.Proxy`` to hand a transport."""
|
|
114
|
+
return Proxy(self.url, auth=self.auth, headers=self.headers or None)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def proxy_to_config(proxy: Optional[ProxyTypes]) -> Optional[ProxyConfig]:
|
|
118
|
+
"""Convert the ``proxy`` connection option — a URL string, an
|
|
119
|
+
``httpx.URL``, or an ``httpx.Proxy`` — to the proxy configuration pyqwest
|
|
120
|
+
transports take: a proxy URL (scheme http, https, socks5, or socks5h,
|
|
121
|
+
credentials allowed in the userinfo), basic-auth credentials, and headers
|
|
122
|
+
to send to the proxy. An ``httpx.Proxy`` ``ssl_context`` has no pyqwest
|
|
123
|
+
counterpart and is rejected rather than silently dropped."""
|
|
124
|
+
if proxy is None:
|
|
125
|
+
return None
|
|
126
|
+
if isinstance(proxy, str):
|
|
127
|
+
return ProxyConfig(proxy)
|
|
128
|
+
if isinstance(proxy, httpx.URL):
|
|
129
|
+
return ProxyConfig(str(proxy))
|
|
130
|
+
if isinstance(proxy, httpx.Proxy):
|
|
131
|
+
if proxy.ssl_context is not None:
|
|
132
|
+
raise InvalidArgumentException("httpx.Proxy ssl_context is not supported")
|
|
133
|
+
# httpx.Proxy splits userinfo out of the URL into `.auth`; pyqwest
|
|
134
|
+
# takes the credentials the same way, so they pass straight through.
|
|
135
|
+
return ProxyConfig(
|
|
136
|
+
str(proxy.url),
|
|
137
|
+
auth=proxy.auth,
|
|
138
|
+
headers=tuple(proxy.headers.items()),
|
|
139
|
+
)
|
|
140
|
+
raise InvalidArgumentException(
|
|
141
|
+
"Only URL-string, httpx.URL, and httpx.Proxy proxies are supported, "
|
|
142
|
+
'e.g. proxy="http://user:pass@localhost:8030"'
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass
|
|
147
|
+
class SandboxCreateResponse:
|
|
148
|
+
sandbox_id: str
|
|
149
|
+
sandbox_domain: Optional[str]
|
|
150
|
+
envd_version: str
|
|
151
|
+
envd_access_token: Optional[str]
|
|
152
|
+
traffic_access_token: Optional[str]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def api_exception_from_code(
|
|
156
|
+
status_code: int,
|
|
157
|
+
message: Optional[str] = None,
|
|
158
|
+
default_exception_class: type[Exception] = SandboxException,
|
|
159
|
+
stack_trace: Optional[TracebackType] = None,
|
|
160
|
+
) -> Exception:
|
|
161
|
+
"""Map an API error code and message to the matching exception class — the
|
|
162
|
+
same mapping :func:`handle_api_exception` applies to HTTP responses, usable
|
|
163
|
+
for error objects embedded in response bodies (e.g. per-fork results)."""
|
|
164
|
+
if status_code == 401:
|
|
165
|
+
text = f"{status_code}: Unauthorized, please check your credentials."
|
|
166
|
+
if message:
|
|
167
|
+
text += f" - {message}"
|
|
168
|
+
return AuthenticationException(text)
|
|
169
|
+
|
|
170
|
+
if status_code == 429:
|
|
171
|
+
text = f"{status_code}: Rate limit exceeded, please try again later."
|
|
172
|
+
if message:
|
|
173
|
+
text += f" - {message}"
|
|
174
|
+
return RateLimitException(text)
|
|
175
|
+
|
|
176
|
+
if status_code == 503:
|
|
177
|
+
text = f"{status_code}: Service temporarily unavailable, please retry."
|
|
178
|
+
if message:
|
|
179
|
+
text += f" - {message}"
|
|
180
|
+
return ServiceBusyException(text)
|
|
181
|
+
|
|
182
|
+
err = default_exception_class(f"{status_code}: {message}").with_traceback(
|
|
183
|
+
stack_trace
|
|
184
|
+
)
|
|
185
|
+
if isinstance(err, SandboxException):
|
|
186
|
+
err.status_code = status_code
|
|
187
|
+
|
|
188
|
+
return err
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def handle_api_exception(
|
|
192
|
+
e: "SupportsApiErrorResponse",
|
|
193
|
+
default_exception_class: type[Exception] = SandboxException,
|
|
194
|
+
stack_trace: Optional[TracebackType] = None,
|
|
195
|
+
):
|
|
196
|
+
try:
|
|
197
|
+
body = json.loads(e.content) if e.content else {}
|
|
198
|
+
except json.JSONDecodeError:
|
|
199
|
+
body = {}
|
|
200
|
+
|
|
201
|
+
message = body["message"] if "message" in body else None
|
|
202
|
+
if message is None and e.status_code not in (401, 429, 503):
|
|
203
|
+
err = default_exception_class(f"{e.status_code}: {e.content}").with_traceback(
|
|
204
|
+
stack_trace
|
|
205
|
+
)
|
|
206
|
+
if isinstance(err, SandboxException):
|
|
207
|
+
err.status_code = e.status_code
|
|
208
|
+
|
|
209
|
+
return err
|
|
210
|
+
|
|
211
|
+
return api_exception_from_code(
|
|
212
|
+
e.status_code, message, default_exception_class, stack_trace
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class SupportsApiErrorResponse(Protocol):
|
|
217
|
+
@property
|
|
218
|
+
def status_code(self) -> int: ...
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def content(self) -> Union[str, bytes]: ...
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class ApiClient(AuthenticatedClient):
|
|
225
|
+
"""
|
|
226
|
+
The client for interacting with the E2B API.
|
|
227
|
+
|
|
228
|
+
A single lazily-created httpx client (see the generated
|
|
229
|
+
``AuthenticatedClient``) serves all threads and event loops: the pyqwest
|
|
230
|
+
transports it delegates to are thread-safe and loop-independent, and
|
|
231
|
+
``httpx.Client`` is documented thread-safe.
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
def __init__(
|
|
235
|
+
self,
|
|
236
|
+
config: ConnectionConfig,
|
|
237
|
+
transport: Optional[Union[BaseTransport, AsyncBaseTransport]] = None,
|
|
238
|
+
*args,
|
|
239
|
+
**kwargs,
|
|
240
|
+
):
|
|
241
|
+
self._proxy = config.proxy
|
|
242
|
+
|
|
243
|
+
if config.api_key is None:
|
|
244
|
+
raise AuthenticationException(
|
|
245
|
+
"API key is required, get one from your BoxFab administrator (keys are shown at https://dashboard.sandd.contextfab.io). "
|
|
246
|
+
"You can either set the environment variable `BOXFAB_API_KEY` "
|
|
247
|
+
'or you can pass it directly to the method like api_key="e2b_..."',
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
token = config.api_key
|
|
251
|
+
auth_header_name = "X-API-KEY"
|
|
252
|
+
prefix = ""
|
|
253
|
+
|
|
254
|
+
self._logger = config.logger
|
|
255
|
+
|
|
256
|
+
headers = {
|
|
257
|
+
**default_headers,
|
|
258
|
+
**(config.headers or {}),
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# Prevent passing these parameters twice
|
|
262
|
+
more_headers: Optional[dict] = kwargs.pop("headers", None)
|
|
263
|
+
if more_headers:
|
|
264
|
+
headers.update(more_headers)
|
|
265
|
+
kwargs.pop("token", None)
|
|
266
|
+
kwargs.pop("auth_header_name", None)
|
|
267
|
+
kwargs.pop("prefix", None)
|
|
268
|
+
|
|
269
|
+
httpx_args = {
|
|
270
|
+
"event_hooks": self._logging_event_hooks(),
|
|
271
|
+
}
|
|
272
|
+
if transport is not None:
|
|
273
|
+
# The proxy lives in the transport; passing `proxy` here too
|
|
274
|
+
# would mount a fresh, never-closed proxy transport per client.
|
|
275
|
+
httpx_args["transport"] = transport
|
|
276
|
+
else:
|
|
277
|
+
httpx_args["proxy"] = config.proxy
|
|
278
|
+
|
|
279
|
+
# config.request_timeout is None when the timeout is explicitly
|
|
280
|
+
# disabled (request_timeout=0), which httpx.Timeout(None) preserves.
|
|
281
|
+
kwargs.setdefault("timeout", Timeout(config.request_timeout))
|
|
282
|
+
|
|
283
|
+
super().__init__(
|
|
284
|
+
base_url=config.api_url,
|
|
285
|
+
httpx_args=httpx_args,
|
|
286
|
+
headers=headers,
|
|
287
|
+
token=token or "",
|
|
288
|
+
auth_header_name=auth_header_name,
|
|
289
|
+
prefix=prefix,
|
|
290
|
+
*args,
|
|
291
|
+
**kwargs,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
def _logging_event_hooks(self) -> dict:
|
|
295
|
+
return make_logging_event_hooks(self._logger)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# We need to override the logging hooks for the async usage
|
|
299
|
+
class AsyncApiClient(ApiClient):
|
|
300
|
+
def _logging_event_hooks(self) -> dict:
|
|
301
|
+
return make_async_logging_event_hooks(self._logger)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains methods for accessing the API"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains endpoint functions for accessing the API"""
|