name: python-api-sdk-group description: Create or update a group of endpoints in the Python API SDK. Use this skill when the user asks to generate or modify a set of related API endpoints in the Python SDK, based on the OpenAPI schema. user-invocable: true allowed-tools: - read_file - write_file - search_replace - grep - bash - ask_user_question
Python API SDK Group Skill
This skill is a complete cookbook for creating or updating a group of related API endpoints in the Python API SDK. It is designed to be used when the user requests to generate or modify a set of related API endpoints based on the OpenAPI schema.
Follow the steps closely to ensure that the generated code is consistent with the existing codebase and adheres to the project's coding standards.
Prerequisite: Ask for the group name
If the user hasn't provided the group name in their initial request, ask them to specify the group name. The group name is a string that corresponds to the value of the x-speakeasy-group property in the OpenAPI schema. It is used to identify which endpoints belong to the same group.
Step 1: Identify the Group of Endpoints
Use jq to identify all the OpenAPI operations that have the property x-speakeasy-group set to the value provided by the user. This will help you determine which endpoints belong to the same group.
jq '.paths | to_entries[] | select(.value[].["x-speakeasy-group"] == "products") | .key' openapi.json
Step 4: Get or create the API SDK file for this group
Each group of endpoints should have a corresponding file in the polar_sdk_python/ directory. The file name should correspond to the group name, prefixed by _ to indicate that it's a private module. For example, if the group name is users, the file should be polar_sdk_python/_users.py.
If the file already exists, use it but don't scratch it entirely. If it doesn't exist, create a new file with the appropriate name.
Step 2: Generate or update the API endpoints
A group should have a single class that contains all the endpoints in the group.
Group class rules
- The class name should be in PascalCase and correspond to the group name. For example, if the group name is
users, the class name should beUsers. - The group class should inherit from
BaseAPIGrouplocated in thepolar_sdk_python/base.pymodule. - Don't add a docstring to the class, don't generate one, don't modify it if it already exists.
Endpoint methods
Each operation in the group should be implemented as a method of the class. The method name should be the value of x-speakeasy-name-override for the operation. The method should have the appropriate parameters and return type based on the OpenAPI schema. Here are the rules for generating the method:
Path parameters
- The path parameters should be positional arguments.
- Each parameter should be type annotated with the appropriate type based on the OpenAPI schema.
- Don't use the
TypedDictfor path parameters, as they are not passed as a dictionary but as individual arguments.
Query parameters
- The query parameters should be keyword arguments.
- They should be type annotated using
typing.Unpack, and use the appropriateTypedDictthat was automatically generated inpolar_sdk_python/types.py. Make sure to import the necessary types at the top of the file if they are not already imported.
Body
- The request body should also be defined as keyword arguments.
- It should type annotated using
typing.Unpack, and use the appropriateTypedDictthat was automatically generated inpolar_sdk_python/types.py. Make sure to import the necessary types at the top of the file if they are not already imported - Double check that the type you want to use is a
TypedDict. If it's a union type, likeUserA | UserB, you'll need to follow the special case below.
Special case for Union types in the body
In the case of Union types, like anyOf or oneOf, you'll need to create @typing.overload for each possible type in the union. DO NOT use a union type as an argument for typing.Unpack, as it will not work.
- Make sure the actual method implementation types the
bodyparameter astyping.Any, as it can accept multiple types. - The overloads shouldn't have a docstring.
Return type
The return type should correspond to the appropriate type that was automatically generated in polar_sdk_python/types.py.
If the operation has no response model, e.g. in the case of a 204 No Content response, the return type should be None.
Docstrings
If available, use the description property of the operation to add a docstring to the method. Otherwise, don't add a docstring, don't try to generate one.
Implementation
The implementation of the method consists of:
- Build a
Requestobject as defined inpolar_sdk_python/_base.py.- It should set the
success_modelcorresponding to the response model of the operation, i.e. the same model that is returned by the method. - It should set the method, path, path parameters, query parameters and body following the OpenAPI specification.
- It should set the
- Call
self._client.request()with theRequestobject. - Return
response.bodyfrom the response, orNoneif there is no response model.
For overloads, there shouldn't be any implementation, just the ellipsis (...).
Example
class Users(BaseAPIGroup):
def get(self, id: str) -> User:
response = self._client.request(
Request(
method="GET",
path="/users/{id}",
path_parameters={"id": id},
success_model=User,
)
)
return response.body
def list(self, **query: typing.Unpack[UserListQuery]) -> UserListResponse:
response = self._client.request(
Request(
method="GET",
path="/users/",
query_parameters=query,
success_model=UserListResponse,
)
)
return response.body
@typing.overload
def create(self, **body: typing.Unpack[UserCreateRequestTypeA]) -> User:
...
@typing.overload
def create(self, **body: typing.Unpack[UserCreateRequestTypeB]) -> User:
...
def create(self, **body: typing.Any) -> User:
response = self._client.request(
Request(
method="POST",
path="/users/",
body=body,
success_model=User,
)
)
return response.body
def update(self, id: str, **body: typing.Unpack[UserUpdateRequest]) -> User:
response = self._client.request(
Request(
method="PATCH",
path=f"/users/{id}/",
body=body,
success_model=User,
)
)
return response.body
def delete(self, id: str) -> None:
self._client.request(
Request(
method="DELETE",
path=f"/users/{id}/",
)
)
IMPORTANT: It's possible that developers added custom code to the existing API SDK files. Make sure to preserve that code when updating the files. Only add or modify the code related to the API endpoints in the group you are working on. Do not modify unrelated code to the task or issue you are working on.
Step 3: Add it to the main SDK wrapper
After generating or updating the API endpoints, make sure to add the new endpoints to the main SDK wrapper, PolarClient. It's located in polar_sdk_python/_client.py.
It should be added:
- As a type annotated class attribute
- As an instance attribute in the
__init__method
Don't forget to import the new group module at the top of the file.
Example
from ._users import Users
class PolarClient:
users: Users # Add the group as a type annotated class attribute
def __init__(self, server: Server = "production") -> None:
self._client = BaseClient(base_url=_SERVERS[server])
self.users = Users(self._client) # Add the group as an instance attribute
If the group is already present in the PolarClient class, you can skip this step.
Done
Once done, your task is done. No need to make follow-up testing or checks. End here and report back.