vantage-python 0.2.0__tar.gz → 0.3.1__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.
- vantage_python-0.3.1/.github/workflows/pypi-publish.yml +54 -0
- {vantage_python-0.2.0 → vantage_python-0.3.1}/.github/workflows/test.yml +11 -2
- vantage_python-0.3.1/.gitignore +7 -0
- {vantage_python-0.2.0 → vantage_python-0.3.1}/Makefile +7 -3
- {vantage_python-0.2.0 → vantage_python-0.3.1}/PKG-INFO +2 -2
- {vantage_python-0.2.0 → vantage_python-0.3.1}/README.md +1 -1
- {vantage_python-0.2.0 → vantage_python-0.3.1}/autogen.py +73 -5
- {vantage_python-0.2.0 → vantage_python-0.3.1}/pyproject.toml +1 -1
- vantage_python-0.3.1/src/vantage/_async/client.py +3224 -0
- vantage_python-0.3.1/src/vantage/_sync/client.py +3224 -0
- vantage_python-0.3.1/src/vantage/_types.py +1938 -0
- {vantage_python-0.2.0 → vantage_python-0.3.1}/tests/test_e2e.py +8 -8
- vantage_python-0.2.0/.github/workflows/pypi-publish.yml +0 -25
- vantage_python-0.2.0/.gitignore +0 -12
- {vantage_python-0.2.0 → vantage_python-0.3.1}/LICENSE +0 -0
- {vantage_python-0.2.0 → vantage_python-0.3.1}/src/vantage/__init__.py +0 -0
- {vantage_python-0.2.0 → vantage_python-0.3.1}/src/vantage/_async/__init__.py +0 -0
- {vantage_python-0.2.0 → vantage_python-0.3.1}/src/vantage/_base.py +0 -0
- {vantage_python-0.2.0 → vantage_python-0.3.1}/src/vantage/_sync/__init__.py +0 -0
- {vantage_python-0.2.0 → vantage_python-0.3.1}/src/vantage/py.typed +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Publish Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
check-if-tag:
|
|
14
|
+
name: Check if Tag is Present
|
|
15
|
+
runs-on: ubuntu-22.04
|
|
16
|
+
outputs:
|
|
17
|
+
TAG_NOT_PRESENT: ${{ steps.get-tag.outputs.TAG_NOT_PRESENT }}
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
- name: Get the tag from Python and check if it's present
|
|
23
|
+
id: get-tag
|
|
24
|
+
run: |
|
|
25
|
+
pip install toml
|
|
26
|
+
VERSION=$(python -c "import toml; print(toml.load('pyproject.toml')['project']['version'])")
|
|
27
|
+
if git tag -l "v$VERSION" | grep -q .; then
|
|
28
|
+
echo "Tag v$VERSION is present"
|
|
29
|
+
echo "TAG_NOT_PRESENT=false" >> $GITHUB_OUTPUT
|
|
30
|
+
else
|
|
31
|
+
echo "Tag v$VERSION is not present"
|
|
32
|
+
echo "TAG_NOT_PRESENT=true" >> $GITHUB_OUTPUT
|
|
33
|
+
|
|
34
|
+
git tag "v$VERSION"
|
|
35
|
+
git config user.name "github-actions[bot]"
|
|
36
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
37
|
+
git push origin "v$VERSION"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
publish:
|
|
41
|
+
name: Publish Package to PyPI
|
|
42
|
+
runs-on: ubuntu-22.04
|
|
43
|
+
needs: check-if-tag
|
|
44
|
+
if: needs.check-if-tag.outputs.TAG_NOT_PRESENT == 'true'
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: actions/setup-python@v5
|
|
48
|
+
with:
|
|
49
|
+
python-version: "3.14"
|
|
50
|
+
- run: pip install build twine
|
|
51
|
+
- run: pip install -e ".[dev]"
|
|
52
|
+
- run: python -m build
|
|
53
|
+
- name: Publish release distributions to PyPI
|
|
54
|
+
uses: pypa/gh-action-pypi-publish@v1.13.0
|
|
@@ -14,7 +14,6 @@ jobs:
|
|
|
14
14
|
with:
|
|
15
15
|
python-version: ${{ matrix.python-version }}
|
|
16
16
|
- run: pip install -e ".[dev]"
|
|
17
|
-
- run: python autogen.py
|
|
18
17
|
- run: pytest tests/test_e2e.py
|
|
19
18
|
env:
|
|
20
19
|
VANTAGE_API_TOKEN: ${{ secrets.VANTAGE_API_TOKEN }}
|
|
@@ -32,5 +31,15 @@ jobs:
|
|
|
32
31
|
with:
|
|
33
32
|
python-version: ${{ matrix.python-version }}
|
|
34
33
|
- run: pip install -e ".[dev]"
|
|
35
|
-
- run: python autogen.py
|
|
36
34
|
- run: python -c "from vantage import Client, AsyncClient"
|
|
35
|
+
|
|
36
|
+
compare-to-api:
|
|
37
|
+
name: Compare to API
|
|
38
|
+
runs-on: ubuntu-22.04
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: actions/setup-python@v5
|
|
42
|
+
with:
|
|
43
|
+
python-version: "3.14"
|
|
44
|
+
- run: pip install -e ".[dev]"
|
|
45
|
+
- run: make diff
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
.PHONY: install publish
|
|
1
|
+
.PHONY: install generate diff publish
|
|
2
2
|
|
|
3
3
|
install:
|
|
4
|
-
python3 autogen.py
|
|
5
4
|
python3 -m pip install -e ".[dev]"
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
generate:
|
|
8
7
|
python3 autogen.py
|
|
8
|
+
|
|
9
|
+
diff: generate
|
|
10
|
+
git diff --exit-code src/vantage
|
|
11
|
+
|
|
12
|
+
publish:
|
|
9
13
|
python3 -m build
|
|
10
14
|
twine upload dist/*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vantage-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Python SDK for the Vantage API
|
|
5
5
|
Project-URL: Homepage, https://github.com/vantage-sh/vantage-python
|
|
6
6
|
Project-URL: Repository, https://github.com/vantage-sh/vantage-python
|
|
@@ -96,7 +96,7 @@ except VantageAPIError as e:
|
|
|
96
96
|
make install
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
This
|
|
99
|
+
This generates Pydantic models, sync client, and the async client. Your pip version will need to be up to date for this. If you wish to generate the client first, you should use `make generate`.
|
|
100
100
|
|
|
101
101
|
### Testing
|
|
102
102
|
|
|
@@ -75,7 +75,7 @@ except VantageAPIError as e:
|
|
|
75
75
|
make install
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
This
|
|
78
|
+
This generates Pydantic models, sync client, and the async client. Your pip version will need to be up to date for this. If you wish to generate the client first, you should use `make generate`.
|
|
79
79
|
|
|
80
80
|
### Testing
|
|
81
81
|
|
|
@@ -248,7 +248,7 @@ def extract_response_type(
|
|
|
248
248
|
responses: dict[str, Any], schemas: dict[str, Any]
|
|
249
249
|
) -> str | None:
|
|
250
250
|
"""Extract successful response type."""
|
|
251
|
-
for code in ["200", "201", "202", "
|
|
251
|
+
for code in ["200", "201", "202", "203"]:
|
|
252
252
|
if code not in responses:
|
|
253
253
|
continue
|
|
254
254
|
response = responses[code]
|
|
@@ -373,6 +373,70 @@ def generate_method_name(endpoint: Endpoint, resource_name: str) -> str:
|
|
|
373
373
|
return to_snake_case(op_id)
|
|
374
374
|
|
|
375
375
|
|
|
376
|
+
def _extract_inner_type(type_hint: str, generic_prefix: str) -> str | None:
|
|
377
|
+
"""Extract inner type from simple generic forms like Prefix[Inner]."""
|
|
378
|
+
if not type_hint.startswith(generic_prefix) or not type_hint.endswith("]"):
|
|
379
|
+
return None
|
|
380
|
+
return type_hint[len(generic_prefix):-1].strip()
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _extract_dict_value_type(type_hint: str) -> str | None:
|
|
384
|
+
"""Extract value type from Dict[str, ValueType]."""
|
|
385
|
+
if not type_hint.startswith("Dict[") or not type_hint.endswith("]"):
|
|
386
|
+
return None
|
|
387
|
+
inner = type_hint[len("Dict["):-1].strip()
|
|
388
|
+
key_and_value = inner.split(",", 1)
|
|
389
|
+
if len(key_and_value) != 2:
|
|
390
|
+
return None
|
|
391
|
+
key_type = key_and_value[0].strip()
|
|
392
|
+
value_type = key_and_value[1].strip()
|
|
393
|
+
if key_type not in {"str", "Optional[str]"}:
|
|
394
|
+
return None
|
|
395
|
+
return value_type
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _is_model_type(type_hint: str) -> bool:
|
|
399
|
+
"""Return True for generated Pydantic model type names."""
|
|
400
|
+
return bool(re.match(r"^[A-Z][A-Za-z0-9_]*$", type_hint))
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def _append_response_mapping(lines: list[str], return_type: str, data_var: str) -> None:
|
|
404
|
+
"""Append generated code that coerces dict payloads into typed models."""
|
|
405
|
+
type_hint = return_type.strip()
|
|
406
|
+
|
|
407
|
+
optional_inner = _extract_inner_type(type_hint, "Optional[")
|
|
408
|
+
if optional_inner:
|
|
409
|
+
type_hint = optional_inner
|
|
410
|
+
|
|
411
|
+
list_inner = _extract_inner_type(type_hint, "List[")
|
|
412
|
+
if list_inner and _is_model_type(list_inner):
|
|
413
|
+
lines.extend(
|
|
414
|
+
[
|
|
415
|
+
f" if isinstance({data_var}, list):",
|
|
416
|
+
f" return [{list_inner}.model_validate(item) if isinstance(item, dict) else item for item in {data_var}]",
|
|
417
|
+
]
|
|
418
|
+
)
|
|
419
|
+
return
|
|
420
|
+
|
|
421
|
+
dict_inner = _extract_dict_value_type(type_hint)
|
|
422
|
+
if dict_inner and _is_model_type(dict_inner):
|
|
423
|
+
lines.extend(
|
|
424
|
+
[
|
|
425
|
+
f" if isinstance({data_var}, dict):",
|
|
426
|
+
f" return {{k: {dict_inner}.model_validate(v) if isinstance(v, dict) else v for k, v in {data_var}.items()}}",
|
|
427
|
+
]
|
|
428
|
+
)
|
|
429
|
+
return
|
|
430
|
+
|
|
431
|
+
if _is_model_type(type_hint):
|
|
432
|
+
lines.extend(
|
|
433
|
+
[
|
|
434
|
+
f" if isinstance({data_var}, dict):",
|
|
435
|
+
f" return {type_hint}.model_validate({data_var})",
|
|
436
|
+
]
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
|
|
376
440
|
def generate_pydantic_models(schema: dict[str, Any]) -> str:
|
|
377
441
|
"""Generate Pydantic models from OpenAPI schemas."""
|
|
378
442
|
schemas = schema.get("components", {}).get("schemas", {})
|
|
@@ -675,10 +739,12 @@ def generate_sync_method(endpoint: Endpoint, method_name: str) -> list[str]:
|
|
|
675
739
|
else:
|
|
676
740
|
lines.append(" body_data = None")
|
|
677
741
|
|
|
678
|
-
# Make request
|
|
742
|
+
# Make request and coerce response payload into typed models where possible
|
|
679
743
|
lines.append(
|
|
680
|
-
f'
|
|
744
|
+
f' data = self._client.request("{endpoint.method}", path, params=params, body=body_data)'
|
|
681
745
|
)
|
|
746
|
+
_append_response_mapping(lines, return_type, "data")
|
|
747
|
+
lines.append(" return data")
|
|
682
748
|
|
|
683
749
|
return lines
|
|
684
750
|
|
|
@@ -899,10 +965,12 @@ def generate_async_method(endpoint: Endpoint, method_name: str) -> list[str]:
|
|
|
899
965
|
else:
|
|
900
966
|
lines.append(" body_data = None")
|
|
901
967
|
|
|
902
|
-
# Make request
|
|
968
|
+
# Make request and coerce response payload into typed models where possible
|
|
903
969
|
lines.append(
|
|
904
|
-
f'
|
|
970
|
+
f' data = await self._client.request("{endpoint.method}", path, params=params, body=body_data)'
|
|
905
971
|
)
|
|
972
|
+
_append_response_mapping(lines, return_type, "data")
|
|
973
|
+
lines.append(" return data")
|
|
906
974
|
|
|
907
975
|
return lines
|
|
908
976
|
|