parallel-web 0.1.0__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.

Files changed (82) hide show
  1. parallel_web-0.1.2/.release-please-manifest.json +3 -0
  2. parallel_web-0.1.2/CHANGELOG.md +76 -0
  3. {parallel_web-0.1.0 → parallel_web-0.1.2}/CONTRIBUTING.md +3 -4
  4. {parallel_web-0.1.0 → parallel_web-0.1.2}/PKG-INFO +24 -20
  5. {parallel_web-0.1.0 → parallel_web-0.1.2}/README.md +17 -13
  6. {parallel_web-0.1.0 → parallel_web-0.1.2}/SECURITY.md +2 -2
  7. {parallel_web-0.1.0 → parallel_web-0.1.2}/pyproject.toml +6 -5
  8. {parallel_web-0.1.0 → parallel_web-0.1.2}/requirements-dev.lock +4 -0
  9. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/__init__.py +5 -0
  10. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_base_client.py +22 -2
  11. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_models.py +2 -0
  12. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_types.py +2 -0
  13. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_proxy.py +4 -1
  14. parallel_web-0.1.2/src/parallel/_utils/_resources_proxy.py +24 -0
  15. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_version.py +1 -1
  16. parallel_web-0.1.2/src/parallel/lib/_pydantic.py +31 -0
  17. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/_time.py +19 -2
  18. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/resources/task_run.py +4 -4
  19. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_run.py +1 -4
  20. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/conftest.py +2 -0
  21. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_client.py +116 -41
  22. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_utils/test_proxy.py +11 -0
  23. parallel_web-0.1.0/.release-please-manifest.json +0 -3
  24. parallel_web-0.1.0/CHANGELOG.md +0 -16
  25. parallel_web-0.1.0/src/parallel/lib/_pydantic.py +0 -29
  26. {parallel_web-0.1.0 → parallel_web-0.1.2}/.gitignore +0 -0
  27. {parallel_web-0.1.0 → parallel_web-0.1.2}/LICENSE +0 -0
  28. {parallel_web-0.1.0 → parallel_web-0.1.2}/api.md +0 -0
  29. {parallel_web-0.1.0 → parallel_web-0.1.2}/bin/check-release-environment +0 -0
  30. {parallel_web-0.1.0 → parallel_web-0.1.2}/bin/publish-pypi +0 -0
  31. {parallel_web-0.1.0 → parallel_web-0.1.2}/examples/.keep +0 -0
  32. {parallel_web-0.1.0 → parallel_web-0.1.2}/mypy.ini +0 -0
  33. {parallel_web-0.1.0 → parallel_web-0.1.2}/noxfile.py +0 -0
  34. {parallel_web-0.1.0 → parallel_web-0.1.2}/release-please-config.json +0 -0
  35. {parallel_web-0.1.0 → parallel_web-0.1.2}/requirements.lock +0 -0
  36. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_client.py +0 -0
  37. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_compat.py +0 -0
  38. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_constants.py +0 -0
  39. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_exceptions.py +0 -0
  40. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_files.py +0 -0
  41. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_qs.py +0 -0
  42. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_resource.py +0 -0
  43. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_response.py +0 -0
  44. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_streaming.py +0 -0
  45. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/__init__.py +0 -0
  46. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_logs.py +0 -0
  47. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_reflection.py +0 -0
  48. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_streams.py +0 -0
  49. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_sync.py +0 -0
  50. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_transform.py +0 -0
  51. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_typing.py +0 -0
  52. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/_utils/_utils.py +0 -0
  53. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/.keep +0 -0
  54. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/__init__.py +0 -0
  55. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/_parsing/__init__.py +0 -0
  56. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/_parsing/_task_run_result.py +0 -0
  57. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/lib/_parsing/_task_spec.py +0 -0
  58. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/py.typed +0 -0
  59. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/resources/__init__.py +0 -0
  60. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/__init__.py +0 -0
  61. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/json_schema_param.py +0 -0
  62. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/parsed_task_run_result.py +0 -0
  63. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_run_create_params.py +0 -0
  64. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_run_result.py +0 -0
  65. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_run_result_params.py +0 -0
  66. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/task_spec_param.py +0 -0
  67. {parallel_web-0.1.0 → parallel_web-0.1.2}/src/parallel/types/text_schema_param.py +0 -0
  68. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/__init__.py +0 -0
  69. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/api_resources/__init__.py +0 -0
  70. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/api_resources/test_task_run.py +0 -0
  71. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/sample_file.txt +0 -0
  72. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_deepcopy.py +0 -0
  73. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_extract_files.py +0 -0
  74. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_files.py +0 -0
  75. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_models.py +0 -0
  76. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_qs.py +0 -0
  77. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_required_args.py +0 -0
  78. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_response.py +0 -0
  79. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_streaming.py +0 -0
  80. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_transform.py +0 -0
  81. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/test_utils/test_typing.py +0 -0
  82. {parallel_web-0.1.0 → parallel_web-0.1.2}/tests/utils.py +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.1.2"
