vantage-python 0.3.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.
@@ -17,6 +17,8 @@ jobs:
17
17
  TAG_NOT_PRESENT: ${{ steps.get-tag.outputs.TAG_NOT_PRESENT }}
18
18
  steps:
19
19
  - uses: actions/checkout@v4
20
+ with:
21
+ fetch-depth: 0
20
22
  - name: Get the tag from Python and check if it's present
21
23
  id: get-tag
22
24
  run: |
@@ -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,7 +31,6 @@ 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"
37
35
 
38
36
  compare-to-api:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vantage-python
3
- Version: 0.3.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
@@ -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' return self._client.request("{endpoint.method}", path, params=params, body=body_data)'
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' return await self._client.request("{endpoint.method}", path, params=params, body=body_data)'
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
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vantage-python"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Python SDK for the Vantage API"
9
9
  readme = "README.md"
10
10
  license = "MIT"