Skip to content

✨ Feat/async typed sdk - #225

Open
YashGaykar0309 wants to merge 68 commits into
mainfrom
feat/async-typed-sdk
Open

✨ Feat/async typed sdk#225
YashGaykar0309 wants to merge 68 commits into
mainfrom
feat/async-typed-sdk

Conversation

@YashGaykar0309

@YashGaykar0309 YashGaykar0309 commented May 27, 2026

Copy link
Copy Markdown
Contributor

Description

Implements the foundation for an async-first, OpenAPI-driven Python SDK with generated typed service clients. The architecture now supports multi-service expansion such as OSC/OAPI, OKS and future services through a modular core plus generated service layers.

Main goals:

  • async-first design
  • multi-service support
  • strong typing and request/response validation
  • profile/config-based client creation
  • minimal integration complexity
  • modular core + generated service architecture
  • OpenAPI-driven client generation

Fixes: NA

Type of Change

Please check the relevant option(s):

  • 🐛 Bug fix
  • ✨ New feature
  • 🧹 Code cleanup or refactor
  • 📝 Documentation update
  • 🔧 Build or CI-related change
  • 🔒 Security fix
  • Other (specify):

How Has This Been Tested?

Please describe the test strategy:

  • Manual testing
  • Unit tests
  • Integration tests
  • Not tested yet

Commands used (if applicable):

uv run python -m pytest tests

Checklist

  • I have followed the Contributing Guidelines
  • I have added tests or explained why they are not needed
  • I have updated relevant documentation (README, examples, etc.)
  • My changes follow the Conventional Commits specification
  • My commits include appropriate Gitmoji

@YashGaykar0309
YashGaykar0309 requested a review from a team May 27, 2026 11:24
super().__init__(
os.path.join(os.path.dirname(__file__), "resources/outscale.yaml"), **kwargs

class AsyncOpenAPIActionAPI(OpenAPIActionAPI):
self.close()


class AsyncOpenAPIPathAPI(OpenAPIPathAPI):
request: GetKubernetesVersionsRequest | None = None,
) -> KubernetesVersionsResponse:
if request is None:
request = GetKubernetesVersionsRequest()
request: GetCPSubregionsRequest | None = None,
) -> CPSubregionsResponse:
if request is None:
request = GetCPSubregionsRequest()
request: GetControlPlanePlansRequest | None = None,
) -> ControlPlanesResponse:
if request is None:
request = GetControlPlanePlansRequest()
request: GetNetPeeringRequestTemplateRequest | None = None,
) -> TemplateResponse_NetPeeringRequest:
if request is None:
request = GetNetPeeringRequestTemplateRequest()
request: GetNetPeeringAcceptanceTemplateRequest | None = None,
) -> TemplateResponse_NetPeeringAcceptance:
if request is None:
request = GetNetPeeringAcceptanceTemplateRequest()
request: GetQuotasRequest | None = None,
) -> quotas__quota_schema__QuotasResponse:
if request is None:
request = GetQuotasRequest()
request: GetClientIPRequest | None = None,
) -> IPResponse:
if request is None:
request = GetClientIPRequest()
import asyncio
import copy
import os
import sys

@jobs62 jobs62 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start. Despite all comments i made, it whould be nice that public facing methods raise only "owned" exception. also string enum support whould be nice in the generator (that may need support for overlays as well)

api_version:
description: 'Outscale API version'
required: true
oks_api_url:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both services can have different release plan, so we should be able to build and release one without the other

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the release workflow to support per-service builds. It can now build osc, oks, or all, and the release script only updates/regenerates the selected service.

Comment thread .github/workflows/code-check-identified.yml Outdated
Comment thread docs/examples.md


async def main():
async with AsyncClient(profile="profile_1") as client:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should have one client per service

Comment thread docs/examples.md Outdated
Comment thread osc_sdk_python/runtime/request.py
Comment thread osc_sdk_python/authentication.py Outdated
Comment thread osc_sdk_python/limiter.py Outdated
from datetime import datetime, timezone, timedelta
import asyncio
import time

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as authentication, limiter should be a httpx middleware

Comment thread osc_sdk_python/runtime/async_/retry.py Outdated
@@ -0,0 +1,131 @@
import asyncio
import json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be a httpx middleware as well

Comment thread osc_sdk_python/runtime/async_/call.py Outdated
from ..request import RequestSpec


class AsyncCall(object):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can probably get ride of Call/Requester. i never understood why we had that much boilerplate for parameters of transports (should be in the session or a middleware ?)

Comment thread pyproject.toml Outdated
Updated README and docs examples to use Client / AsyncClient and async snake_case operation methods.

Added unresolved path placeholder validation in RequestSpec with unit tests.

Removed unnecessary IAM v2 secret injection from the CI workflow while keeping SDK credential support unchanged.
@YashGaykar0309
YashGaykar0309 force-pushed the feat/async-typed-sdk branch from 3c2fc69 to 3a96751 Compare July 9, 2026 06:30
Quote OSC_TEST_PASSWORD in local-tests.sh so shell parsing preserves the full value with spaces.
Remove the Python 3.9 tox environment and GitHub Actions mapping because the package metadata requires Python 3.10 or newer.
Raise AttributeError for unknown dynamic OSC and OKS operation attributes so hasattr() reflects the generated operation lists instead of accepting any name. Add regression coverage for valid and invalid dynamic service attributes.
Update the README requirements to state Python 3.10+ so they match the package metadata.
Treat missing OSC_TLS_SKIP_VERIFY as unset instead of false so environment loading does not accidentally override tls_skip_verify from the credentials file.
self.requests = []
self._lock = Lock()

def acquire(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't like than acquire and async_acquire are basicly duplicated logic. it's bad taste. with only déférence being who the lock and sleep are handled.

def __init__(self, message, *, request=None, response=None):
super().__init__(message)
self.request = request
self.response = response

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we have a helper to extract (at best effort) the request id that lead to failure ? we should also have a repl implémentation that print this information. that whould be a great help for customer support

if response is not None:
if 400 <= response.status_code < 500 and response.status_code != 429:
return False
return attempt < self.max_retries

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i know it was my code. but if the request have been sent, and error occure before we get an answer, retrying could be hazardous

)


class SdkTransport(httpx.BaseTransport):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as limiter. i don't like the fact that we have 2 class that a basicly duplicated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants