-
Notifications
You must be signed in to change notification settings - Fork 26
Description
Problem Statement
Hi Team,
I've noticed that the Zscaler Python SDK returns a tuple (data, resp, err) for every API call. For example, if I want to fetch all users, I currently have to do something like this:
users, resp, err = client.zia.user_management.list_users()
if err:
handle
return usersThis feels quite awkward for Python because typically we'd expect the SDK to raise exceptions when something goes wrong, and return only the actual data when the call succeeds. I'm guessing this pattern carried over from Go, where returning (val, err) tuples is normal practice.
Digging into the SDK code, every endpoint repeats a very similar block of logic, catching exceptions and returning them quietly in the tuple. Here's a common example:
try:
# parse response
except Exception as error:
return (None, response, error)The issue with this approach is that exceptions can slip by unnoticed unless developers manually check and handle the err value every single time. This makes debugging harder, since tracebacks and real exceptions aren't surfaced naturally.
Proposed Solution
It seems that we could greatly simplify things by extracting this repetitive logic into a shared helper method. That helper could handle request building, execution, response parsing, and importantly, raise exceptions when errors occur.
Then each endpoint method would become much simpler, e.g.:
def get_user(self, user_id: int) -> UserManagement:
return self._call_api(
method="GET",
endpoint=f"/users/{user_id}",
response_type=UserManagement
)Of course if there's some extra handling required then that could still be handled on a per-endpoint basis directly in the method or via a decorator.
Proposed Solution
Currently the implementation for each API endpoint has duplicate code doing the same thing. The repetitive try/catch/return block code can be hoisted to a helper method, simplifying endpoint implementation.
With this approach, exceptions would naturally propagate, eliminating the need for developers to explicitly check errors every single time. It'd also mean significantly less duplicated code across the SDK, making it easier to maintain especially when things like pagination changes or other centralised functionality needs to be added.
This would align the SDK's error-handling behaviour with other Python libraries where exceptions are raised on errors by default, rather than returning tuples containing error details.
So in summary:
- With the logic centralised in a helper function, maintenance on the SDK is much easier as there is a single place to maintain API call logic. If Zscaler change the way they handle pagination or if we want to do something for all API calls then we don't need to edit hundreds of endpoint methods.
- All errors become exceptions and developers no longer need to remember to handle
data, resp, err - Developers will see real Python exceptions immediately in the stack trace
- Returning the expected data (obj or list) and raising on error will be familiar to all devs and anyone new to the SDK won't be surprised or have to learn tuple unpacking just to get data.
- We can remove 1000s of lines of redundant code from the SDK
Additional Context
Check some major and modern Python SDKs like requests, boto3 etc. These libraries will raise exceptions rather than return error objects.
Thanks for considering, happy to discuss further and help out with implementation if this is a direction you'd like to go.