Skip to content

feat: update python sdk generator #169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@
"customManagers": [
{
"customType": "regex",
"fileMatch": [
"(^|\\/)generate-sdk\\.sh$"
"managerFilePatterns": [
"/(^|\\/)generate-sdk\\.sh$/"
],
"matchStrings": [
"# Renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?) versioning=(?<versioning>.*?)?\\sGENERATOR_VERSION=\\\"?(?<currentValue>.*?)\\\"\\s"
"# Renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?) versioning=(?<versioning>.*?)?\\s+GENERATOR_VERSION=\"?(?<currentValue>.*?)\""
]
},
{
"customType": "regex",
"fileMatch": [
"(^|\\/)go\\.mod\\.mustache$"
"managerFilePatterns": [
"/(^|\\/)go\\.mod\\.mustache$/"
],
"matchStrings": [
"{{gitHost}}\\/{{gitUserId}}\\/{{gitRepoId}}\\/core ?(?<currentValue>.*?)\\s"
"github.com\/stackitcloud\/stackit-sdk-go\/core (?<currentValue>.*?)\\s"
],
"datasourceTemplate": "go",
"depNameTemplate": "github.com/stackitcloud/stackit-sdk-go",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
runs-on: ${{ matrix.os }}
steps:
- name: Install SSH Key
Expand Down
7 changes: 4 additions & 3 deletions scripts/generate-sdk/generate-sdk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ go)
GENERATOR_VERSION="v6.6.0" # There are issues with GO SDK generation in version v7
;;
python)
GENERATOR_VERSION="v7.9.0"
# Renovate: datasource=github-tags depName=OpenAPITools/openapi-generator versioning=semver
GENERATOR_VERSION="v7.14.0"
;;
*)
echo "SDK language not supported."
Expand Down Expand Up @@ -90,8 +91,8 @@ python)
echo -e "\n>> Generating the Python SDK..."

