stackmachine 0.2.1__tar.gz → 0.3.0__tar.gz

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.
Files changed (34) hide show
  1. stackmachine-0.3.0/PKG-INFO +193 -0
  2. stackmachine-0.3.0/README.md +172 -0
  3. stackmachine-0.3.0/examples/async_usage.py +16 -0
  4. stackmachine-0.3.0/examples/basic_usage.py +13 -0
  5. stackmachine-0.3.0/examples/deploy_app.py +31 -0
  6. stackmachine-0.3.0/examples/list_apps.py +10 -0
  7. {stackmachine-0.2.1 → stackmachine-0.3.0}/pyproject.toml +20 -5
  8. stackmachine-0.3.0/src/stackmachine/__init__.py +72 -0
  9. stackmachine-0.3.0/src/stackmachine/_async_client.py +122 -0
  10. stackmachine-0.3.0/src/stackmachine/_client.py +120 -0
  11. stackmachine-0.3.0/src/stackmachine/_config.py +27 -0
  12. stackmachine-0.3.0/src/stackmachine/_errors.py +97 -0
  13. stackmachine-0.3.0/src/stackmachine/_graphql/__init__.py +1 -0
  14. stackmachine-0.3.0/src/stackmachine/_graphql/operations.py +551 -0
  15. stackmachine-0.3.0/src/stackmachine/_models.py +229 -0
  16. stackmachine-0.3.0/src/stackmachine/_pagination.py +387 -0
  17. stackmachine-0.3.0/src/stackmachine/_transport.py +562 -0
  18. stackmachine-0.3.0/src/stackmachine/_uploads.py +372 -0
  19. stackmachine-0.3.0/src/stackmachine/_utils.py +75 -0
  20. stackmachine-0.3.0/src/stackmachine/resources/__init__.py +12 -0
  21. stackmachine-0.3.0/src/stackmachine/resources/_shared.py +43 -0
  22. stackmachine-0.3.0/src/stackmachine/resources/apps.py +271 -0
  23. stackmachine-0.3.0/src/stackmachine/resources/deployments.py +398 -0
  24. stackmachine-0.3.0/src/stackmachine/resources/domains.py +298 -0
  25. stackmachine-0.3.0/src/stackmachine/resources/files.py +82 -0
  26. stackmachine-0.3.0/src/stackmachine/resources/ssh.py +633 -0
  27. stackmachine-0.3.0/src/stackmachine/resources/versions.py +210 -0
  28. stackmachine-0.3.0/tests/test_package.py +265 -0
  29. stackmachine-0.2.1/PKG-INFO +0 -42
  30. stackmachine-0.2.1/README.md +0 -24
  31. stackmachine-0.2.1/src/stackmachine/__init__.py +0 -10
  32. stackmachine-0.2.1/tests/test_package.py +0 -7
  33. {stackmachine-0.2.1 → stackmachine-0.3.0}/.gitignore +0 -0
  34. {stackmachine-0.2.1 → stackmachine-0.3.0}/src/stackmachine/py.typed +0 -0
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: stackmachine
3
+ Version: 0.3.0
4
+ Summary: Python SDK for StackMachine.
5
+ Project-URL: Homepage, https://github.com/stackmachine/sdks/tree/main/python
6
+ Project-URL: Repository, https://github.com/stackmachine/sdks
7
+ Project-URL: Issues, https://github.com/stackmachine/sdks/issues
8
+ Classifier: Development Status :: 2 - Pre-Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.9
17
+ Requires-Dist: httpx<1,>=0.27
18
+ Requires-Dist: websocket-client<2,>=1.8
19
+ Requires-Dist: websockets<16,>=12
20
+ Description-Content-Type: text/markdown
21
+
22
+ # StackMachine Python SDK
23
+
24
+ Python SDK for StackMachine. It mirrors the JavaScript SDK surface with a
25
+ Pythonic sync client and a native async client.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ uv add stackmachine
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```python
36
+ from stackmachine import StackMachine
37
+
38
+ with StackMachine("sk_stackmachine_...") as stackmachine:
39
+ app = stackmachine.apps.retrieve_by_name("my-app")
40
+ print(app.url)
41
+ ```
42
+
43
+ ```python
44
+ from stackmachine import AsyncStackMachine
45
+
46
+ async with AsyncStackMachine("sk_stackmachine_...") as stackmachine:
47
+ app = await stackmachine.apps.retrieve_by_name("my-app")
48
+ print(app.url)
49
+ ```
50
+
51
+ ## Clients
52
+
53
+ `StackMachine` exposes sync methods. `AsyncStackMachine` exposes the same
54
+ resource tree with awaitable methods.
55
+
56
+ ```python
57
+ from stackmachine import AsyncStackMachine, StackMachine
58
+
59
+ stackmachine = StackMachine("sk_stackmachine_...")
60
+ async_stackmachine = AsyncStackMachine("sk_stackmachine_...")
61
+ ```
62
+
63
+ Both clients accept JavaScript-style aliases during initialization:
64
+
65
+ ```python
66
+ stackmachine = StackMachine.init(
67
+ {
68
+ "token": "sk_stackmachine_...",
69
+ "apiUrl": "https://api.stackmachine.com/graphql",
70
+ "maxNetworkRetries": 2,
71
+ }
72
+ )
73
+ ```
74
+
75
+ ## Apps
76
+
77
+ ```python
78
+ apps = stackmachine.apps.list(limit=10)
79
+
80
+ for app in apps:
81
+ print(app.id, app.name)
82
+
83
+ app = stackmachine.apps.retrieve("app_id")
84
+ app = stackmachine.apps.retrieve_by_name("my-app")
85
+ apps = stackmachine.apps.retrieve_many(["app_1", "app_2"])
86
+ stackmachine.apps.delete("app_id")
87
+ ```
88
+
89
+ Async lists can either be awaited for the first page or iterated directly:
90
+
91
+ ```python
92
+ apps = async_stackmachine.apps.list(limit=10)
93
+ first_page = await apps
94
+
95
+ async for app in apps:
96
+ print(app.name)
97
+ ```
98
+
99
+ ## Deployments
100
+
101
+ ```python
102
+ deployment = stackmachine.deployments.create(
103
+ {
104
+ "file": "https://example.com/app.zip",
105
+ "name": "my-app",
106
+ }
107
+ )
108
+
109
+ version = deployment.wait()
110
+ ```
111
+
112
+ ```python
113
+ deployment = stackmachine.apps.autobuild(
114
+ {
115
+ "file": "https://example.com/app.zip",
116
+ "name": "my-app",
117
+ }
118
+ )
119
+ ```
120
+
121
+ ## Files
122
+
123
+ ```python
124
+ from stackmachine import create_zip
125
+
126
+ zip_bytes = create_zip({"index.html": "<h1>Hello</h1>"})
127
+ url = stackmachine.files.upload(zip_bytes)
128
+ ```
129
+
130
+ ## Domains
131
+
132
+ ```python
133
+ domain = stackmachine.apps.domains.create(
134
+ app="app_id",
135
+ hostname="example.com",
136
+ )
137
+
138
+ is_verified = stackmachine.apps.domains.verify(domain.id)
139
+ stackmachine.apps.domains.delete(domain.id)
140
+ ```
141
+
142
+ ## SSH
143
+
144
+ ```python
145
+ server = stackmachine.apps.ssh.retrieve("app_id")
146
+ server = stackmachine.apps.ssh.update("app_id", enabled=True)
147
+ token = stackmachine.apps.ssh.tokens.create(app="app_id")
148
+
149
+ users = stackmachine.apps.ssh.users.list(app="app_id")
150
+ user = stackmachine.apps.ssh.users.retrieve("ssh_user_id")
151
+ password = stackmachine.apps.ssh.users.passwords.rotate("ssh_user_id")
152
+
153
+ key = stackmachine.apps.ssh.users.authorized_keys.create(
154
+ user="ssh_user_id",
155
+ public_key="ssh-ed25519 AAAA...",
156
+ )
157
+ ```
158
+
159
+ ## Request Options
160
+
161
+ Most methods accept `request_options` for per-request configuration:
162
+
163
+ ```python
164
+ app = stackmachine.apps.retrieve(
165
+ "app_id",
166
+ request_options={
167
+ "api_key": "sk_stackmachine_other",
168
+ "timeout": 30,
169
+ "idempotency_key": "deploy-123",
170
+ },
171
+ )
172
+ ```
173
+
174
+ ## Development
175
+
176
+ ```bash
177
+ cd python
178
+ uv sync --dev
179
+ uv run ruff check src examples tests
180
+ uv run pytest
181
+ uv build --no-sources
182
+ ```
183
+
184
+ ## Manual Publish
185
+
186
+ Use this path only for manual package uploads:
187
+
188
+ ```bash
189
+ cd python
190
+ uv sync --dev
191
+ uv build --no-sources
192
+ uv publish
193
+ ```
@@ -0,0 +1,172 @@
1
+ # StackMachine Python SDK
2
+
3
+ Python SDK for StackMachine. It mirrors the JavaScript SDK surface with a
4
+ Pythonic sync client and a native async client.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ uv add stackmachine
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```python
15
+ from stackmachine import StackMachine
16
+
17
+ with StackMachine("sk_stackmachine_...") as stackmachine:
18
+ app = stackmachine.apps.retrieve_by_name("my-app")
19
+ print(app.url)
20
+ ```
21
+
22
+ ```python
23
+ from stackmachine import AsyncStackMachine
24
+
25
+ async with AsyncStackMachine("sk_stackmachine_...") as stackmachine:
26
+ app = await stackmachine.apps.retrieve_by_name("my-app")
27
+ print(app.url)
28
+ ```
29
+
30
+ ## Clients
31
+
32
+ `StackMachine` exposes sync methods. `AsyncStackMachine` exposes the same
33
+ resource tree with awaitable methods.
34
+
35
+ ```python
36
+ from stackmachine import AsyncStackMachine, StackMachine
37
+
38
+ stackmachine = StackMachine("sk_stackmachine_...")
39
+ async_stackmachine = AsyncStackMachine("sk_stackmachine_...")
40
+ ```
41
+
42
+ Both clients accept JavaScript-style aliases during initialization:
43
+
44
+ ```python
45
+ stackmachine = StackMachine.init(
46
+ {
47
+ "token": "sk_stackmachine_...",
48
+ "apiUrl": "https://api.stackmachine.com/graphql",
49
+ "maxNetworkRetries": 2,
50
+ }
51
+ )
52
+ ```
53
+
54
+ ## Apps
55
+
56
+ ```python
57
+ apps = stackmachine.apps.list(limit=10)
58
+
59
+ for app in apps:
60
+ print(app.id, app.name)
61
+
62
+ app = stackmachine.apps.retrieve("app_id")
63
+ app = stackmachine.apps.retrieve_by_name("my-app")
64
+ apps = stackmachine.apps.retrieve_many(["app_1", "app_2"])
65
+ stackmachine.apps.delete("app_id")
66
+ ```
67
+
68
+ Async lists can either be awaited for the first page or iterated directly:
69
+
70
+ ```python
71
+ apps = async_stackmachine.apps.list(limit=10)
72
+ first_page = await apps
73
+
74
+ async for app in apps:
75
+ print(app.name)
76
+ ```
77
+
78
+ ## Deployments
79
+
80
+ ```python
81
+ deployment = stackmachine.deployments.create(
82
+ {
83
+ "file": "https://example.com/app.zip",
84
+ "name": "my-app",
85
+ }
86
+ )
87
+
88
+ version = deployment.wait()
89
+ ```
90
+
91
+ ```python
92
+ deployment = stackmachine.apps.autobuild(
93
+ {
94
+ "file": "https://example.com/app.zip",
95
+ "name": "my-app",
96
+ }
97
+ )
98
+ ```
99
+
100
+ ## Files
101
+
102
+ ```python
103
+ from stackmachine import create_zip
104
+
105
+ zip_bytes = create_zip({"index.html": "<h1>Hello</h1>"})
106
+ url = stackmachine.files.upload(zip_bytes)
107
+ ```
108
+
109
+ ## Domains
110
+
111
+ ```python
112
+ domain = stackmachine.apps.domains.create(
113
+ app="app_id",
114
+ hostname="example.com",
115
+ )
116
+
117
+ is_verified = stackmachine.apps.domains.verify(domain.id)
118
+ stackmachine.apps.domains.delete(domain.id)
119
+ ```
120
+
121
+ ## SSH
122
+
123
+ ```python
124
+ server = stackmachine.apps.ssh.retrieve("app_id")
125
+ server = stackmachine.apps.ssh.update("app_id", enabled=True)
126
+ token = stackmachine.apps.ssh.tokens.create(app="app_id")
127
+
128
+ users = stackmachine.apps.ssh.users.list(app="app_id")
129
+ user = stackmachine.apps.ssh.users.retrieve("ssh_user_id")
130
+ password = stackmachine.apps.ssh.users.passwords.rotate("ssh_user_id")
131
+
132
+ key = stackmachine.apps.ssh.users.authorized_keys.create(
133
+ user="ssh_user_id",
134
+ public_key="ssh-ed25519 AAAA...",
135
+ )
136
+ ```
137
+
138
+ ## Request Options
139
+
140
+ Most methods accept `request_options` for per-request configuration:
141
+
142
+ ```python
143
+ app = stackmachine.apps.retrieve(
144
+ "app_id",
145
+ request_options={
146
+ "api_key": "sk_stackmachine_other",
147
+ "timeout": 30,
148
+ "idempotency_key": "deploy-123",
149
+ },
150
+ )
151
+ ```
152
+
153
+ ## Development
154
+
155
+ ```bash
156
+ cd python
157
+ uv sync --dev
158
+ uv run ruff check src examples tests
159
+ uv run pytest
160
+ uv build --no-sources
161
+ ```
162
+
163
+ ## Manual Publish
164
+
165
+ Use this path only for manual package uploads:
166
+
167
+ ```bash
168
+ cd python
169
+ uv sync --dev
170
+ uv build --no-sources
171
+ uv publish
172
+ ```
@@ -0,0 +1,16 @@
1
+ import asyncio
2
+ import os
3
+
4
+ from stackmachine import AsyncStackMachine
5
+
6
+
7
+ async def main():
8
+ async with AsyncStackMachine(os.environ["STACKMACHINE_API_KEY"]) as client:
9
+ viewer = await client.viewer()
10
+ print(viewer.username if viewer else "anonymous")
11
+
12
+ async for app in client.apps.list(limit=10):
13
+ print(app.name, app.url)
14
+
15
+
16
+ asyncio.run(main())
@@ -0,0 +1,13 @@
1
+ import os
2
+
3
+ from stackmachine import StackMachine
4
+
5
+ client = StackMachine(os.environ["STACKMACHINE_API_KEY"])
6
+
7
+ viewer = client.viewer()
8
+ print(viewer.username if viewer else "anonymous")
9
+
10
+ for app in client.apps.list(limit=10):
11
+ print(app.name, app.url)
12
+
13
+ client.close()
@@ -0,0 +1,31 @@
1
+ import os
2
+
3
+ from stackmachine import StackMachine, create_zip
4
+
5
+ client = StackMachine(os.environ["STACKMACHINE_API_KEY"])
6
+
7
+ zip_file = create_zip(
8
+ {
9
+ "index.php": "<html><body><h1>Hello StackMachine</h1></body></html>",
10
+ }
11
+ )
12
+
13
+ upload_url = client.files.upload(
14
+ zip_file,
15
+ on_progress=lambda progress: print("Uploading", progress.percent * 100, "%"),
16
+ )
17
+
18
+ deployment = client.deployments.create(
19
+ app_name="hello-stackmachine",
20
+ owner="stackmachine",
21
+ upload_url=upload_url,
22
+ )
23
+
24
+ app_version = deployment.wait(
25
+ on_progress=lambda event: print(
26
+ event.datetime, event.stream, event.kind, event.message
27
+ )
28
+ )
29
+
30
+ print(app_version.app.url)
31
+ client.close()
@@ -0,0 +1,10 @@
1
+ import os
2
+
3
+ from stackmachine import StackMachine
4
+
5
+ with StackMachine(os.environ["STACKMACHINE_API_KEY"]) as client:
6
+ apps = client.apps.list(limit=25)
7
+ print("First page:", len(apps.data))
8
+
9
+ for app in apps:
10
+ print(app.name, app.url)
@@ -4,11 +4,15 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "stackmachine"
7
- version = "0.2.1"
7
+ version = "0.3.0"
8
8
  description = "Python SDK for StackMachine."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
