parse-sdk 0.1.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.
- parse_sdk-0.1.0/.gitignore +18 -0
- parse_sdk-0.1.0/CHANGELOG.md +18 -0
- parse_sdk-0.1.0/LICENSE +21 -0
- parse_sdk-0.1.0/PKG-INFO +177 -0
- parse_sdk-0.1.0/README.md +147 -0
- parse_sdk-0.1.0/parse_sdk/__init__.py +57 -0
- parse_sdk-0.1.0/parse_sdk/_labels.py +51 -0
- parse_sdk-0.1.0/parse_sdk/_oauth.py +301 -0
- parse_sdk-0.1.0/parse_sdk/_project.py +184 -0
- parse_sdk-0.1.0/parse_sdk/_runtime.py +2142 -0
- parse_sdk-0.1.0/parse_sdk/_sanitize.py +15 -0
- parse_sdk-0.1.0/parse_sdk/_sync.py +887 -0
- parse_sdk-0.1.0/parse_sdk/checks.py +601 -0
- parse_sdk-0.1.0/parse_sdk/cli.py +1667 -0
- parse_sdk-0.1.0/parse_sdk/cli_help.py +110 -0
- parse_sdk-0.1.0/parse_sdk/codegen_reconcile.py +157 -0
- parse_sdk-0.1.0/parse_sdk/codegen_v2.py +3361 -0
- parse_sdk-0.1.0/parse_sdk/config.py +349 -0
- parse_sdk-0.1.0/parse_sdk/docgen.py +587 -0
- parse_sdk-0.1.0/parse_sdk/doctor.py +348 -0
- parse_sdk-0.1.0/parse_sdk/migrate.py +212 -0
- parse_sdk-0.1.0/parse_sdk/preview.py +588 -0
- parse_sdk-0.1.0/parse_sdk/py.typed +0 -0
- parse_sdk-0.1.0/parse_sdk/resource_surface.py +196 -0
- parse_sdk-0.1.0/parse_sdk/sample_gate.py +245 -0
- parse_sdk-0.1.0/parse_sdk/scaffold.py +273 -0
- parse_sdk-0.1.0/pyproject.toml +87 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
.venv/
|
|
7
|
+
venv/
|
|
8
|
+
parse_stubs/
|
|
9
|
+
uv.lock
|
|
10
|
+
.coverage
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.dev/
|
|
15
|
+
|
|
16
|
+
# Personal working-notes / superpowers plans+specs — keep out of the shared repo.
|
|
17
|
+
docs/superpowers/
|
|
18
|
+
/plans/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `parse-sdk` are documented here. This project follows
|
|
4
|
+
[Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] — first public release
|
|
7
|
+
|
|
8
|
+
Initial public release of the `parse-sdk` runtime + `parse` CLI: a
|
|
9
|
+
dynamic-yet-typed Python SDK that generates a project-local, fully typed client
|
|
10
|
+
for the Parse APIs your key can call.
|
|
11
|
+
|
|
12
|
+
- **CLI** (`parse`): `init`, `sync`, `login` (API key or browser OAuth via
|
|
13
|
+
`--web`), `doctor` (diagnose / `--fix`), `list`, `whoami`, `help`, `clean`,
|
|
14
|
+
and marketplace `add` / `search` / `remove`, plus `[tool.parse]`-driven
|
|
15
|
+
selective sync.
|
|
16
|
+
- **Typed runtime**: resources, collections, pagination, retries with a
|
|
17
|
+
wall-clock budget, and host-bound authentication.
|
|
18
|
+
- Ships `py.typed`; supports Python 3.10–3.14.
|
parse_sdk-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Parse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
parse_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: parse-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Dynamic-yet-typed Python SDK for Parse APIs
|
|
5
|
+
Project-URL: Homepage, https://parse.bot
|
|
6
|
+
Project-URL: Changelog, https://pypi.org/project/parse-sdk/#history
|
|
7
|
+
Author-email: Parse <team@parse.bot>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agents,api-client,codegen,parse,scraping,sdk
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: click>=8.1
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Requires-Dist: tomli>=1.1.0; python_version < '3.11'
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pyright>=1.1.408; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
28
|
+
Requires-Dist: tomli>=2; (python_version < '3.11') and extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# parse-sdk
|
|
32
|
+
|
|
33
|
+
Typed Python SDK tooling for [Parse](https://parse.bot) APIs.
|
|
34
|
+
|
|
35
|
+
`parse-sdk` is the shared **runtime + CLI**. It generates a project-local
|
|
36
|
+
`parse_apis` package — a real, installed, editable dependency — that holds the
|
|
37
|
+
typed client for every API your key can call. Your application code imports from
|
|
38
|
+
`parse_apis`; `parse-sdk` provides the runtime bases and errors those clients use.
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
In a uv-managed project (`uv init` first if you don't have a `pyproject.toml`):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv add parse-sdk # installs the runtime + `parse` CLI into your project
|
|
46
|
+
uv run parse init # prompts for your API key, then scaffolds + syncs parse_apis
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Non-interactive (CI, agents, scripts): authenticate first with
|
|
50
|
+
`uv run parse login --api-key YOUR_KEY` (add `--base-url …` for a non-default
|
|
51
|
+
host), or export `PARSE_API_KEY` — then `uv run parse init` runs without prompts.
|
|
52
|
+
|
|
53
|
+
`parse init` is the one-time setup. It:
|
|
54
|
+
|
|
55
|
+
1. ensures credentials (prompts and stores to `~/.config/parse/credentials`, or run `parse login` first);
|
|
56
|
+
2. creates the committed `parse_apis` scaffold;
|
|
57
|
+
3. records `parse_apis` as an **editable path dependency** (and a `[tool.uv.sources]` entry);
|
|
58
|
+
4. appends a deny-by-default `.gitignore` block (commits the scaffold, ignores generated payload);
|
|
59
|
+
5. migrates — or safely refuses — any legacy flat `parse_apis/` layout;
|
|
60
|
+
6. runs the first `parse sync`.
|
|
61
|
+
|
|
62
|
+
Then use it:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from parse_apis.reddit_com_api import Reddit # generated, typed client (slugs look like <domain>_<tld>_api)
|
|
66
|
+
from parse_apis import RateLimitError # runtime errors, re-exported
|
|
67
|
+
|
|
68
|
+
reddit = Reddit() # picks up `parse login` credentials
|
|
69
|
+
try:
|
|
70
|
+
# Navigate from a constructible resource, with a bounded fetch:
|
|
71
|
+
for post in reddit.subreddit("smallbusiness").search_posts(query="hiring", limit=10):
|
|
72
|
+
print(post.title)
|
|
73
|
+
except RateLimitError:
|
|
74
|
+
...
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
(The exact methods are generated from *your* API's spec — `parse_apis/<slug>/README.md`
|
|
78
|
+
and `example.py` document each client's real surface.)
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
uv run python app.py # run your app in the project environment
|
|
82
|
+
uv run parse sync # later: refresh after you change APIs in Parse
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Running commands (`uv run` vs. activation)
|
|
86
|
+
|
|
87
|
+
`uv add parse-sdk` installs the `parse` console script into your project's `.venv`,
|
|
88
|
+
which isn't on your shell `PATH` by default. Two equivalent ways to reach it:
|
|
89
|
+
|
|
90
|
+
- **`uv run parse <cmd>`** — runs the venv's `parse` with no activation, auto-syncing first. (Recommended.)
|
|
91
|
+
- **`source .venv/bin/activate`** then `parse <cmd>` directly.
|
|
92
|
+
|
|
93
|
+
Prefer one of these over a global `uv tool install parse-sdk`: codegen must run with
|
|
94
|
+
the **same** pinned `parse-sdk` your project runs against (see *Versioning* below), and a
|
|
95
|
+
global copy can drift out of sync. Your end users running their *app* never invoke `parse` —
|
|
96
|
+
they just `import parse_apis`.
|
|
97
|
+
|
|
98
|
+
## On-disk layout
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
parse_apis/
|
|
102
|
+
pyproject.toml # committed: name="parse-apis", pinned parse-sdk==X
|
|
103
|
+
src/parse_apis/
|
|
104
|
+
__init__.py # committed scaffold — re-exports runtime errors
|
|
105
|
+
py.typed # committed
|
|
106
|
+
reddit/__init__.py # generated (gitignored)
|
|
107
|
+
_manifest.json # generated (gitignored)
|
|
108
|
+
AGENTS.md CLAUDE.md # generated cross-API index (gitignored)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Commit the three scaffold files; the generated payload is per-key (it reveals your API
|
|
112
|
+
inventory) and stays gitignored. `parse sync` builds the next payload in a staging tree,
|
|
113
|
+
self-tests it (secret scan, import-model, no-stale-docs), and promotes it with an atomic
|
|
114
|
+
directory swap — a failed sync never leaves broken or secret-bearing files in your project.
|
|
115
|
+
|
|
116
|
+
## Import model
|
|
117
|
+
|
|
118
|
+
- Application code imports generated clients from `parse_apis.<slug>`.
|
|
119
|
+
- Runtime errors/base types are re-exported from `parse_apis` (sourced from `parse_sdk`).
|
|
120
|
+
- Because `parse_apis` is an **installed** editable package, imports resolve from any
|
|
121
|
+
directory — no `sys.path` or editor `extraPaths` tricks.
|
|
122
|
+
- Generated files are project-local and differ per Parse API key.
|
|
123
|
+
|
|
124
|
+
## Fresh clone & CI (post-publish)
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv sync # installs deps incl. the editable parse_apis scaffold
|
|
128
|
+
uv run parse sync # regenerate the per-key payload
|
|
129
|
+
uv run pytest && uv run pyright # pyright must run against the PROJECT env —
|
|
130
|
+
# add both as dev deps (`uv add --dev pytest pyright`);
|
|
131
|
+
# a bare/global `pyright` can't resolve parse_apis
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The committed scaffold is a valid dependency target before any slugs exist, so `uv sync`
|
|
135
|
+
succeeds on a clean checkout.
|
|
136
|
+
|
|
137
|
+
## CLI
|
|
138
|
+
|
|
139
|
+
| Command | What it does |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| `parse login` | Save API key + base URL to local credentials. No project changes. |
|
|
142
|
+
| `parse init` | One-time: ensure creds, scaffold `parse_apis`, record deps + gitignore, first sync. |
|
|
143
|
+
| `parse sync` | Regenerate the typed payload into `parse_apis/src/parse_apis` (staged + atomic). |
|
|
144
|
+
| `parse clean` | Remove generated payload; keep the committed scaffold. |
|
|
145
|
+
| `parse list` | Print the APIs the current key can call (no generation). |
|
|
146
|
+
| `parse whoami` | Show the current key + base URL. |
|
|
147
|
+
|
|
148
|
+
Run `parse <cmd> --help` for full per-command help.
|
|
149
|
+
|
|
150
|
+
## Versioning
|
|
151
|
+
|
|
152
|
+
`parse_apis/pyproject.toml` pins the **exact** `parse-sdk` it was generated against — generated
|
|
153
|
+
code subclasses the runtime by name. After upgrading `parse-sdk`, **re-run `parse sync`** to
|
|
154
|
+
regenerate against the new runtime; `init`/`sync` restamp the pin.
|
|
155
|
+
|
|
156
|
+
## Env vars
|
|
157
|
+
|
|
158
|
+
| Var | Default | Notes |
|
|
159
|
+
| --- | --- | --- |
|
|
160
|
+
| `PARSE_API_KEY` | – | Auth for real requests (or use `parse login`). |
|
|
161
|
+
| `PARSE_API_BASE_URL` | `https://api.parse.bot` | Locally: `http://localhost:8001`. |
|
|
162
|
+
|
|
163
|
+
## Changelog
|
|
164
|
+
|
|
165
|
+
Release history is on the PyPI project page:
|
|
166
|
+
<https://pypi.org/project/parse-sdk/#history>.
|
|
167
|
+
|
|
168
|
+
## Security
|
|
169
|
+
|
|
170
|
+
To report a security issue, email **security@parse.bot** — please do not open
|
|
171
|
+
a public issue.
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
[MIT](LICENSE) © 2026 Parse. The license covers the `parse-sdk` runtime,
|
|
176
|
+
codegen, and CLI; use of the Parse API/service is governed separately by
|
|
177
|
+
Parse's terms.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# parse-sdk
|
|
2
|
+
|
|
3
|
+
Typed Python SDK tooling for [Parse](https://parse.bot) APIs.
|
|
4
|
+
|
|
5
|
+
`parse-sdk` is the shared **runtime + CLI**. It generates a project-local
|
|
6
|
+
`parse_apis` package — a real, installed, editable dependency — that holds the
|
|
7
|
+
typed client for every API your key can call. Your application code imports from
|
|
8
|
+
`parse_apis`; `parse-sdk` provides the runtime bases and errors those clients use.
|
|
9
|
+
|
|
10
|
+
## Quickstart
|
|
11
|
+
|
|
12
|
+
In a uv-managed project (`uv init` first if you don't have a `pyproject.toml`):
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
uv add parse-sdk # installs the runtime + `parse` CLI into your project
|
|
16
|
+
uv run parse init # prompts for your API key, then scaffolds + syncs parse_apis
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Non-interactive (CI, agents, scripts): authenticate first with
|
|
20
|
+
`uv run parse login --api-key YOUR_KEY` (add `--base-url …` for a non-default
|
|
21
|
+
host), or export `PARSE_API_KEY` — then `uv run parse init` runs without prompts.
|
|
22
|
+
|
|
23
|
+
`parse init` is the one-time setup. It:
|
|
24
|
+
|
|
25
|
+
1. ensures credentials (prompts and stores to `~/.config/parse/credentials`, or run `parse login` first);
|
|
26
|
+
2. creates the committed `parse_apis` scaffold;
|
|
27
|
+
3. records `parse_apis` as an **editable path dependency** (and a `[tool.uv.sources]` entry);
|
|
28
|
+
4. appends a deny-by-default `.gitignore` block (commits the scaffold, ignores generated payload);
|
|
29
|
+
5. migrates — or safely refuses — any legacy flat `parse_apis/` layout;
|
|
30
|
+
6. runs the first `parse sync`.
|
|
31
|
+
|
|
32
|
+
Then use it:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from parse_apis.reddit_com_api import Reddit # generated, typed client (slugs look like <domain>_<tld>_api)
|
|
36
|
+
from parse_apis import RateLimitError # runtime errors, re-exported
|
|
37
|
+
|
|
38
|
+
reddit = Reddit() # picks up `parse login` credentials
|
|
39
|
+
try:
|
|
40
|
+
# Navigate from a constructible resource, with a bounded fetch:
|
|
41
|
+
for post in reddit.subreddit("smallbusiness").search_posts(query="hiring", limit=10):
|
|
42
|
+
print(post.title)
|
|
43
|
+
except RateLimitError:
|
|
44
|
+
...
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
(The exact methods are generated from *your* API's spec — `parse_apis/<slug>/README.md`
|
|
48
|
+
and `example.py` document each client's real surface.)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv run python app.py # run your app in the project environment
|
|
52
|
+
uv run parse sync # later: refresh after you change APIs in Parse
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Running commands (`uv run` vs. activation)
|
|
56
|
+
|
|
57
|
+
`uv add parse-sdk` installs the `parse` console script into your project's `.venv`,
|
|
58
|
+
which isn't on your shell `PATH` by default. Two equivalent ways to reach it:
|
|
59
|
+
|
|
60
|
+
- **`uv run parse <cmd>`** — runs the venv's `parse` with no activation, auto-syncing first. (Recommended.)
|
|
61
|
+
- **`source .venv/bin/activate`** then `parse <cmd>` directly.
|
|
62
|
+
|
|
63
|
+
Prefer one of these over a global `uv tool install parse-sdk`: codegen must run with
|
|
64
|
+
the **same** pinned `parse-sdk` your project runs against (see *Versioning* below), and a
|
|
65
|
+
global copy can drift out of sync. Your end users running their *app* never invoke `parse` —
|
|
66
|
+
they just `import parse_apis`.
|
|
67
|
+
|
|
68
|
+
## On-disk layout
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
parse_apis/
|
|
72
|
+
pyproject.toml # committed: name="parse-apis", pinned parse-sdk==X
|
|
73
|
+
src/parse_apis/
|
|
74
|
+
__init__.py # committed scaffold — re-exports runtime errors
|
|
75
|
+
py.typed # committed
|
|
76
|
+
reddit/__init__.py # generated (gitignored)
|
|
77
|
+
_manifest.json # generated (gitignored)
|
|
78
|
+
AGENTS.md CLAUDE.md # generated cross-API index (gitignored)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Commit the three scaffold files; the generated payload is per-key (it reveals your API
|
|
82
|
+
inventory) and stays gitignored. `parse sync` builds the next payload in a staging tree,
|
|
83
|
+
self-tests it (secret scan, import-model, no-stale-docs), and promotes it with an atomic
|
|
84
|
+
directory swap — a failed sync never leaves broken or secret-bearing files in your project.
|
|
85
|
+
|
|
86
|
+
## Import model
|
|
87
|
+
|
|
88
|
+
- Application code imports generated clients from `parse_apis.<slug>`.
|
|
89
|
+
- Runtime errors/base types are re-exported from `parse_apis` (sourced from `parse_sdk`).
|
|
90
|
+
- Because `parse_apis` is an **installed** editable package, imports resolve from any
|
|
91
|
+
directory — no `sys.path` or editor `extraPaths` tricks.
|
|
92
|
+
- Generated files are project-local and differ per Parse API key.
|
|
93
|
+
|
|
94
|
+
## Fresh clone & CI (post-publish)
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
uv sync # installs deps incl. the editable parse_apis scaffold
|
|
98
|
+
uv run parse sync # regenerate the per-key payload
|
|
99
|
+
uv run pytest && uv run pyright # pyright must run against the PROJECT env —
|
|
100
|
+
# add both as dev deps (`uv add --dev pytest pyright`);
|
|
101
|
+
# a bare/global `pyright` can't resolve parse_apis
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The committed scaffold is a valid dependency target before any slugs exist, so `uv sync`
|
|
105
|
+
succeeds on a clean checkout.
|
|
106
|
+
|
|
107
|
+
## CLI
|
|
108
|
+
|
|
109
|
+
| Command | What it does |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `parse login` | Save API key + base URL to local credentials. No project changes. |
|
|
112
|
+
| `parse init` | One-time: ensure creds, scaffold `parse_apis`, record deps + gitignore, first sync. |
|
|
113
|
+
| `parse sync` | Regenerate the typed payload into `parse_apis/src/parse_apis` (staged + atomic). |
|
|
114
|
+
| `parse clean` | Remove generated payload; keep the committed scaffold. |
|
|
115
|
+
| `parse list` | Print the APIs the current key can call (no generation). |
|
|
116
|
+
| `parse whoami` | Show the current key + base URL. |
|
|
117
|
+
|
|
118
|
+
Run `parse <cmd> --help` for full per-command help.
|
|
119
|
+
|
|
120
|
+
## Versioning
|
|
121
|
+
|
|
122
|
+
`parse_apis/pyproject.toml` pins the **exact** `parse-sdk` it was generated against — generated
|
|
123
|
+
code subclasses the runtime by name. After upgrading `parse-sdk`, **re-run `parse sync`** to
|
|
124
|
+
regenerate against the new runtime; `init`/`sync` restamp the pin.
|
|
125
|
+
|
|
126
|
+
## Env vars
|
|
127
|
+
|
|
128
|
+
| Var | Default | Notes |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| `PARSE_API_KEY` | – | Auth for real requests (or use `parse login`). |
|
|
131
|
+
| `PARSE_API_BASE_URL` | `https://api.parse.bot` | Locally: `http://localhost:8001`. |
|
|
132
|
+
|
|
133
|
+
## Changelog
|
|
134
|
+
|
|
135
|
+
Release history is on the PyPI project page:
|
|
136
|
+
<https://pypi.org/project/parse-sdk/#history>.
|
|
137
|
+
|
|
138
|
+
## Security
|
|
139
|
+
|
|
140
|
+
To report a security issue, email **security@parse.bot** — please do not open
|
|
141
|
+
a public issue.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
[MIT](LICENSE) © 2026 Parse. The license covers the `parse-sdk` runtime,
|
|
146
|
+
codegen, and CLI; use of the Parse API/service is governed separately by
|
|
147
|
+
Parse's terms.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Parse SDK engine — errors, runtime helpers, and the `parse` CLI.
|
|
2
|
+
|
|
3
|
+
Typed clients are generated by `parse sync` into a project-local
|
|
4
|
+
`parse_apis/` package and imported directly:
|
|
5
|
+
|
|
6
|
+
import parse_sdk as parse # errors / runtime helpers
|
|
7
|
+
from parse_apis.reddit import Reddit # generated typed client
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
reddit = Reddit() # picks up `parse login` creds
|
|
11
|
+
for post in reddit.subreddit("smallbusiness").search_posts(query="hiring", limit=10):
|
|
12
|
+
print(post.title)
|
|
13
|
+
except parse.RateLimitError:
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
`parse_apis` is a real on-disk namespace package (pyright-resolvable, no
|
|
17
|
+
import magic). `parse_sdk` no longer resolves generated submodules.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from parse_sdk._runtime import (
|
|
23
|
+
AuthError,
|
|
24
|
+
BadRequestError,
|
|
25
|
+
BaseAPI,
|
|
26
|
+
Collection,
|
|
27
|
+
ForbiddenError,
|
|
28
|
+
NotFoundError,
|
|
29
|
+
PaginationError,
|
|
30
|
+
PaginationLimitError,
|
|
31
|
+
Paginator,
|
|
32
|
+
ParseError,
|
|
33
|
+
QuotaExceededError,
|
|
34
|
+
RateLimitError,
|
|
35
|
+
Resource,
|
|
36
|
+
RequestMeta,
|
|
37
|
+
UpstreamError,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
__version__ = "0.1.0"
|
|
41
|
+
__all__ = [
|
|
42
|
+
"BaseAPI",
|
|
43
|
+
"Resource",
|
|
44
|
+
"Collection",
|
|
45
|
+
"Paginator",
|
|
46
|
+
"RequestMeta",
|
|
47
|
+
"ParseError",
|
|
48
|
+
"AuthError",
|
|
49
|
+
"ForbiddenError",
|
|
50
|
+
"NotFoundError",
|
|
51
|
+
"BadRequestError",
|
|
52
|
+
"QuotaExceededError",
|
|
53
|
+
"RateLimitError",
|
|
54
|
+
"UpstreamError",
|
|
55
|
+
"PaginationError",
|
|
56
|
+
"PaginationLimitError",
|
|
57
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Shared parsing for the closed type-label grammar codegen emits
|
|
2
|
+
(``str`` | ``List[X]`` | ``Optional[X]`` | ``Dict[K, V]`` | ``_Paginator[X]`` |
|
|
3
|
+
unions). Single source: ``codegen_reconcile``, ``sample_gate``, and the
|
|
4
|
+
runtime previously each hand-rolled a splitter/unwrapper and drifted
|
|
5
|
+
(``Paginator[`` vs ``_Paginator[``). Stdlib-only — ``_runtime`` imports it."""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
_WRAPPERS = ("List[", "Optional[", "_Paginator[")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def strip_wrapper(s: str, prefix: str) -> Optional[str]:
|
|
14
|
+
"""The inner of a single ``<prefix>[...]`` layer, or ``None`` if ``s`` is not
|
|
15
|
+
that shape: ``strip_wrapper('Optional[X]', 'Optional[')`` → ``'X'``. The
|
|
16
|
+
one-layer primitive that ``unwrap_label`` loops over and ``sample_gate``
|
|
17
|
+
applies to a known prefix — single source for the ``s[len(prefix):-1]``
|
|
18
|
+
slice so the grammar parsers cannot drift."""
|
|
19
|
+
if s.startswith(prefix) and s.endswith("]"):
|
|
20
|
+
return s[len(prefix):-1].strip()
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def split_top(s: str) -> List[str]:
|
|
25
|
+
"""Split on commas at bracket-depth 0: ``'str, Dict[a, b]'`` →
|
|
26
|
+
``['str', 'Dict[a, b]']``."""
|
|
27
|
+
parts, depth, start = [], 0, 0
|
|
28
|
+
for i, ch in enumerate(s):
|
|
29
|
+
if ch == "[":
|
|
30
|
+
depth += 1
|
|
31
|
+
elif ch == "]":
|
|
32
|
+
depth -= 1
|
|
33
|
+
elif ch == "," and depth == 0:
|
|
34
|
+
parts.append(s[start:i].strip())
|
|
35
|
+
start = i + 1
|
|
36
|
+
parts.append(s[start:].strip())
|
|
37
|
+
return parts
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def unwrap_label(s: str, *, max_depth: int = 5) -> str:
|
|
41
|
+
"""Strip wrapper layers down to the leaf: ``'List[Optional[X]]'`` → ``'X'``."""
|
|
42
|
+
s = s.strip()
|
|
43
|
+
for _ in range(max_depth):
|
|
44
|
+
for pfx in _WRAPPERS:
|
|
45
|
+
inner = strip_wrapper(s, pfx)
|
|
46
|
+
if inner is not None:
|
|
47
|
+
s = inner
|
|
48
|
+
break
|
|
49
|
+
else:
|
|
50
|
+
return s
|
|
51
|
+
return s
|