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.
- stackmachine-0.3.0/PKG-INFO +193 -0
- stackmachine-0.3.0/README.md +172 -0
- stackmachine-0.3.0/examples/async_usage.py +16 -0
- stackmachine-0.3.0/examples/basic_usage.py +13 -0
- stackmachine-0.3.0/examples/deploy_app.py +31 -0
- stackmachine-0.3.0/examples/list_apps.py +10 -0
- {stackmachine-0.2.1 → stackmachine-0.3.0}/pyproject.toml +20 -5
- stackmachine-0.3.0/src/stackmachine/__init__.py +72 -0
- stackmachine-0.3.0/src/stackmachine/_async_client.py +122 -0
- stackmachine-0.3.0/src/stackmachine/_client.py +120 -0
- stackmachine-0.3.0/src/stackmachine/_config.py +27 -0
- stackmachine-0.3.0/src/stackmachine/_errors.py +97 -0
- stackmachine-0.3.0/src/stackmachine/_graphql/__init__.py +1 -0
- stackmachine-0.3.0/src/stackmachine/_graphql/operations.py +551 -0
- stackmachine-0.3.0/src/stackmachine/_models.py +229 -0
- stackmachine-0.3.0/src/stackmachine/_pagination.py +387 -0
- stackmachine-0.3.0/src/stackmachine/_transport.py +562 -0
- stackmachine-0.3.0/src/stackmachine/_uploads.py +372 -0
- stackmachine-0.3.0/src/stackmachine/_utils.py +75 -0
- stackmachine-0.3.0/src/stackmachine/resources/__init__.py +12 -0
- stackmachine-0.3.0/src/stackmachine/resources/_shared.py +43 -0
- stackmachine-0.3.0/src/stackmachine/resources/apps.py +271 -0
- stackmachine-0.3.0/src/stackmachine/resources/deployments.py +398 -0
- stackmachine-0.3.0/src/stackmachine/resources/domains.py +298 -0
- stackmachine-0.3.0/src/stackmachine/resources/files.py +82 -0
- stackmachine-0.3.0/src/stackmachine/resources/ssh.py +633 -0
- stackmachine-0.3.0/src/stackmachine/resources/versions.py +210 -0
- stackmachine-0.3.0/tests/test_package.py +265 -0
- stackmachine-0.2.1/PKG-INFO +0 -42
- stackmachine-0.2.1/README.md +0 -24
- stackmachine-0.2.1/src/stackmachine/__init__.py +0 -10
- stackmachine-0.2.1/tests/test_package.py +0 -7
- {stackmachine-0.2.1 → stackmachine-0.3.0}/.gitignore +0 -0
- {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()
|
|
@@ -4,11 +4,15 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "stackmachine"
|
|
7
|
-
version = "0.
|
|
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
|