Critique and Refactoring of Volcengine Python SDK Request Signing
This article critiques the Volcengine Python SDK's convoluted request‑signing design, demonstrates the problems with its custom classes and static methods, and proposes cleaner solutions using the requests library's AuthBase and Session mechanisms to simplify signing and improve maintainability.
The author introduces Tetos, a Python library that unifies cloud TTS service interfaces.
While reviewing the Volcengine SDK, they notice inconsistent API designs and a convoluted request‑signing implementation that wraps the requests library in custom classes.
They pose the design question: how to expose a clean SDK for users when the underlying service requires a complex signature?
They show the original problematic code (class SAMIService and its common_json_handler ), pointing out unnecessary singletons, static methods, and double JSON encoding/decoding.
<code>class SAMIService(Service):
_instance_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
if not hasattr(SAMIService, "_instance"):
with SAMIService._instance_lock:
if not hasattr(SAMIService, "_instance"):
SAMIService._instance = object.__new__(cls)
return SAMIService._instance
def __init__(self):
self.service_info = SAMIService.get_service_info()
self.api_info = SAMIService.get_api_info()
super(SAMIService, self).__init__(self.service_info, self.api_info)
@staticmethod
def get_service_info():
api_url = 'open.volcengineapi.com'
service_info = ServiceInfo(api_url, {}, Credentials('', '', 'sami', 'cn-north-1'), 10, 10)
return service_info
@staticmethod
def get_api_info():
api_info = {"GetToken": ApiInfo("POST", "/", {"Action": "GetToken", "Version": "2021-07-27"}, {}, {})}
return api_info
def common_json_handler(self, api, body):
params = dict()
try:
body = json.dumps(body)
res = self.json(api, params, body)
res_json = json.loads(res)
return res_json
except Exception as e:
try:
res_json = json.loads(str(e))
return res_json
except:
raise Exception(str(e))
if __name__ == '__main__':
sami_service = SAMIService()
sami_service.set_ak(ACCESS_KEY)
sami_service.set_sk(SECRET_KEY)
req = {"appkey": APPKEY, "token_version": AUTH_VERSION, "expiration": 3600}
resp = sami_service.common_json_handler("GetToken", req)
try:
print("response task_id=%s status_code=%d status_text=%s expires_at=%s\n\t token=%s" % (
resp["task_id"], resp["status_code"], resp["status_text"], resp["expires_at"], resp["token"]))
except:
print("get token failed, ", resp)</code>They propose simplifying the flow by using a plain function, by directly calling requests.post , and by moving the signature step into a requests.auth.AuthBase implementation.
<code>def common_json_handler(service, api, body):
params = dict()
try:
body = json.dumps(body)
res = service.json(api, params, body)
res_json = json.loads(res)
return res_json
except Exception as e:
# ...
...
</code>A custom authentication class can be written as:
<code>class VolcAuth(AuthBase):
def __init__(self, service_info, credentials):
self.service_info = service_info
self.credentials = credentials
def __call__(self, r):
# new_sign implementation (omitted)
new_sign(r, self.service_info, self.credentials)
return r
auth = VolcAuth(service_info, credentials)
res = requests.post(url, json=body, auth=auth)
</code>Alternatively, a session subclass can inject the signature in send :
<code>class VolcSession(requests.Session):
def send(self, request, **kwargs):
new_sign(request, service_info, credentials)
return super().send(request, **kwargs)
</code>The author also mentions a reference implementation for httpx and shows that the refactored version reduces the code from hundreds of lines to about sixty, demonstrating the benefit of understanding library internals.
In conclusion, the SDK appears to be a direct translation from another language, and the author suggests using the standard extension points of the requests library to keep the interface minimal and maintainable.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.