From cc53069ae11fc9499cdb1cbc416f6cba2bd7d4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Dzivjak?= Date: Tue, 20 Jan 2026 16:37:12 +0100 Subject: [PATCH] feat: report runtime properties Propagate more information about the SDK's runtime including the API version, package version, etc. to get more observability into the SDKs usage. Also removes the generation of the client/client.go file as it doesn't depend on the OpenAPI specs and can be maintained manually. --- codegen/pkg/builder/builder.go | 4 +++ codegen/pkg/builder/out.go | 18 ++++++++++ codegen/templates/api_version.py.tmpl | 2 ++ codegen/templates/client.py.tmpl | 4 ++- sumup/_api_version.py | 2 ++ sumup/_client.py | 4 ++- sumup/_service.py | 47 +++++++++++++++++++++++++++ 7 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 codegen/templates/api_version.py.tmpl create mode 100644 sumup/_api_version.py diff --git a/codegen/pkg/builder/builder.go b/codegen/pkg/builder/builder.go index 22eb40d3..6d32534a 100644 --- a/codegen/pkg/builder/builder.go +++ b/codegen/pkg/builder/builder.go @@ -100,6 +100,10 @@ func (b *Builder) Build() error { return err } + if err := b.writeAPIVersionFile(path.Join(b.cfg.Out, "_api_version.py")); err != nil { + return err + } + took := time.Since(b.start) slog.Info("sdk generated", slog.Duration("took", took)) diff --git a/codegen/pkg/builder/out.go b/codegen/pkg/builder/out.go index 03a646de..25f117ae 100644 --- a/codegen/pkg/builder/out.go +++ b/codegen/pkg/builder/out.go @@ -272,6 +272,24 @@ func (b *Builder) writeClientFile(fname string, tags []string) error { return nil } +func (b *Builder) writeAPIVersionFile(fname string) error { + f, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.FileMode(0o755)) + if err != nil { + return fmt.Errorf("create %q: %w", fname, err) + } + defer func() { + _ = f.Close() + }() + + if err := b.templates.ExecuteTemplate(f, "api_version.py.tmpl", map[string]any{ + "Version": b.spec.Info.Version, + }); err != nil { + return fmt.Errorf("generate api version: %w", err) + } + + return nil +} + func openGeneratedFile(filename string) (*os.File, error) { cwd, err := os.Getwd() if err != nil { diff --git a/codegen/templates/api_version.py.tmpl b/codegen/templates/api_version.py.tmpl new file mode 100644 index 00000000..70c2173f --- /dev/null +++ b/codegen/templates/api_version.py.tmpl @@ -0,0 +1,2 @@ +# Code generated by `py-sdk-gen`. DO NOT EDIT. +__api_version__ = "{{ .Version }}" diff --git a/codegen/templates/client.py.tmpl b/codegen/templates/client.py.tmpl index 6c7be575..0ee67020 100644 --- a/codegen/templates/client.py.tmpl +++ b/codegen/templates/client.py.tmpl @@ -3,7 +3,7 @@ import os import httpx import typing -from ._service import Resource, AsyncResource +from ._service import Resource, AsyncResource, runtime_headers {{- range .Resources }} from .{{ .Package }} import {{ .Name }}Resource, Async{{ .Name }}Resource {{- end }} @@ -25,6 +25,7 @@ class Sumup(Resource): headers={ "User-Agent": f"sumup-py/{self.version()}", "Authorization": f"Bearer {self.api_key}", + **runtime_headers(), }, )) @@ -51,6 +52,7 @@ class AsyncSumup(AsyncResource): headers={ "User-Agent": f"sumup-py/{self.version()}", "Authorization": f"Bearer {self.api_key}", + **runtime_headers(), }, )) diff --git a/sumup/_api_version.py b/sumup/_api_version.py new file mode 100644 index 00000000..9d20483c --- /dev/null +++ b/sumup/_api_version.py @@ -0,0 +1,2 @@ +# Code generated by `py-sdk-gen`. DO NOT EDIT. +__api_version__ = "1.0.0" diff --git a/sumup/_client.py b/sumup/_client.py index 821f6697..fa27d33d 100644 --- a/sumup/_client.py +++ b/sumup/_client.py @@ -3,7 +3,7 @@ import httpx import typing -from ._service import Resource, AsyncResource +from ._service import Resource, AsyncResource, runtime_headers from .checkouts import CheckoutsResource, AsyncCheckoutsResource from .customers import CustomersResource, AsyncCustomersResource from .members import MembersResource, AsyncMembersResource @@ -35,6 +35,7 @@ def __init__( headers={ "User-Agent": f"sumup-py/{self.version()}", "Authorization": f"Bearer {self.api_key}", + **runtime_headers(), }, ) ) @@ -127,6 +128,7 @@ def __init__( headers={ "User-Agent": f"sumup-py/{self.version()}", "Authorization": f"Bearer {self.api_key}", + **runtime_headers(), }, ) ) diff --git a/sumup/_service.py b/sumup/_service.py index 7e6003af..126a7a94 100644 --- a/sumup/_service.py +++ b/sumup/_service.py @@ -1,4 +1,9 @@ import httpx +import platform +import sys +from functools import lru_cache + +from ._api_version import __api_version__ from ._version import __version__ HeaderTypes = dict[str, str] @@ -10,6 +15,48 @@ def version(): return f"v{__version__}" +def runtime_headers() -> dict[str, str]: + return dict(_runtime_headers()) + + +@lru_cache(maxsize=1) +def _runtime_headers() -> tuple[tuple[str, str], ...]: + arch_raw = platform.machine() + arch = arch_raw.lower() if arch_raw else "" + arch_map = { + "x86_64": "x86_64", + "x64": "x86_64", + "amd64": "x86_64", + "x86": "x86", + "i386": "x86", + "i686": "x86", + "ia32": "x86", + "x32": "x86", + "aarch64": "arm64", + "arm64": "arm64", + "arm": "arm", + } + arch = arch_map.get(arch, "unknown") + + os_name = sys.platform + os_map = { + "win32": "windows", + "linux": "linux", + "darwin": "darwin", + } + os_name = os_map.get(os_name, os_name) + + return ( + ("X-Sumup-Api-Version", __api_version__), + ("X-Sumup-Lang", "python"), + ("X-Sumup-Package-Version", __version__), + ("X-Sumup-Os", os_name), + ("X-Sumup-Arch", arch), + ("X-Sumup-Runtime", "python"), + ("X-Sumup-Runtime-Version", platform.python_version()), + ) + + class Resource(BaseResource): _client: httpx.Client