11
- dependencies = []
11
+ dependencies = [
12
+ "httpx>=0.27,<1",
13
+ "websocket-client>=1.8,<2",
14
+ "websockets>=12,<16",
15
+ ]
12
16
  classifiers = [
13
17
  "Development Status :: 2 - Pre-Alpha",
14
18
  "Intended Audience :: Developers",
@@ -28,18 +32,29 @@ Issues = "https://github.com/stackmachine/sdks/issues"
28
32
  [dependency-groups]
29
33
  dev = [
30
34
  "pytest>=8.0",
35
+ "pytest-asyncio>=0.24",
36
+ "ruff>=0.8",
31
37
  ]
32
38
 
39
+ [tool.ruff]
40
+ line-length = 88
41
+ target-version = "py39"
42
+
43
+ [tool.ruff.lint]
44
+ select = ["E", "F", "I", "B"]
45
+
46
+ [tool.pytest.ini_options]
47
+ testpaths = ["tests"]
48
+ asyncio_mode = "auto"
49
+
33
50
  [tool.hatch.build.targets.wheel]
34
51
  packages = ["src/stackmachine"]
35
52
 
36
53
  [tool.hatch.build.targets.sdist]
37
54
  include = [
38
55
  "/README.md",
56
+ "/examples",
39
57
  "/pyproject.toml",
40
58
  "/src",
41
59
  "/tests",
42
60
  ]
43
-
44
- [tool.pytest.ini_options]
45
- testpaths = ["tests"]
@@ -0,0 +1,72 @@
1
+ """StackMachine Python SDK."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ from ._async_client import AsyncStackMachine
6
+ from ._client import StackMachine
7
+ from ._config import RequestOptions
8
+ from ._errors import (
9
+ StackMachineAPIError,
10
+ StackMachineAuthenticationError,
11
+ StackMachineConnectionError,
12
+ StackMachineError,
13
+ StackMachineGraphQLError,
14
+ StackMachineInvalidRequestError,
15
+ StackMachinePermissionError,
16
+ StackMachineRateLimitError,
17
+ StackMachineValidationError,
18
+ is_stackmachine_error,
19
+ )
20
+ from ._models import (
21
+ AppAlias,
22
+ AppSshServer,
23
+ DeployApp,
24
+ DeployAppKindWordPress,
25
+ DeployAppVersion,
26
+ DeploymentProgress,
27
+ ExpectedDNSRecord,
28
+ Log,
29
+ SshAuthorizedKey,
30
+ SshUser,
31
+ UploadProgress,
32
+ Viewer,
33
+ )
34
+ from ._pagination import AsyncStackMachineList, StackMachineList
35
+ from ._uploads import create_zip
36
+
37
+ try:
38
+ __version__ = version("stackmachine")
39
+ except PackageNotFoundError:
40
+ __version__ = "0.3.0"
41
+
42
+ __all__ = [
43
+ "AppAlias",
44
+ "AppSshServer",
45
+ "AsyncStackMachine",
46
+ "AsyncStackMachineList",
47
+ "DeployApp",
48
+ "DeployAppKindWordPress",
49
+ "DeployAppVersion",
50
+ "DeploymentProgress",
51
+ "ExpectedDNSRecord",
52
+ "Log",
53
+ "RequestOptions",
54
+ "SshAuthorizedKey",
55
+ "SshUser",
56
+ "StackMachine",
57
+ "StackMachineAPIError",
58
+ "StackMachineAuthenticationError",
59
+ "StackMachineConnectionError",
60
+ "StackMachineError",
61
+ "StackMachineGraphQLError",
62
+ "StackMachineInvalidRequestError",
63
+ "StackMachineList",
64
+ "StackMachinePermissionError",
65
+ "StackMachineRateLimitError",
66
+ "StackMachineValidationError",
67
+ "UploadProgress",
68
+ "Viewer",
69
+ "__version__",
70
+ "create_zip",
71
+ "is_stackmachine_error",
72
+ ]
@@ -0,0 +1,122 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Mapping, Optional
4
+
5
+ import httpx
6
+
7
+ from ._config import (
8
+ DEFAULT_API_URL,
9
+ DEFAULT_MAX_NETWORK_RETRIES,
10
+ DEFAULT_TIMEOUT,
11
+ ClientConfig,
12
+ )
13
+ from ._graphql import operations as gql
14
+ from ._models import Viewer
15
+ from ._transport import AsyncTransport
16
+ from ._uploads import AsyncUploader
17
+ from .resources.apps import AsyncDeployAppsResource
18
+ from .resources.deployments import AsyncDeploymentsResource
19
+ from .resources.files import AsyncFilesResource
20
+
21
+
22
+ class AsyncStackMachine:
23
+ def __init__(
24
+ self,
25
+ api_key: str,
26
+ *,
27
+ api_url: str = DEFAULT_API_URL,
28
+ headers: Optional[Mapping[str, str]] = None,
29
+ timeout: float = DEFAULT_TIMEOUT,
30
+ max_network_retries: int = DEFAULT_MAX_NETWORK_RETRIES,
31
+ http_client: Optional[httpx.AsyncClient] = None,
32
+ http_transport: Optional[httpx.AsyncBaseTransport] = None,
33
+ ) -> None:
34
+ self.api_key = api_key
35
+ self.api_url = api_url
36
+ self.apiUrl = api_url
37
+ self.timeout = timeout
38
+ self.max_network_retries = max_network_retries
39
+ self.maxNetworkRetries = max_network_retries
40
+ self._config = ClientConfig(
41
+ api_url=api_url,
42
+ headers=headers,
43
+ timeout=timeout,
44
+ max_network_retries=max_network_retries,
45
+ )
46
+ self._transport = AsyncTransport(
47
+ api_key,
48
+ self._config,
49
+ http_client=http_client,
50
+ http_transport=http_transport,
51
+ )
52
+ self.deployments = AsyncDeploymentsResource(self)
53
+ self.apps = AsyncDeployAppsResource(self, self.deployments)
54
+ self.files = AsyncFilesResource(self, AsyncUploader(self._transport))
55
+
56
+ @classmethod
57
+ def init(
58
+ cls,
59
+ settings: Optional[Mapping[str, Any]] = None,
60
+ **kwargs: Any,
61
+ ) -> "AsyncStackMachine":
62
+ values = {**dict(settings or {}), **kwargs}
63
+ api_key = values.pop("api_key", None) or values.pop("token", None) or ""
64
+ if "apiUrl" in values:
65
+ values["api_url"] = values.pop("apiUrl")
66
+ if "maxNetworkRetries" in values:
67
+ values["max_network_retries"] = values.pop("maxNetworkRetries")
68
+ return cls(api_key, **values)
69
+
70
+ async def close(self) -> None:
71
+ await self._transport.close()
72
+
73
+ async def __aenter__(self) -> "AsyncStackMachine":
74
+ return self
75
+
76
+ async def __aexit__(self, *exc_info: object) -> None:
77
+ await self.close()
78
+
79
+ async def _query(
80
+ self,
81
+ query: str,
82
+ variables: Optional[Mapping[str, Any]] = None,
83
+ *,
84
+ request_options: Optional[Mapping[str, Any]] = None,
85
+ ) -> Any:
86
+ return await self._transport.execute(
87
+ query,
88
+ variables,
89
+ request_options=request_options,
90
+ )
91
+
92
+ async def _mutation(
93
+ self,
94
+ query: str,
95
+ variables: Optional[Mapping[str, Any]] = None,
96
+ *,
97
+ request_options: Optional[Mapping[str, Any]] = None,
98
+ ) -> Any:
99
+ return await self._transport.execute(
100
+ query,
101
+ variables,
102
+ request_options=request_options,
103
+ mutation=True,
104
+ )
105
+
106
+ def _subscribe_deployment(
107
+ self, build_id: str, request_options: Optional[Mapping[str, Any]] = None
108
+ ):
109
+ return self._transport.subscribe(
110
+ gql.AUTOBUILD_SUBSCRIPTION,
111
+ {"buildId": build_id},
112
+ request_options=request_options,
113
+ )
114
+
115
+ async def viewer(
116
+ self, *, request_options: Optional[Mapping[str, Any]] = None
117
+ ) -> Optional[Viewer]:
118
+ response = await self._query(
119
+ gql.VIEWER_QUERY, {}, request_options=request_options
120
+ )
121
+ viewer = response.get("viewer") if response else None
122
+ return Viewer(username=viewer["username"]) if viewer else None