fix ruff on main; add typed XenAPIError with the structured error - #1
Merged
Conversation
added 3 commits
August 17, 2026 10:14
The ruff job has been red since xscert.py landed: examples/xscert.py:29:1 I001 Import block is un-sorted or un-formatted examples/xscert.py:262:13 B007 Loop control variable `ref` not used Sort the imports, and iterate .values() since the ref is unused.
_call and login_with_password formatted the JSON-RPC error into a string and
threw the object away, so callers had to substring-match the message to tell an
RBAC denial from a bad reference:
except RuntimeError as e:
if 'RBAC_PERMISSION_DENIED' in str(e): ...
XenAPIError keeps it:
except XenAPIError as e:
if e.code == 'RBAC_PERMISSION_DENIED': ...
.code XAPI's error name, which JSON-RPC carries in `message`
.params XAPI's error parameters (`data`)
.error the raw error object
.method the failing call
It subclasses RuntimeError, so existing `except RuntimeError` keeps working,
and a non-dict error degrades to code=None rather than raising.
Bump to 1.0.6 so this and the 3.9 annotation fix can reach PyPI: the published
1.0.5 still declares requires-python >=3.12 and cannot be imported on 3.12/3.13.
Every client-certificate consumer repeats the same four lines, and gets the
ordering trap wrong at least once (check_hostname must be cleared before
verify_mode, or CPython raises):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
ctx.load_cert_chain(cert, key)
Now:
ctx = client_cert_context('client.crt', 'client.key')
session = AsyncXenAPISession(url, ssl_context=ctx)
It also makes server verification a first-class option rather than an
afterthought: pass cafile= to verify the pool (CERT_REQUIRED + check_hostname),
omit it for a lab pool with a self-signed certificate. The docstring says
plainly that omitting it leaves the channel encrypted but unauthenticated.
Verified against a real XS9 pool: without cafile the certificate login
succeeds (client_certificate=True, subject=OpaqueRef:NULL); with cafile the
handshake fails as it should, since that CA does not sign the pool's server
certificate.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two independent fixes, smallest first.
1.
fix(ci)— the ruff job is red onmainSince
examples/xscert.pylanded,ruff@0.16.2 check .fails:Imports sorted; the loop now iterates
.values()since the key is unused.Only the
ruffjob was failing — build and all six import jobs were green.2.
feat(python)— keep the structured error_callandlogin_with_passwordrendered the JSON-RPC error into a string anddiscarded the object, so a caller had to substring-match to classify a failure:
XenAPIErrorpreserves it:.codemessage).paramsdata).error.methodIt subclasses
RuntimeError, so existingexcept RuntimeErrorkeeps working,and a non-dict error degrades to
code=Noneinstead of raising.Version bumped to 1.0.6 so this and the earlier 3.9 annotation fix can reach
PyPI — the published 1.0.5 still declares
requires-python >=3.12yet cannot beimported on 3.12/3.13.
Verification
Ran the three CI jobs locally against this branch before pushing:
uvx ruff@0.16.2 check .— All checks passeduv venv --python 3.9 && uv pip install . && import— OK (also checked 3.14)uv build—async_xenapi-1.0.6.tar.gz+.whlMotivation
Found while building a client-certificate auth demo against a real XS9 pool. The
demo currently subclasses the session to inject an SSL context and matches error
strings; with
ssl_context=,session_refand nowXenAPIError, all threeworkarounds go away.