parallel-web 0.1.1__tar.gz → 0.1.2__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.
Potentially problematic release.
This version of parallel-web might be problematic. Click here for more details.
- parallel_web-0.1.2/.release-please-manifest.json +3 -0
- parallel_web-0.1.2/CHANGELOG.md +76 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/CONTRIBUTING.md +3 -4
- {parallel_web-0.1.1 → parallel_web-0.1.2}/PKG-INFO +12 -12
- {parallel_web-0.1.1 → parallel_web-0.1.2}/README.md +5 -5
- {parallel_web-0.1.1 → parallel_web-0.1.2}/SECURITY.md +2 -2
- {parallel_web-0.1.1 → parallel_web-0.1.2}/pyproject.toml +6 -5
- {parallel_web-0.1.1 → parallel_web-0.1.2}/requirements-dev.lock +4 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/__init__.py +5 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_base_client.py +22 -2
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_models.py +2 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_types.py +2 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_utils/_proxy.py +4 -1
- parallel_web-0.1.2/src/parallel/_utils/_resources_proxy.py +24 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_version.py +1 -1
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/lib/_time.py +19 -2
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/resources/task_run.py +4 -4
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/conftest.py +2 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_client.py +116 -41
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_utils/test_proxy.py +11 -0
- parallel_web-0.1.1/.release-please-manifest.json +0 -3
- parallel_web-0.1.1/CHANGELOG.md +0 -34
- {parallel_web-0.1.1 → parallel_web-0.1.2}/.gitignore +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/LICENSE +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/api.md +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/bin/check-release-environment +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/bin/publish-pypi +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/examples/.keep +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/mypy.ini +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/noxfile.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/release-please-config.json +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/requirements.lock +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_client.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_compat.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_constants.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_exceptions.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_files.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_qs.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_resource.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_response.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_streaming.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_utils/__init__.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_utils/_logs.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_utils/_reflection.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_utils/_streams.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_utils/_sync.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_utils/_transform.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_utils/_typing.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/_utils/_utils.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/lib/.keep +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/lib/__init__.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/lib/_parsing/__init__.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/lib/_parsing/_task_run_result.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/lib/_parsing/_task_spec.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/lib/_pydantic.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/py.typed +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/resources/__init__.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/types/__init__.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/types/json_schema_param.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/types/parsed_task_run_result.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/types/task_run.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/types/task_run_create_params.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/types/task_run_result.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/types/task_run_result_params.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/types/task_spec_param.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/src/parallel/types/text_schema_param.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/__init__.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/api_resources/__init__.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/api_resources/test_task_run.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/sample_file.txt +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_deepcopy.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_extract_files.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_files.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_models.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_qs.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_required_args.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_response.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_streaming.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_transform.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/test_utils/test_typing.py +0 -0
- {parallel_web-0.1.1 → parallel_web-0.1.2}/tests/utils.py +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.2 (2025-06-25)
|
|
4
|
+
|
|
5
|
+
Full Changelog: [v0.1.1...v0.1.2](https://github.com/parallel-web/parallel-sdk-python/compare/v0.1.1...v0.1.2)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* **api:** add execute method and structured output support ([5e51379](https://github.com/parallel-web/parallel-sdk-python/commit/5e51379e3ff28bdf70a3cc9167d4413bf3e8690c))
|
|
10
|
+
* **api:** update via SDK Studio ([7526908](https://github.com/parallel-web/parallel-sdk-python/commit/752690867c75ee970582fabc05c939a2f619cb3f))
|
|
11
|
+
* **api:** update via SDK Studio ([6698e71](https://github.com/parallel-web/parallel-sdk-python/commit/6698e716bdddcf2146cc802cfaaa26f7ddb4d3dc))
|
|
12
|
+
* **client:** add follow_redirects request option ([deff733](https://github.com/parallel-web/parallel-sdk-python/commit/deff733f189070bb471ebd6cbf92dfd61d19734a))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **api:** handle retryable errors ([#2](https://github.com/parallel-web/parallel-sdk-python/issues/2)) ([5317550](https://github.com/parallel-web/parallel-sdk-python/commit/531755070eb4b798a7f0b51153414425a0c293b0))
|
|
18
|
+
* **client:** correctly parse binary response | stream ([9546f27](https://github.com/parallel-web/parallel-sdk-python/commit/9546f276ca2d63cf3c6a9b0eef23f1eed35758fa))
|
|
19
|
+
* **package:** support direct resource imports ([52fe297](https://github.com/parallel-web/parallel-sdk-python/commit/52fe297a34a6a2a473be0f124e2febab1df527fe))
|
|
20
|
+
* **pydantic:** add fields to json schema, better error messages ([38a2ddc](https://github.com/parallel-web/parallel-sdk-python/commit/38a2ddc348ac7acf11f9f75f69900b628e539c1d))
|
|
21
|
+
* **tests:** fix: tests which call HTTP endpoints directly with the example parameters ([bfad009](https://github.com/parallel-web/parallel-sdk-python/commit/bfad009314f4f3ce31265d2be07f091eb7db664a))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Chores
|
|
25
|
+
|
|
26
|
+
* **ci:** enable for pull requests ([0ae47ea](https://github.com/parallel-web/parallel-sdk-python/commit/0ae47eaf080510a886eb40aed7c8189faa940f2c))
|
|
27
|
+
* **ci:** fix installation instructions ([150a642](https://github.com/parallel-web/parallel-sdk-python/commit/150a6429ee584a0c32160be88d9bdcd4eeab4579))
|
|
28
|
+
* **ci:** upload sdks to package manager ([3bd8b36](https://github.com/parallel-web/parallel-sdk-python/commit/3bd8b361b84bad87c0943c2fe71465c92cdea599))
|
|
29
|
+
* **docs:** grammar improvements ([c5b636b](https://github.com/parallel-web/parallel-sdk-python/commit/c5b636bfeb60b02f84f5b9e93687359cd9c5c251))
|
|
30
|
+
* **docs:** remove reference to rye shell ([a64869e](https://github.com/parallel-web/parallel-sdk-python/commit/a64869e70e9c493f2dc3e8618327f28544d36058))
|
|
31
|
+
* **docs:** remove unnecessary param examples ([e15712a](https://github.com/parallel-web/parallel-sdk-python/commit/e15712a074ba66a6b0d225bb3a6979a767c15225))
|
|
32
|
+
* **internal:** avoid errors for isinstance checks on proxies ([4149fb9](https://github.com/parallel-web/parallel-sdk-python/commit/4149fb963b39db2211f404f94bf7b55a57c2556b))
|
|
33
|
+
* **internal:** codegen related update ([6a0bb66](https://github.com/parallel-web/parallel-sdk-python/commit/6a0bb662f5011bbea13f75334eb55c5144b50e8b))
|
|
34
|
+
* **internal:** update conftest.py ([0e08356](https://github.com/parallel-web/parallel-sdk-python/commit/0e0835661e91993042605131065729d006761a5a))
|
|
35
|
+
* **readme:** update badges ([36c14b5](https://github.com/parallel-web/parallel-sdk-python/commit/36c14b529ec8611508b6b7cc9065c67e59e5ecdc))
|
|
36
|
+
* **readme:** update low level api examples ([f17e34e](https://github.com/parallel-web/parallel-sdk-python/commit/f17e34e0e0a6d3205c344c278f1643826938e9d1))
|
|
37
|
+
* **tests:** add tests for httpx client instantiation & proxies ([d84ffff](https://github.com/parallel-web/parallel-sdk-python/commit/d84ffff48a814edc81ef62249353053df6398c90))
|
|
38
|
+
* **tests:** run tests in parallel ([62252c6](https://github.com/parallel-web/parallel-sdk-python/commit/62252c6f1098ad138978b6efa1fc2a9c22961040))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Documentation
|
|
42
|
+
|
|
43
|
+
* **client:** fix httpx.Timeout documentation reference ([17f87ee](https://github.com/parallel-web/parallel-sdk-python/commit/17f87eef5af2b06b3791f9218b7ab4f9098faf9c))
|
|
44
|
+
|
|
45
|
+
## 0.1.1 (2025-04-25)
|
|
46
|
+
|
|
47
|
+
Full Changelog: [v0.1.0...v0.1.1](https://github.com/shapleyai/parallel-sdk-python/compare/v0.1.0...v0.1.1)
|
|
48
|
+
|
|
49
|
+
### Features
|
|
50
|
+
|
|
51
|
+
* **api:** update via SDK Studio ([4cc79c4](https://github.com/shapleyai/parallel-sdk-python/commit/4cc79c4d1edaa9d1d080b81830961252c8b327c1))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### Bug Fixes
|
|
55
|
+
|
|
56
|
+
* **pydantic:** add fields to json schema, better error messages ([38a2ddc](https://github.com/shapleyai/parallel-sdk-python/commit/38a2ddc348ac7acf11f9f75f69900b628e539c1d))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
### Chores
|
|
60
|
+
|
|
61
|
+
* **readme:** update low level api examples ([f17e34e](https://github.com/shapleyai/parallel-sdk-python/commit/f17e34e0e0a6d3205c344c278f1643826938e9d1))
|
|
62
|
+
|
|
63
|
+
## 0.1.0 (2025-04-24)
|
|
64
|
+
|
|
65
|
+
Full Changelog: [v0.0.1-alpha.0...v0.1.0](https://github.com/shapleyai/parallel-sdk-python/compare/v0.0.1-alpha.0...v0.1.0)
|
|
66
|
+
|
|
67
|
+
### Features
|
|
68
|
+
|
|
69
|
+
* **api:** add execute method and structured output support ([5e51379](https://github.com/shapleyai/parallel-sdk-python/commit/5e51379e3ff28bdf70a3cc9167d4413bf3e8690c))
|
|
70
|
+
* **api:** update via SDK Studio ([c393d04](https://github.com/shapleyai/parallel-sdk-python/commit/c393d048bddb554c37eb750ca57c4335243a70ed))
|
|
71
|
+
* **api:** update via SDK Studio ([6698e71](https://github.com/shapleyai/parallel-sdk-python/commit/6698e716bdddcf2146cc802cfaaa26f7ddb4d3dc))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### Chores
|
|
75
|
+
|
|
76
|
+
* go live ([061677a](https://github.com/shapleyai/parallel-sdk-python/commit/061677a22549f3dd3d9f4591c9ccfdf71209c12e))
|
|
@@ -17,8 +17,7 @@ $ rye sync --all-features
|
|
|
17
17
|
You can then run scripts using `rye run python script.py` or by activating the virtual environment:
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
|
|
21
|
-
# or manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work
|
|
20
|
+
# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work
|
|
22
21
|
$ source .venv/bin/activate
|
|
23
22
|
|
|
24
23
|
# now you can omit the `rye run` prefix
|
|
@@ -63,7 +62,7 @@ If you’d like to use the repository from source, you can either install from g
|
|
|
63
62
|
To install via git:
|
|
64
63
|
|
|
65
64
|
```sh
|
|
66
|
-
$ pip install git+ssh://git@github.com/
|
|
65
|
+
$ pip install git+ssh://git@github.com/parallel-web/parallel-sdk-python.git
|
|
67
66
|
```
|
|
68
67
|
|
|
69
68
|
Alternatively, you can build from source and install the wheel file:
|
|
@@ -121,7 +120,7 @@ the changes aren't made through the automated pipeline, you may want to make rel
|
|
|
121
120
|
|
|
122
121
|
### Publish with a GitHub workflow
|
|
123
122
|
|
|
124
|
-
You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/
|
|
123
|
+
You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/parallel-web/parallel-sdk-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up.
|
|
125
124
|
|
|
126
125
|
### Publish manually
|
|
127
126
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: parallel-web
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: The official Python library for the Parallel API
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/parallel-web/parallel-sdk-python
|
|
6
|
+
Project-URL: Repository, https://github.com/parallel-web/parallel-sdk-python
|
|
7
7
|
Author-email: Parallel <support@parallel.ai>
|
|
8
8
|
License: MIT
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
@@ -31,7 +31,7 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
# Parallel Python API library
|
|
33
33
|
|
|
34
|
-
[](https://pypi.org/project/parallel-web/)
|
|
34
|
+
[>)](https://pypi.org/project/parallel-web/)
|
|
35
35
|
|
|
36
36
|
The Parallel Python library provides convenient access to the Parallel REST API from any Python 3.8+
|
|
37
37
|
application. The library includes type definitions for all request params and response fields,
|
|
@@ -43,7 +43,7 @@ It is generated with [Stainless](https://www.stainless.com/).
|
|
|
43
43
|
## Documentation
|
|
44
44
|
|
|
45
45
|
The REST API documentation can be found on our [docs](https://docs.parallel.ai).
|
|
46
|
-
The full API of this Python library can be found in [api.md](https://github.com/
|
|
46
|
+
The full API of this Python library can be found in [api.md](https://github.com/parallel-web/parallel-sdk-python/tree/main/api.md).
|
|
47
47
|
|
|
48
48
|
## Installation
|
|
49
49
|
|
|
@@ -54,7 +54,7 @@ pip install parallel-web
|
|
|
54
54
|
|
|
55
55
|
## Usage
|
|
56
56
|
|
|
57
|
-
The full API of this library can be found in [api.md](https://github.com/
|
|
57
|
+
The full API of this library can be found in [api.md](https://github.com/parallel-web/parallel-sdk-python/tree/main/api.md).
|
|
58
58
|
|
|
59
59
|
```python
|
|
60
60
|
import os
|
|
@@ -343,7 +343,7 @@ client.with_options(max_retries=5).task_run.execute(
|
|
|
343
343
|
### Timeouts
|
|
344
344
|
|
|
345
345
|
By default requests time out after 1 minute. You can configure this with a `timeout` option,
|
|
346
|
-
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object:
|
|
346
|
+
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
|
|
347
347
|
|
|
348
348
|
```python
|
|
349
349
|
from parallel import Parallel
|
|
@@ -369,7 +369,7 @@ client.with_options(timeout=5.0).task_run.execute(
|
|
|
369
369
|
|
|
370
370
|
On timeout, an `APITimeoutError` is thrown.
|
|
371
371
|
|
|
372
|
-
Note that requests that time out are [retried twice by default](https://github.com/
|
|
372
|
+
Note that requests that time out are [retried twice by default](https://github.com/parallel-web/parallel-sdk-python/tree/main/#retries).
|
|
373
373
|
|
|
374
374
|
## Advanced
|
|
375
375
|
|
|
@@ -416,9 +416,9 @@ task_run = response.parse() # get the object that `task_run.execute()` would ha
|
|
|
416
416
|
print(task_run.output)
|
|
417
417
|
```
|
|
418
418
|
|
|
419
|
-
These methods return an [`APIResponse`](https://github.com/
|
|
419
|
+
These methods return an [`APIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) object.
|
|
420
420
|
|
|
421
|
-
The async client returns an [`AsyncAPIResponse`](https://github.com/
|
|
421
|
+
The async client returns an [`AsyncAPIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.
|
|
422
422
|
|
|
423
423
|
#### `.with_streaming_response`
|
|
424
424
|
|
|
@@ -526,7 +526,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con
|
|
|
526
526
|
|
|
527
527
|
We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
|
|
528
528
|
|
|
529
|
-
We are keen for your feedback; please open an [issue](https://www.github.com/
|
|
529
|
+
We are keen for your feedback; please open an [issue](https://www.github.com/parallel-web/parallel-sdk-python/issues) with questions, bugs, or suggestions.
|
|
530
530
|
|
|
531
531
|
### Determining the installed version
|
|
532
532
|
|
|
@@ -545,4 +545,4 @@ Python 3.8 or higher.
|
|
|
545
545
|
|
|
546
546
|
## Contributing
|
|
547
547
|
|
|
548
|
-
See [the contributing documentation](https://github.com/
|
|
548
|
+
See [the contributing documentation](https://github.com/parallel-web/parallel-sdk-python/tree/main/./CONTRIBUTING.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Parallel Python API library
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/parallel-web/)
|
|
3
|
+
[>)](https://pypi.org/project/parallel-web/)
|
|
4
4
|
|
|
5
5
|
The Parallel Python library provides convenient access to the Parallel REST API from any Python 3.8+
|
|
6
6
|
application. The library includes type definitions for all request params and response fields,
|
|
@@ -312,7 +312,7 @@ client.with_options(max_retries=5).task_run.execute(
|
|
|
312
312
|
### Timeouts
|
|
313
313
|
|
|
314
314
|
By default requests time out after 1 minute. You can configure this with a `timeout` option,
|
|
315
|
-
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object:
|
|
315
|
+
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
|
|
316
316
|
|
|
317
317
|
```python
|
|
318
318
|
from parallel import Parallel
|
|
@@ -385,9 +385,9 @@ task_run = response.parse() # get the object that `task_run.execute()` would ha
|
|
|
385
385
|
print(task_run.output)
|
|
386
386
|
```
|
|
387
387
|
|
|
388
|
-
These methods return an [`APIResponse`](https://github.com/
|
|
388
|
+
These methods return an [`APIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) object.
|
|
389
389
|
|
|
390
|
-
The async client returns an [`AsyncAPIResponse`](https://github.com/
|
|
390
|
+
The async client returns an [`AsyncAPIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.
|
|
391
391
|
|
|
392
392
|
#### `.with_streaming_response`
|
|
393
393
|
|
|
@@ -495,7 +495,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con
|
|
|
495
495
|
|
|
496
496
|
We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
|
|
497
497
|
|
|
498
|
-
We are keen for your feedback; please open an [issue](https://www.github.com/
|
|
498
|
+
We are keen for your feedback; please open an [issue](https://www.github.com/parallel-web/parallel-sdk-python/issues) with questions, bugs, or suggestions.
|
|
499
499
|
|
|
500
500
|
### Determining the installed version
|
|
501
501
|
|
|
@@ -16,11 +16,11 @@ before making any information public.
|
|
|
16
16
|
## Reporting Non-SDK Related Security Issues
|
|
17
17
|
|
|
18
18
|
If you encounter security issues that are not directly related to SDKs but pertain to the services
|
|
19
|
-
or products provided by Parallel please follow the respective company's security reporting guidelines.
|
|
19
|
+
or products provided by Parallel, please follow the respective company's security reporting guidelines.
|
|
20
20
|
|
|
21
21
|
### Parallel Terms and Policies
|
|
22
22
|
|
|
23
|
-
Please contact support@parallel.ai for any questions or concerns regarding security of our services.
|
|
23
|
+
Please contact support@parallel.ai for any questions or concerns regarding the security of our services.
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "parallel-web"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "The official Python library for the Parallel API"
|
|
5
5
|
dynamic = ["readme"]
|
|
6
6
|
license = "MIT"
|
|
@@ -34,8 +34,8 @@ classifiers = [
|
|
|
34
34
|
]
|
|
35
35
|
|
|
36
36
|
[project.urls]
|
|
37
|
-
Homepage = "https://github.com/
|
|
38
|
-
Repository = "https://github.com/
|
|
37
|
+
Homepage = "https://github.com/parallel-web/parallel-sdk-python"
|
|
38
|
+
Repository = "https://github.com/parallel-web/parallel-sdk-python"
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
[tool.rye]
|
|
@@ -54,6 +54,7 @@ dev-dependencies = [
|
|
|
54
54
|
"importlib-metadata>=6.7.0",
|
|
55
55
|
"rich>=13.7.1",
|
|
56
56
|
"nest_asyncio==1.6.0",
|
|
57
|
+
"pytest-xdist>=3.6.1",
|
|
57
58
|
]
|
|
58
59
|
|
|
59
60
|
[tool.rye.scripts]
|
|
@@ -121,11 +122,11 @@ path = "README.md"
|
|
|
121
122
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
|
|
122
123
|
# replace relative links with absolute links
|
|
123
124
|
pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)'
|
|
124
|
-
replacement = '[\1](https://github.com/
|
|
125
|
+
replacement = '[\1](https://github.com/parallel-web/parallel-sdk-python/tree/main/\g<2>)'
|
|
125
126
|
|
|
126
127
|
[tool.pytest.ini_options]
|
|
127
128
|
testpaths = ["tests"]
|
|
128
|
-
addopts = "--tb=short"
|
|
129
|
+
addopts = "--tb=short -n auto"
|
|
129
130
|
xfail_strict = true
|
|
130
131
|
asyncio_mode = "auto"
|
|
131
132
|
asyncio_default_fixture_loop_scope = "session"
|
|
@@ -30,6 +30,8 @@ distro==1.8.0
|
|
|
30
30
|
exceptiongroup==1.2.2
|
|
31
31
|
# via anyio
|
|
32
32
|
# via pytest
|
|
33
|
+
execnet==2.1.1
|
|
34
|
+
# via pytest-xdist
|
|
33
35
|
filelock==3.12.4
|
|
34
36
|
# via virtualenv
|
|
35
37
|
h11==0.14.0
|
|
@@ -72,7 +74,9 @@ pygments==2.18.0
|
|
|
72
74
|
pyright==1.1.399
|
|
73
75
|
pytest==8.3.3
|
|
74
76
|
# via pytest-asyncio
|
|
77
|
+
# via pytest-xdist
|
|
75
78
|
pytest-asyncio==0.24.0
|
|
79
|
+
pytest-xdist==3.7.0
|
|
76
80
|
python-dateutil==2.8.2
|
|
77
81
|
# via time-machine
|
|
78
82
|
pytz==2023.3.post1
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
2
|
|
|
3
|
+
import typing as _t
|
|
4
|
+
|
|
3
5
|
from . import types
|
|
4
6
|
from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes
|
|
5
7
|
from ._utils import file_from_path
|
|
@@ -78,6 +80,9 @@ __all__ = [
|
|
|
78
80
|
"DefaultAsyncHttpxClient",
|
|
79
81
|
]
|
|
80
82
|
|
|
83
|
+
if not _t.TYPE_CHECKING:
|
|
84
|
+
from ._utils._resources_proxy import resources as resources
|
|
85
|
+
|
|
81
86
|
_setup_logging()
|
|
82
87
|
|
|
83
88
|
# Update the __module__ attribute for exported symbols so that
|
|
@@ -960,6 +960,9 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
|
|
|
960
960
|
if self.custom_auth is not None:
|
|
961
961
|
kwargs["auth"] = self.custom_auth
|
|
962
962
|
|
|
963
|
+
if options.follow_redirects is not None:
|
|
964
|
+
kwargs["follow_redirects"] = options.follow_redirects
|
|
965
|
+
|
|
963
966
|
log.debug("Sending HTTP Request: %s %s", request.method, request.url)
|
|
964
967
|
|
|
965
968
|
response = None
|
|
@@ -1068,7 +1071,14 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
|
|
|
1068
1071
|
) -> ResponseT:
|
|
1069
1072
|
origin = get_origin(cast_to) or cast_to
|
|
1070
1073
|
|
|
1071
|
-
if
|
|
1074
|
+
if (
|
|
1075
|
+
inspect.isclass(origin)
|
|
1076
|
+
and issubclass(origin, BaseAPIResponse)
|
|
1077
|
+
# we only want to actually return the custom BaseAPIResponse class if we're
|
|
1078
|
+
# returning the raw response, or if we're not streaming SSE, as if we're streaming
|
|
1079
|
+
# SSE then `cast_to` doesn't actively reflect the type we need to parse into
|
|
1080
|
+
and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
|
|
1081
|
+
):
|
|
1072
1082
|
if not issubclass(origin, APIResponse):
|
|
1073
1083
|
raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}")
|
|
1074
1084
|
|
|
@@ -1460,6 +1470,9 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1460
1470
|
if self.custom_auth is not None:
|
|
1461
1471
|
kwargs["auth"] = self.custom_auth
|
|
1462
1472
|
|
|
1473
|
+
if options.follow_redirects is not None:
|
|
1474
|
+
kwargs["follow_redirects"] = options.follow_redirects
|
|
1475
|
+
|
|
1463
1476
|
log.debug("Sending HTTP Request: %s %s", request.method, request.url)
|
|
1464
1477
|
|
|
1465
1478
|
response = None
|
|
@@ -1568,7 +1581,14 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1568
1581
|
) -> ResponseT:
|
|
1569
1582
|
origin = get_origin(cast_to) or cast_to
|
|
1570
1583
|
|
|
1571
|
-
if
|
|
1584
|
+
if (
|
|
1585
|
+
inspect.isclass(origin)
|
|
1586
|
+
and issubclass(origin, BaseAPIResponse)
|
|
1587
|
+
# we only want to actually return the custom BaseAPIResponse class if we're
|
|
1588
|
+
# returning the raw response, or if we're not streaming SSE, as if we're streaming
|
|
1589
|
+
# SSE then `cast_to` doesn't actively reflect the type we need to parse into
|
|
1590
|
+
and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
|
|
1591
|
+
):
|
|
1572
1592
|
if not issubclass(origin, AsyncAPIResponse):
|
|
1573
1593
|
raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}")
|
|
1574
1594
|
|
|
@@ -737,6 +737,7 @@ class FinalRequestOptionsInput(TypedDict, total=False):
|
|
|
737
737
|
idempotency_key: str
|
|
738
738
|
json_data: Body
|
|
739
739
|
extra_json: AnyMapping
|
|
740
|
+
follow_redirects: bool
|
|
740
741
|
|
|
741
742
|
|
|
742
743
|
@final
|
|
@@ -750,6 +751,7 @@ class FinalRequestOptions(pydantic.BaseModel):
|
|
|
750
751
|
files: Union[HttpxRequestFiles, None] = None
|
|
751
752
|
idempotency_key: Union[str, None] = None
|
|
752
753
|
post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
|
|
754
|
+
follow_redirects: Union[bool, None] = None
|
|
753
755
|
|
|
754
756
|
# It should be noted that we cannot use `json` here as that would override
|
|
755
757
|
# a BaseModel method in an incompatible fashion.
|
|
@@ -100,6 +100,7 @@ class RequestOptions(TypedDict, total=False):
|
|
|
100
100
|
params: Query
|
|
101
101
|
extra_json: AnyMapping
|
|
102
102
|
idempotency_key: str
|
|
103
|
+
follow_redirects: bool
|
|
103
104
|
|
|
104
105
|
|
|
105
106
|
# Sentinel class used until PEP 0661 is accepted
|
|
@@ -215,3 +216,4 @@ class _GenericAlias(Protocol):
|
|
|
215
216
|
|
|
216
217
|
class HttpxSendArgs(TypedDict, total=False):
|
|
217
218
|
auth: httpx.Auth
|
|
219
|
+
follow_redirects: bool
|
|
@@ -46,7 +46,10 @@ class LazyProxy(Generic[T], ABC):
|
|
|
46
46
|
@property # type: ignore
|
|
47
47
|
@override
|
|
48
48
|
def __class__(self) -> type: # pyright: ignore
|
|
49
|
-
|
|
49
|
+
try:
|
|
50
|
+
proxied = self.__get_proxied__()
|
|
51
|
+
except Exception:
|
|
52
|
+
return type(self)
|
|
50
53
|
if issubclass(type(proxied), LazyProxy):
|
|
51
54
|
return type(proxied)
|
|
52
55
|
return proxied.__class__
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
from typing_extensions import override
|
|
5
|
+
|
|
6
|
+
from ._proxy import LazyProxy
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ResourcesProxy(LazyProxy[Any]):
|
|
10
|
+
"""A proxy for the `parallel.resources` module.
|
|
11
|
+
|
|
12
|
+
This is used so that we can lazily import `parallel.resources` only when
|
|
13
|
+
needed *and* so that users can just import `parallel` and reference `parallel.resources`
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@override
|
|
17
|
+
def __load__(self) -> Any:
|
|
18
|
+
import importlib
|
|
19
|
+
|
|
20
|
+
mod = importlib.import_module("parallel.resources")
|
|
21
|
+
return mod
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
resources = ResourcesProxy().__as_proxied__()
|
|
@@ -28,6 +28,24 @@ def _raise_timeout(run_id: str, exc: Union[Exception, None]) -> NoReturn:
|
|
|
28
28
|
raise TimeoutError(f"Fetching task run result for run id {run_id} timed out.") from exc
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
def _is_retryable_error(status_code: int) -> bool:
|
|
32
|
+
"""Determine if an error is retryable.
|
|
33
|
+
|
|
34
|
+
We retry the following HTTP status codes within the SDK:
|
|
35
|
+
- 408 (Request Timeout): The server timed out waiting for the request
|
|
36
|
+
- 503 (Service Unavailable): The server is temporarily unable to handle the request
|
|
37
|
+
- 504 (Gateway Timeout): The gateway server timed out
|
|
38
|
+
|
|
39
|
+
These errors typically indicate temporary issues with the server or network
|
|
40
|
+
that may resolve upon retry. We don't include 429 (Too Many Requests) as this
|
|
41
|
+
indicates rate limiting, which requires backing off rather than immediate retries.
|
|
42
|
+
|
|
43
|
+
Note: This is a low-level retry mechanism within the SDK. Customers may want to
|
|
44
|
+
implement their own retry logic at the application level for other error types.
|
|
45
|
+
"""
|
|
46
|
+
return status_code in (408, 503, 504)
|
|
47
|
+
|
|
48
|
+
|
|
31
49
|
@contextlib.contextmanager
|
|
32
50
|
def timeout_retry_context(run_id: str, deadline: float) -> Iterator[None]:
|
|
33
51
|
"""Context manager for handling timeouts and retries when fetching task run results.
|
|
@@ -49,8 +67,7 @@ def timeout_retry_context(run_id: str, deadline: float) -> Iterator[None]:
|
|
|
49
67
|
exc = e
|
|
50
68
|
continue
|
|
51
69
|
except APIStatusError as e:
|
|
52
|
-
|
|
53
|
-
if e.status_code == 408:
|
|
70
|
+
if _is_retryable_error(e.status_code):
|
|
54
71
|
exc = e
|
|
55
72
|
continue
|
|
56
73
|
raise
|
|
@@ -42,7 +42,7 @@ class TaskRunResource(SyncAPIResource):
|
|
|
42
42
|
This property can be used as a prefix for any HTTP method call to return
|
|
43
43
|
the raw response object instead of the parsed content.
|
|
44
44
|
|
|
45
|
-
For more information, see https://www.github.com/
|
|
45
|
+
For more information, see https://www.github.com/parallel-web/parallel-sdk-python#accessing-raw-response-data-eg-headers
|
|
46
46
|
"""
|
|
47
47
|
return TaskRunResourceWithRawResponse(self)
|
|
48
48
|
|
|
@@ -51,7 +51,7 @@ class TaskRunResource(SyncAPIResource):
|
|
|
51
51
|
"""
|
|
52
52
|
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
|
|
53
53
|
|
|
54
|
-
For more information, see https://www.github.com/
|
|
54
|
+
For more information, see https://www.github.com/parallel-web/parallel-sdk-python#with_streaming_response
|
|
55
55
|
"""
|
|
56
56
|
return TaskRunResourceWithStreamingResponse(self)
|
|
57
57
|
|
|
@@ -317,7 +317,7 @@ class AsyncTaskRunResource(AsyncAPIResource):
|
|
|
317
317
|
This property can be used as a prefix for any HTTP method call to return
|
|
318
318
|
the raw response object instead of the parsed content.
|
|
319
319
|
|
|
320
|
-
For more information, see https://www.github.com/
|
|
320
|
+
For more information, see https://www.github.com/parallel-web/parallel-sdk-python#accessing-raw-response-data-eg-headers
|
|
321
321
|
"""
|
|
322
322
|
return AsyncTaskRunResourceWithRawResponse(self)
|
|
323
323
|
|
|
@@ -326,7 +326,7 @@ class AsyncTaskRunResource(AsyncAPIResource):
|
|
|
326
326
|
"""
|
|
327
327
|
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
|
|
328
328
|
|
|
329
|
-
For more information, see https://www.github.com/
|
|
329
|
+
For more information, see https://www.github.com/parallel-web/parallel-sdk-python#with_streaming_response
|
|
330
330
|
"""
|
|
331
331
|
return AsyncTaskRunResourceWithStreamingResponse(self)
|
|
332
332
|
|
|
@@ -23,17 +23,16 @@ from pydantic import ValidationError
|
|
|
23
23
|
|
|
24
24
|
from parallel import Parallel, AsyncParallel, APIResponseValidationError
|
|
25
25
|
from parallel._types import Omit
|
|
26
|
-
from parallel._utils import maybe_transform
|
|
27
26
|
from parallel._models import BaseModel, FinalRequestOptions
|
|
28
|
-
from parallel._constants import RAW_RESPONSE_HEADER
|
|
29
27
|
from parallel._exceptions import ParallelError, APIStatusError, APITimeoutError, APIResponseValidationError
|
|
30
28
|
from parallel._base_client import (
|
|
31
29
|
DEFAULT_TIMEOUT,
|
|
32
30
|
HTTPX_DEFAULT_TIMEOUT,
|
|
33
31
|
BaseClient,
|
|
32
|
+
DefaultHttpxClient,
|
|
33
|
+
DefaultAsyncHttpxClient,
|
|
34
34
|
make_request_options,
|
|
35
35
|
)
|
|
36
|
-
from parallel.types.task_run_create_params import TaskRunCreateParams
|
|
37
36
|
|
|
38
37
|
from .utils import update_env
|
|
39
38
|
|
|
@@ -713,36 +712,21 @@ class TestParallel:
|
|
|
713
712
|
|
|
714
713
|
@mock.patch("parallel._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
|
715
714
|
@pytest.mark.respx(base_url=base_url)
|
|
716
|
-
def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
|
|
715
|
+
def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Parallel) -> None:
|
|
717
716
|
respx_mock.post("/v1/tasks/runs").mock(side_effect=httpx.TimeoutException("Test timeout error"))
|
|
718
717
|
|
|
719
718
|
with pytest.raises(APITimeoutError):
|
|
720
|
-
|
|
721
|
-
"/v1/tasks/runs",
|
|
722
|
-
body=cast(
|
|
723
|
-
object, maybe_transform(dict(input="France (2023)", processor="processor"), TaskRunCreateParams)
|
|
724
|
-
),
|
|
725
|
-
cast_to=httpx.Response,
|
|
726
|
-
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
|
|
727
|
-
)
|
|
719
|
+
client.task_run.with_streaming_response.create(input="France (2023)", processor="processor").__enter__()
|
|
728
720
|
|
|
729
721
|
assert _get_open_connections(self.client) == 0
|
|
730
722
|
|
|
731
723
|
@mock.patch("parallel._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
|
732
724
|
@pytest.mark.respx(base_url=base_url)
|
|
733
|
-
def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
|
|
725
|
+
def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Parallel) -> None:
|
|
734
726
|
respx_mock.post("/v1/tasks/runs").mock(return_value=httpx.Response(500))
|
|
735
727
|
|
|
736
728
|
with pytest.raises(APIStatusError):
|
|
737
|
-
|
|
738
|
-
"/v1/tasks/runs",
|
|
739
|
-
body=cast(
|
|
740
|
-
object, maybe_transform(dict(input="France (2023)", processor="processor"), TaskRunCreateParams)
|
|
741
|
-
),
|
|
742
|
-
cast_to=httpx.Response,
|
|
743
|
-
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
|
|
744
|
-
)
|
|
745
|
-
|
|
729
|
+
client.task_run.with_streaming_response.create(input="France (2023)", processor="processor").__enter__()
|
|
746
730
|
assert _get_open_connections(self.client) == 0
|
|
747
731
|
|
|
748
732
|
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
|
|
@@ -826,6 +810,55 @@ class TestParallel:
|
|
|
826
810
|
|
|
827
811
|
assert response.http_request.headers.get("x-stainless-retry-count") == "42"
|
|
828
812
|
|
|
813
|
+
def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
814
|
+
# Test that the proxy environment variables are set correctly
|
|
815
|
+
monkeypatch.setenv("HTTPS_PROXY", "https://example.org")
|
|
816
|
+
|
|
817
|
+
client = DefaultHttpxClient()
|
|
818
|
+
|
|
819
|
+
mounts = tuple(client._mounts.items())
|
|
820
|
+
assert len(mounts) == 1
|
|
821
|
+
assert mounts[0][0].pattern == "https://"
|
|
822
|
+
|
|
823
|
+
@pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning")
|
|
824
|
+
def test_default_client_creation(self) -> None:
|
|
825
|
+
# Ensure that the client can be initialized without any exceptions
|
|
826
|
+
DefaultHttpxClient(
|
|
827
|
+
verify=True,
|
|
828
|
+
cert=None,
|
|
829
|
+
trust_env=True,
|
|
830
|
+
http1=True,
|
|
831
|
+
http2=False,
|
|
832
|
+
limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
@pytest.mark.respx(base_url=base_url)
|
|
836
|
+
def test_follow_redirects(self, respx_mock: MockRouter) -> None:
|
|
837
|
+
# Test that the default follow_redirects=True allows following redirects
|
|
838
|
+
respx_mock.post("/redirect").mock(
|
|
839
|
+
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
|
|
840
|
+
)
|
|
841
|
+
respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
|
|
842
|
+
|
|
843
|
+
response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
|
|
844
|
+
assert response.status_code == 200
|
|
845
|
+
assert response.json() == {"status": "ok"}
|
|
846
|
+
|
|
847
|
+
@pytest.mark.respx(base_url=base_url)
|
|
848
|
+
def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
|
|
849
|
+
# Test that follow_redirects=False prevents following redirects
|
|
850
|
+
respx_mock.post("/redirect").mock(
|
|
851
|
+
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
with pytest.raises(APIStatusError) as exc_info:
|
|
855
|
+
self.client.post(
|
|
856
|
+
"/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
assert exc_info.value.response.status_code == 302
|
|
860
|
+
assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
|
|
861
|
+
|
|
829
862
|
|
|
830
863
|
class TestAsyncParallel:
|
|
831
864
|
client = AsyncParallel(base_url=base_url, api_key=api_key, _strict_response_validation=True)
|
|
@@ -1497,36 +1530,29 @@ class TestAsyncParallel:
|
|
|
1497
1530
|
|
|
1498
1531
|
@mock.patch("parallel._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
|
1499
1532
|
@pytest.mark.respx(base_url=base_url)
|
|
1500
|
-
async def test_retrying_timeout_errors_doesnt_leak(
|
|
1533
|
+
async def test_retrying_timeout_errors_doesnt_leak(
|
|
1534
|
+
self, respx_mock: MockRouter, async_client: AsyncParallel
|
|
1535
|
+
) -> None:
|
|
1501
1536
|
respx_mock.post("/v1/tasks/runs").mock(side_effect=httpx.TimeoutException("Test timeout error"))
|
|
1502
1537
|
|
|
1503
1538
|
with pytest.raises(APITimeoutError):
|
|
1504
|
-
await
|
|
1505
|
-
"
|
|
1506
|
-
|
|
1507
|
-
object, maybe_transform(dict(input="France (2023)", processor="processor"), TaskRunCreateParams)
|
|
1508
|
-
),
|
|
1509
|
-
cast_to=httpx.Response,
|
|
1510
|
-
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
|
|
1511
|
-
)
|
|
1539
|
+
await async_client.task_run.with_streaming_response.create(
|
|
1540
|
+
input="France (2023)", processor="processor"
|
|
1541
|
+
).__aenter__()
|
|
1512
1542
|
|
|
1513
1543
|
assert _get_open_connections(self.client) == 0
|
|
1514
1544
|
|
|
1515
1545
|
@mock.patch("parallel._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
|
1516
1546
|
@pytest.mark.respx(base_url=base_url)
|
|
1517
|
-
async def test_retrying_status_errors_doesnt_leak(
|
|
1547
|
+
async def test_retrying_status_errors_doesnt_leak(
|
|
1548
|
+
self, respx_mock: MockRouter, async_client: AsyncParallel
|
|
1549
|
+
) -> None:
|
|
1518
1550
|
respx_mock.post("/v1/tasks/runs").mock(return_value=httpx.Response(500))
|
|
1519
1551
|
|
|
1520
1552
|
with pytest.raises(APIStatusError):
|
|
1521
|
-
await
|
|
1522
|
-
"
|
|
1523
|
-
|
|
1524
|
-
object, maybe_transform(dict(input="France (2023)", processor="processor"), TaskRunCreateParams)
|
|
1525
|
-
),
|
|
1526
|
-
cast_to=httpx.Response,
|
|
1527
|
-
options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
|
|
1528
|
-
)
|
|
1529
|
-
|
|
1553
|
+
await async_client.task_run.with_streaming_response.create(
|
|
1554
|
+
input="France (2023)", processor="processor"
|
|
1555
|
+
).__aenter__()
|
|
1530
1556
|
assert _get_open_connections(self.client) == 0
|
|
1531
1557
|
|
|
1532
1558
|
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
|
|
@@ -1657,3 +1683,52 @@ class TestAsyncParallel:
|
|
|
1657
1683
|
raise AssertionError("calling get_platform using asyncify resulted in a hung process")
|
|
1658
1684
|
|
|
1659
1685
|
time.sleep(0.1)
|
|
1686
|
+
|
|
1687
|
+
async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1688
|
+
# Test that the proxy environment variables are set correctly
|
|
1689
|
+
monkeypatch.setenv("HTTPS_PROXY", "https://example.org")
|
|
1690
|
+
|
|
1691
|
+
client = DefaultAsyncHttpxClient()
|
|
1692
|
+
|
|
1693
|
+
mounts = tuple(client._mounts.items())
|
|
1694
|
+
assert len(mounts) == 1
|
|
1695
|
+
assert mounts[0][0].pattern == "https://"
|
|
1696
|
+
|
|
1697
|
+
@pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning")
|
|
1698
|
+
async def test_default_client_creation(self) -> None:
|
|
1699
|
+
# Ensure that the client can be initialized without any exceptions
|
|
1700
|
+
DefaultAsyncHttpxClient(
|
|
1701
|
+
verify=True,
|
|
1702
|
+
cert=None,
|
|
1703
|
+
trust_env=True,
|
|
1704
|
+
http1=True,
|
|
1705
|
+
http2=False,
|
|
1706
|
+
limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
|
|
1707
|
+
)
|
|
1708
|
+
|
|
1709
|
+
@pytest.mark.respx(base_url=base_url)
|
|
1710
|
+
async def test_follow_redirects(self, respx_mock: MockRouter) -> None:
|
|
1711
|
+
# Test that the default follow_redirects=True allows following redirects
|
|
1712
|
+
respx_mock.post("/redirect").mock(
|
|
1713
|
+
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
|
|
1714
|
+
)
|
|
1715
|
+
respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
|
|
1716
|
+
|
|
1717
|
+
response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
|
|
1718
|
+
assert response.status_code == 200
|
|
1719
|
+
assert response.json() == {"status": "ok"}
|
|
1720
|
+
|
|
1721
|
+
@pytest.mark.respx(base_url=base_url)
|
|
1722
|
+
async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
|
|
1723
|
+
# Test that follow_redirects=False prevents following redirects
|
|
1724
|
+
respx_mock.post("/redirect").mock(
|
|
1725
|
+
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
|
|
1726
|
+
)
|
|
1727
|
+
|
|
1728
|
+
with pytest.raises(APIStatusError) as exc_info:
|
|
1729
|
+
await self.client.post(
|
|
1730
|
+
"/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
|
|
1731
|
+
)
|
|
1732
|
+
|
|
1733
|
+
assert exc_info.value.response.status_code == 302
|
|
1734
|
+
assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
|
|
@@ -21,3 +21,14 @@ def test_recursive_proxy() -> None:
|
|
|
21
21
|
assert dir(proxy) == []
|
|
22
22
|
assert type(proxy).__name__ == "RecursiveLazyProxy"
|
|
23
23
|
assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_isinstance_does_not_error() -> None:
|
|
27
|
+
class AlwaysErrorProxy(LazyProxy[Any]):
|
|
28
|
+
@override
|
|
29
|
+
def __load__(self) -> Any:
|
|
30
|
+
raise RuntimeError("Mocking missing dependency")
|
|
31
|
+
|
|
32
|
+
proxy = AlwaysErrorProxy()
|
|
33
|
+
assert not isinstance(proxy, dict)
|
|
34
|
+
assert isinstance(proxy, LazyProxy)
|
parallel_web-0.1.1/CHANGELOG.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.1.1 (2025-04-25)
|
|
4
|
-
|
|
5
|
-
Full Changelog: [v0.1.0...v0.1.1](https://github.com/shapleyai/parallel-sdk-python/compare/v0.1.0...v0.1.1)
|
|
6
|
-
|
|
7
|
-
### Features
|
|
8
|
-
|
|
9
|
-
* **api:** update via SDK Studio ([4cc79c4](https://github.com/shapleyai/parallel-sdk-python/commit/4cc79c4d1edaa9d1d080b81830961252c8b327c1))
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
### Bug Fixes
|
|
13
|
-
|
|
14
|
-
* **pydantic:** add fields to json schema, better error messages ([38a2ddc](https://github.com/shapleyai/parallel-sdk-python/commit/38a2ddc348ac7acf11f9f75f69900b628e539c1d))
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
### Chores
|
|
18
|
-
|
|
19
|
-
* **readme:** update low level api examples ([f17e34e](https://github.com/shapleyai/parallel-sdk-python/commit/f17e34e0e0a6d3205c344c278f1643826938e9d1))
|
|
20
|
-
|
|
21
|
-
## 0.1.0 (2025-04-24)
|
|
22
|
-
|
|
23
|
-
Full Changelog: [v0.0.1-alpha.0...v0.1.0](https://github.com/shapleyai/parallel-sdk-python/compare/v0.0.1-alpha.0...v0.1.0)
|
|
24
|
-
|
|
25
|
-
### Features
|
|
26
|
-
|
|
27
|
-
* **api:** add execute method and structured output support ([5e51379](https://github.com/shapleyai/parallel-sdk-python/commit/5e51379e3ff28bdf70a3cc9167d4413bf3e8690c))
|
|
28
|
-
* **api:** update via SDK Studio ([c393d04](https://github.com/shapleyai/parallel-sdk-python/commit/c393d048bddb554c37eb750ca57c4335243a70ed))
|
|
29
|
-
* **api:** update via SDK Studio ([6698e71](https://github.com/shapleyai/parallel-sdk-python/commit/6698e716bdddcf2146cc802cfaaa26f7ddb4d3dc))
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
### Chores
|
|
33
|
-
|
|
34
|
-
* go live ([061677a](https://github.com/shapleyai/parallel-sdk-python/commit/061677a22549f3dd3d9f4591c9ccfdf71209c12e))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|