source ${LANGUAGE_GENERATORS_FOLDER_PATH}/${LANGUAGE}.sh
# Usage: generate_python_sdk GENERATOR_PATH GIT_HOST GIT_USER_ID [GIT_REPO_ID] [SDK_REPO_URL]
generate_python_sdk ${jar_path} ${GIT_HOST} ${GIT_USER_ID} ${GIT_REPO_ID} ${SDK_REPO_URL}
# Usage: generate_python_sdk GENERATOR_PATH GIT_HOST GIT_USER_ID [GIT_REPO_ID] [SDK_REPO_URL] [SDK_BRANCH]
generate_python_sdk "${jar_path}" "${GIT_HOST}" "${GIT_USER_ID}" "${GIT_REPO_ID}" "${SDK_REPO_URL}" "${SDK_BRANCH}"
;;
*)
echo "! SDK language not supported."
Expand Down
3 changes: 2 additions & 1 deletion scripts/generate-sdk/languages/python.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ generate_python_sdk() {
# Optional parameters
local GIT_REPO_ID=$4
local SDK_REPO_URL=$5
local SDK_BRANCH=$6

# Check required parameters
if [[ -z ${GIT_HOST} ]]; then
Expand Down Expand Up @@ -54,7 +55,7 @@ generate_python_sdk() {
echo "Old SDK repo clone was found, it will be removed."
rm -rf ${SDK_REPO_LOCAL_PATH}
fi
git clone --quiet ${SDK_REPO_URL} ${SDK_REPO_LOCAL_PATH}
git clone --quiet -b ${SDK_BRANCH} ${SDK_REPO_URL} ${SDK_REPO_LOCAL_PATH}

# Install SDK project tools
cd ${ROOT_DIR}
Expand Down
14 changes: 14 additions & 0 deletions templates/python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Python templates

This folder contains only our customized python templates. Beside these customized templates,
the original templates of openapi-generator for python are used. These can be found in the
official GitHub repo of the [openapi-generator](https://github.com/OpenAPITools/openapi-generator/tree/v7.14.0/modules/openapi-generator/src/main/resources/python).

If you need to change something in the Python Generator, try always first to add
[user-defined templates](https://openapi-generator.tech/docs/customization#user-defined-templates),
instead of overwriting existing templates. These ensure an easier upgrade process, to newer
versions of the openapi-generator.

If it's required to customize the original templates, you can copy them into this directory.
Try to minimize the customization as much as possible, to ensure, that we can easily upgrade
to newer versions in the future.
44 changes: 0 additions & 44 deletions templates/python/README_onlypackage.mustache

This file was deleted.

Empty file.
5 changes: 0 additions & 5 deletions templates/python/__init__api.mustache

This file was deleted.

11 changes: 0 additions & 11 deletions templates/python/__init__model.mustache

This file was deleted.

41 changes: 29 additions & 12 deletions templates/python/__init__package.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,44 @@

__version__ = "{{packageVersion}}"

# Define package exports
__all__ = [
{{#apiInfo}}{{#apis}}"{{classname}}",
{{/apis}}{{/apiInfo}}"ApiResponse",
"ApiClient",
"HostConfiguration",
"OpenApiException",
"ApiTypeError",
"ApiValueError",
"ApiKeyError",
"ApiAttributeError",
"ApiException",
{{#hasHttpSignatureMethods}}"HttpSigningConfiguration",
{{/hasHttpSignatureMethods}}{{#models}}{{#model}}"{{classname}}"{{^-last}},
{{/-last}}{{#-last}},{{/-last}}{{/model}}{{/models}}
]

# import apis into sdk package
{{#apiInfo}}{{#apis}}from {{apiPackage}}.{{classFilename}} import {{classname}}
{{#apiInfo}}{{#apis}}from {{apiPackage}}.{{classFilename}} import {{classname}} as {{classname}}
{{/apis}}{{/apiInfo}}
# import ApiClient
from {{packageName}}.api_response import ApiResponse
from {{packageName}}.api_client import ApiClient
from {{packageName}}.configuration import HostConfiguration
from {{packageName}}.exceptions import OpenApiException
from {{packageName}}.exceptions import ApiTypeError
from {{packageName}}.exceptions import ApiValueError
from {{packageName}}.exceptions import ApiKeyError
from {{packageName}}.exceptions import ApiAttributeError
from {{packageName}}.exceptions import ApiException
from {{packageName}}.api_response import ApiResponse as ApiResponse
from {{packageName}}.api_client import ApiClient as ApiClient
from {{packageName}}.configuration import HostConfiguration as HostConfiguration
from {{packageName}}.exceptions import OpenApiException as OpenApiException
from {{packageName}}.exceptions import ApiTypeError as ApiTypeError
from {{packageName}}.exceptions import ApiValueError as ApiValueError
from {{packageName}}.exceptions import ApiKeyError as ApiKeyError
from {{packageName}}.exceptions import ApiAttributeError as ApiAttributeError
from {{packageName}}.exceptions import ApiException as ApiException
{{#hasHttpSignatureMethods}}
from {{packageName}}.signing import HttpSigningConfiguration
from {{packageName}}.signing import HttpSigningConfiguration as HttpSigningConfiguration
{{/hasHttpSignatureMethods}}

# import models into sdk package
{{#models}}
{{#model}}
from {{modelPackage}}.{{classFilename}} import {{classname}}
from {{modelPackage}}.{{classFilename}} import {{classname}} as {{classname}}
{{/model}}
{{/models}}
{{#recursionLimit}}
Expand Down
7 changes: 6 additions & 1 deletion templates/python/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ class {{classname}}:
_query_params: List[Tuple[str, str]] = []
_header_params: Dict[str, Optional[str]] = _headers or {}
_form_params: List[Tuple[str, str]] = []
_files: Dict[str, Union[str, bytes]] = {}
_files: Dict[
str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]]
] = {}
_body_params: Optional[bytes] = None

# process the path parameters
Expand Down Expand Up @@ -167,6 +169,9 @@ class {{classname}}:
if isinstance({{paramName}}, str):
with open({{paramName}}, "rb") as _fp:
_body_params = _fp.read()
elif isinstance({{paramName}}, tuple):
# drop the filename from the tuple
_body_params = {{paramName}}[1]
else:
_body_params = {{paramName}}
{{/isBinary}}
Expand Down
22 changes: 17 additions & 5 deletions templates/python/api_client.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ from {{packageName}}.exceptions import (

RequestSerialized = Tuple[str, str, Dict[str, str], Optional[str], List[str]]


class ApiClient:
"""Generic API client for OpenAPI client library builds.

Expand Down Expand Up @@ -372,6 +371,10 @@ class ApiClient:
else:
obj_dict = obj.__dict__

if isinstance(obj_dict, list):
# here we handle instances that can either be a list or something else, and only became a real list by calling to_dict()
return self.sanitize_for_serialization(obj_dict)

return {
key: self.sanitize_for_serialization(val)
for key, val in obj_dict.items()
Expand All @@ -394,12 +397,12 @@ class ApiClient:
data = json.loads(response_text)
except ValueError:
data = response_text
elif content_type.startswith("application/json"):
elif re.match(r'^application/(json|[\w!#$&.+-^_]+\+json)\s*(;|$)', content_type, re.IGNORECASE):
if response_text == "":
data = ""
else:
data = json.loads(response_text)
elif content_type.startswith("text/plain"):
elif re.match(r'^text\/[a-z.+-]+\s*(;|$)', content_type, re.IGNORECASE):
data = response_text
else:
raise ApiException(
Expand Down Expand Up @@ -507,7 +510,7 @@ class ApiClient:
if k in collection_formats:
collection_format = collection_formats[k]
if collection_format == 'multi':
new_params.extend((k, str(value)) for value in v)
new_params.extend((k, quote(str(value))) for value in v)
else:
if collection_format == 'ssv':
delimiter = ' '
Expand All @@ -525,7 +528,10 @@ class ApiClient:

return "&".join(["=".join(map(str, item)) for item in new_params])

def files_parameters(self, files: Dict[str, Union[str, bytes]]):
def files_parameters(
self,
files: Dict[str, Union[str, bytes, List[str], List[bytes], Tuple[str, bytes]]],
):
"""Builds form parameters.

:param files: File parameters.
Expand All @@ -540,6 +546,12 @@ class ApiClient:
elif isinstance(v, bytes):
filename = k
filedata = v
elif isinstance(v, tuple):
filename, filedata = v
elif isinstance(v, list):
for file_param in v:
params.extend(self.files_parameters({k: file_param}))
continue
else:
raise ValueError("Unsupported file value")
mimetype = (
Expand Down
76 changes: 0 additions & 76 deletions templates/python/api_doc.mustache

This file was deleted.

Loading
Loading