Skip to content

Commit 261aee3

Browse files
committed
feat: vpc splitted as discussed in comments
1 parent 6dd111b commit 261aee3

40 files changed

Lines changed: 1110 additions & 1239 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Requires [uv](https://docs.astral.sh/uv/) and Python 3.11+.
1919
uv sync --group dev # install with dev dependencies
2020
uv run ruff check src/ # lint
2121
uv run mypy src/ # type check
22-
uv run pytest -v # test
22+
uv run pytest -v # test core
2323
```
2424

2525
## License

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ dependencies = [
1818
[dependency-groups]
1919
dev = [
2020
"pytest>=9.0.2,<10",
21+
"pytest-mock>=3.14",
2122
"pytest-httpx>=0.36",
2223
"ruff>=0.15.2",
2324
"mypy>=1.19",
@@ -48,4 +49,5 @@ testpaths = ["tests"]
4849
log_cli = true
4950
log_cli_level = "INFO"
5051
log_cli_format = "%(asctime)s [%(levelname)s] %(message)s"
51-
log_cli_date_format = "%H:%M:%S"
52+
log_cli_date_format = "%H:%M:%S"
53+
addopts = "--import-mode=importlib --ignore=tests/acceptance"

src/sdk/core/opts.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Base classes for request options."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Any, ClassVar
6+
7+
from pydantic import BaseModel
8+
9+
10+
class BaseOpts(BaseModel):
11+
"""Base class for request body options.
12+
13+
Subclasses set ``_wrapper_key`` to wrap the payload in a named object
14+
(e.g. ``{"vpc": {...}}``). Without it the body is returned flat.
15+
16+
Field-omission rules (``exclude_none=True, exclude_defaults=True``):
17+
18+
* ``name: str = ""`` -> omitted when caller does not set it.
19+
* ``name: str | None = None`` -> ``None`` is omitted, but any other
20+
value (including ``""``) is sent, allowing callers to clear
21+
server-side fields.
22+
"""
23+
24+
_wrapper_key: ClassVar[str | None] = None
25+
26+
def to_request_body(self) -> dict[str, Any]:
27+
body = self.model_dump(
28+
exclude_none=True,
29+
exclude_defaults=True,
30+
by_alias=True,
31+
)
32+
if self._wrapper_key is not None:
33+
return {self._wrapper_key: body}
34+
return body
35+
36+
37+
class BaseQueryOpts(BaseModel):
38+
"""Base class for query string options.
39+
40+
Output is a flat ``dict[str, str]`` ready for an HTTP client's
41+
``params=`` argument. ``None`` and empty strings are dropped; numeric
42+
zero and ``False`` are preserved. Nested models or collections raise
43+
:class:`TypeError`.
44+
"""
45+
46+
def to_query_params(self) -> dict[str, str]:
47+
raw = self.model_dump(exclude_none=True, by_alias=True)
48+
params: dict[str, str] = {}
49+
for key, value in raw.items():
50+
if value == "":
51+
continue
52+
if isinstance(value, (dict, list)):
53+
raise TypeError(
54+
f"Query param {key!r} has non-scalar value {value!r}; "
55+
"query strings only support scalar values."
56+
)
57+
if isinstance(value, bool):
58+
params[key] = "true" if value else "false"
59+
else:
60+
params[key] = str(value)
61+
return params
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""VPC service, API v1.
2+
3+
Resources are exposed as submodules. Usage::
4+
5+
from sdk.services.vpc.v1 import vpcs
6+
7+
new_vpc = vpcs.create(client, vpcs.CreateVpcOpts(name="my-vpc"))
8+
for v in vpcs.list(client):
9+
print(v.id, v.name)
10+
11+
Future resources (subnets, peerings, ...) are added as sibling submodules.
12+
"""
13+
14+
from __future__ import annotations
15+
16+
from . import vpcs
17+
18+
__all__ = ["vpcs"]

src/sdk/services/vpc/v1/models.py

Lines changed: 0 additions & 155 deletions
This file was deleted.

src/sdk/services/vpc/v1/requests.py

Lines changed: 0 additions & 135 deletions
This file was deleted.

0 commit comments

Comments
 (0)