3
+ }
@@ -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
- $ rye shell
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/shapleyai/parallel-sdk-python.git
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/shapleyai/parallel-sdk-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up.
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.0
3
+ Version: 0.1.2
4
4
  Summary: The official Python library for the Parallel API
5
- Project-URL: Homepage, https://github.com/shapleyai/parallel-sdk-python
6
- Project-URL: Repository, https://github.com/shapleyai/parallel-sdk-python
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
- [![PyPI version](https://img.shields.io/pypi/v/parallel-web.svg)](https://pypi.org/project/parallel-web/)
34
+ [![PyPI version](https://github.com/parallel-web/parallel-sdk-python/tree/main/<https://img.shields.io/pypi/v/parallel-web.svg?label=pypi%20(stable)>)](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/shapleyai/parallel-sdk-python/tree/main/api.md).
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/shapleyai/parallel-sdk-python/tree/main/api.md).
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
@@ -223,15 +223,15 @@ from parallel.types import TaskSpecParam
223
223
  client = Parallel()
224
224
 
225
225
  task_run = client.task_run.create(
226
- input="France (2023)",
227
- processor="processor",
226
+ input={"country": "France", "year": 2023},
227
+ processor="core",
228
228
  task_spec={
229
229
  "output_schema": {
230
230
  "json_schema": {
231
231
  "additionalProperties": False,
232
232
  "properties": {
233
233
  "gdp": {
234
- "description": "GDP in USD for the year, formatted like '$3.1 trillion (2023)'",
234
+ "description": "GDP in USD for the year",
235
235
  "type": "string",
236
236
  }
237
237
  },
@@ -244,12 +244,16 @@ task_run = client.task_run.create(
244
244
  "json_schema": {
245
245
  "additionalProperties": False,
246
246
  "properties": {
247
- "gdp": {
248
- "description": "GDP in USD for the year, formatted like '$3.1 trillion (2023)'",
247
+ "country": {
248
+ "description": "Name of the country to research",
249
249
  "type": "string",
250
- }
250
+ },
251
+ "year": {
252
+ "description": "Year for which to retrieve information",
253
+ "type": "integer",
254
+ },
251
255
  },
252
- "required": ["gdp"],
256
+ "required": ["country", "year"],
253
257
  "type": "object",
254
258
  },
255
259
  "type": "json",
@@ -257,7 +261,7 @@ task_run = client.task_run.create(
257
261
  },
258
262
  )
259
263
 
260
- run_result = client.task_run.result(task_run.id)
264
+ run_result = client.task_run.result(task_run.run_id)
261
265
  print(run_result.output)
262
266
  ```
263
267
 
@@ -339,7 +343,7 @@ client.with_options(max_retries=5).task_run.execute(
339
343
  ### Timeouts
340
344
 
341
345
  By default requests time out after 1 minute. You can configure this with a `timeout` option,
342
- 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:
343
347
 
344
348
  ```python
345
349
  from parallel import Parallel
@@ -365,7 +369,7 @@ client.with_options(timeout=5.0).task_run.execute(
365
369
 
366
370
  On timeout, an `APITimeoutError` is thrown.
367
371
 
368
- Note that requests that time out are [retried twice by default](https://github.com/shapleyai/parallel-sdk-python/tree/main/#retries).
372
+ Note that requests that time out are [retried twice by default](https://github.com/parallel-web/parallel-sdk-python/tree/main/#retries).
369
373
 
370
374
  ## Advanced
371
375
 
@@ -412,9 +416,9 @@ task_run = response.parse() # get the object that `task_run.execute()` would ha
412
416
  print(task_run.output)
413
417
  ```
414
418
 
415
- These methods return an [`APIResponse`](https://github.com/shapleyai/parallel-sdk-python/tree/main/src/parallel/_response.py) object.
419
+ These methods return an [`APIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) object.
416
420
 
417
- The async client returns an [`AsyncAPIResponse`](https://github.com/shapleyai/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.
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.
418
422
 
419
423
  #### `.with_streaming_response`
420
424
 
@@ -522,7 +526,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con
522
526
 
523
527
  We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
524
528
 
525
- We are keen for your feedback; please open an [issue](https://www.github.com/shapleyai/parallel-sdk-python/issues) with questions, bugs, or suggestions.
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.
526
530
 
527
531
  ### Determining the installed version
528
532
 
@@ -541,4 +545,4 @@ Python 3.8 or higher.
541
545
 
542
546
  ## Contributing
543
547
 
544
- See [the contributing documentation](https://github.com/shapleyai/parallel-sdk-python/tree/main/./CONTRIBUTING.md).
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
- [![PyPI version](https://img.shields.io/pypi/v/parallel-web.svg)](https://pypi.org/project/parallel-web/)
3
+ [![PyPI version](<https://img.shields.io/pypi/v/parallel-web.svg?label=pypi%20(stable)>)](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,
@@ -192,15 +192,15 @@ from parallel.types import TaskSpecParam
192
192
  client = Parallel()
193
193
 
194
194
  task_run = client.task_run.create(
195
- input="France (2023)",
196
- processor="processor",
195
+ input={"country": "France", "year": 2023},
196
+ processor="core",
197
197
  task_spec={
198
198
  "output_schema": {
199
199
  "json_schema": {
200
200
  "additionalProperties": False,
201
201
  "properties": {
202
202
  "gdp": {
203
- "description": "GDP in USD for the year, formatted like '$3.1 trillion (2023)'",
203
+ "description": "GDP in USD for the year",
204
204
  "type": "string",
205
205
  }
206
206
  },
@@ -213,12 +213,16 @@ task_run = client.task_run.create(
213
213
  "json_schema": {
214
214
  "additionalProperties": False,
215
215
  "properties": {
216
- "gdp": {
217
- "description": "GDP in USD for the year, formatted like '$3.1 trillion (2023)'",
216
+ "country": {
217
+ "description": "Name of the country to research",
218
218
  "type": "string",
219
- }
219
+ },
220
+ "year": {
221
+ "description": "Year for which to retrieve information",
222
+ "type": "integer",
223
+ },
220
224
  },
221
- "required": ["gdp"],
225
+ "required": ["country", "year"],
222
226
  "type": "object",
223
227
  },
224
228
  "type": "json",
@@ -226,7 +230,7 @@ task_run = client.task_run.create(
226
230
  },
227
231
  )
228
232
 
229
- run_result = client.task_run.result(task_run.id)
233
+ run_result = client.task_run.result(task_run.run_id)
230
234
  print(run_result.output)
231
235
  ```
232
236
 
@@ -308,7 +312,7 @@ client.with_options(max_retries=5).task_run.execute(
308
312
  ### Timeouts
309
313
 
310
314
  By default requests time out after 1 minute. You can configure this with a `timeout` option,
311
- 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:
312
316
 
313
317
  ```python
314
318
  from parallel import Parallel
@@ -381,9 +385,9 @@ task_run = response.parse() # get the object that `task_run.execute()` would ha
381
385
  print(task_run.output)
382
386
  ```
383
387
 
384
- These methods return an [`APIResponse`](https://github.com/shapleyai/parallel-sdk-python/tree/main/src/parallel/_response.py) object.
388
+ These methods return an [`APIResponse`](https://github.com/parallel-web/parallel-sdk-python/tree/main/src/parallel/_response.py) object.
385
389
 
386
- The async client returns an [`AsyncAPIResponse`](https://github.com/shapleyai/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.
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.
387
391
 
388
392
  #### `.with_streaming_response`
389
393
 
@@ -491,7 +495,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con
491
495
 
492
496
  We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
493
497
 
494
- We are keen for your feedback; please open an [issue](https://www.github.com/shapleyai/parallel-sdk-python/issues) with questions, bugs, or suggestions.
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.
495
499
 
496
500
  ### Determining the installed version
497
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.0"
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/shapleyai/parallel-sdk-python"
38
- Repository = "https://github.com/shapleyai/parallel-sdk-python"
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/shapleyai/parallel-sdk-python/tree/main/\g<2>)'
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 inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
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 inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
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
- proxied = self.__get_proxied__()
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__()
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "parallel"
4
- __version__ = "0.1.0" # x-release-please-version
4
+ __version__ = "0.1.2" # x-release-please-version
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from typing import Any
5
+ from typing_extensions import TypeGuard
6
+
7
+ import pydantic
8
+
9
+ from .._compat import PYDANTIC_V2, model_json_schema
10
+
11
+
12
+ def to_json_schema(
13
+ model_type: type[pydantic.BaseModel] | pydantic.TypeAdapter[Any],
14
+ ) -> dict[str, Any]:
15
+ """Convert a Pydantic model/type adapter to a JSON schema."""
16
+ if is_basemodel_type(model_type):
17
+ schema = model_json_schema(model_type)
18
+ elif isinstance(model_type, pydantic.TypeAdapter):
19
+ if not PYDANTIC_V2:
20
+ raise TypeError(f"TypeAdapters are only supported with Pydantic v2 - {model_type}")
21
+ schema = model_type.json_schema()
22
+ else:
23
+ raise TypeError(f"Unsupported type: {model_type}")
24
+
25
+ # modify the schema to make it compatible with the API format
26
+ schema["additionalProperties"] = False
27
+ return schema
28
+
29
+ def is_basemodel_type(model_type: object) -> TypeGuard[type[pydantic.BaseModel]]:
30
+ """Check if a type is a Pydantic BaseModel to avoid using type: ignore."""
31
+ return inspect.isclass(model_type) and issubclass(model_type, pydantic.BaseModel)
@@ -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
- # retry on timeouts from the API
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/shapleyai/parallel-sdk-python#accessing-raw-response-data-eg-headers
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/shapleyai/parallel-sdk-python#with_streaming_response
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/shapleyai/parallel-sdk-python#accessing-raw-response-data-eg-headers
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/shapleyai/parallel-sdk-python#with_streaming_response
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
 
@@ -29,12 +29,9 @@ class TaskRun(BaseModel):
29
29
  is_active: bool
30
30
  """Whether the run is currently active; i.e.
31
31
 
32
- status is one of {'queued', 'running', 'cancelling'}.
32
+ status is one of {'running', 'queued', 'cancelling'}.
33
33
  """
34
34
 
35
- message: Optional[str] = None
36
- """Human-readable status message for the run."""
37
-
38
35
  modified_at: Optional[str] = None
39
36
  """Timestamp of the last modification to the task, as an RFC 3339 string."""
40
37
 
@@ -1,3 +1,5 @@
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  import os
@@ -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
- self.client.post(
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
- self.client.post(
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(self, respx_mock: MockRouter) -> None:
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 self.client.post(
1505
- "/v1/tasks/runs",
1506
- body=cast(
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(self, respx_mock: MockRouter) -> None:
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 self.client.post(
1522
- "/v1/tasks/runs",
1523
- body=cast(
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)
@@ -1,3 +0,0 @@
1
- {
2
- ".": "0.1.0"
3
- }
@@ -1,16 +0,0 @@
1
- # Changelog
2
-
3
- ## 0.1.0 (2025-04-24)
4
-
5
- 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)
6
-
7
- ### Features
8
-
9
- * **api:** add execute method and structured output support ([5e51379](https://github.com/shapleyai/parallel-sdk-python/commit/5e51379e3ff28bdf70a3cc9167d4413bf3e8690c))
10
- * **api:** update via SDK Studio ([c393d04](https://github.com/shapleyai/parallel-sdk-python/commit/c393d048bddb554c37eb750ca57c4335243a70ed))
11
- * **api:** update via SDK Studio ([6698e71](https://github.com/shapleyai/parallel-sdk-python/commit/6698e716bdddcf2146cc802cfaaa26f7ddb4d3dc))
12
-
13
-
14
- ### Chores
15
-
16
- * go live ([061677a](https://github.com/shapleyai/parallel-sdk-python/commit/061677a22549f3dd3d9f4591c9ccfdf71209c12e))
@@ -1,29 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import inspect
4
- from typing import Any
5
- from typing_extensions import TypeGuard
6
-
7
- import pydantic
8
-
9
- from .._compat import PYDANTIC_V2, model_json_schema
10
-
11
-
12
- def to_json_schema(
13
- model: type[pydantic.BaseModel] | pydantic.TypeAdapter[Any],
14
- ) -> dict[str, Any]:
15
- """Convert a Pydantic model/type adapter to a JSON schema."""
16
- if inspect.isclass(model) and is_basemodel_type(model):
17
- return model_json_schema(model)
18
-
19
- if PYDANTIC_V2 and isinstance(model, pydantic.TypeAdapter):
20
- return model.json_schema()
21
-
22
- raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {model}")
23
-
24
-
25
- def is_basemodel_type(typ: type | object) -> TypeGuard[type[pydantic.BaseModel]]:
26
- """Check if a type is a Pydantic BaseModel."""
27
- if not inspect.isclass(typ):
28
- return False
29
- return issubclass(typ, pydantic.BaseModel)
File without changes
File without changes
File without changes
File without changes
File without changes