Various and sundry.¶
Safe mode.¶
If you set the GRIST_SAFEMODE
configuration key to Y
, all API call
functions attempting a write operation will be blocked: Pygrister will throw
a GristApiInSafeMode
exception instead.
Please note that the two run_sql*
functions are still allowed in safe mode,
because the underlying API only accepts SELECT
statements anyway.
Troubleshooting the API call.¶
It goes without saying, Pygrister relies on the excellent Python Requests library to do the actual talking with the Grist API. Every time a request is sent, and a response is retrieved, Pygrister saves the relevant details of both for subsequent inspection.
You may check the req_url
, req_body
, req_headers
, req_method
,
resp_content
, resp_code
, resp_reason
, resp_headers
, attributes
right after each API call to make sure Pygrister has handled the request
correctly.
The convenience function inspect
will output all the saved data at once.
You may call it if anything goes wrong: in fact, request/response data are
collected even if an Http error occurred (see below).
Please be aware: the “inspect” attributes req_*
and resp_*
will store the status of the last api call that was successfully responded to
by the server. If the call times out, for example, there will be no response
object to store and inspect in the first place.
In other words, if you receive a Http404 (or any other “bad” status code,
resulting in a HTTPError from Requests - again, see below for details), then
the details of the response will be retrieved and stored all the same.
However, if the call fails before a response is even retrieved, Pygrister
will simply bail out propagating the appropriate
Requests exception.
If this happen, the “inspect” attributes will still reflect the status of the
next-to-last call, which can be confusing. In other words, do not call
inspect
after any Requests exception that is not a HTTPError.
Please note only the first 5000 characters of a text/json response will be
stored: this should be plenty for inspection purposes, but if you really
need to save more, you may raise the value of the MAXSAVEDRESP
constant.
Finally, binary response bodies (eg, when you download a file) will not be
saved by default, but if you really need those too, you may set the
SAVEBINARYRESP
flag. The binary string will then be stored, up to the
MAXSAVEDRESP
value.
Errors vs Status codes.¶
When you call an API and the Http status code of the response is “bad” (that is, 300 or more), the underlying Requests library may optionally throw and exception - and Pygrister offers you the same choice.
If you set the GRIST_RAISE_ERROR
config key to Y
(the default), then
Pygrister will raise a requests.HTTPError
if the response is not ok.
Otherwise, Pygrister will simply return the bad response as if nothing.
Keep in mind that both status code and response body will always be retrieved:
upon success, they will be returned by the API call function as usual; if
an HTTPError occurred and you choose to raise it, you can still access the
retrieved data via the resp_code
and resp_content
attributes afterwards.
Since the response is delivered in any case, it is really just a matter of taste.
If you prefer “to ask for forgiveness”, set the config key to Y
(the default)
and prepare for the possible exception:
from requests import HTTPError
grist = GristApi()
try:
st_code, res = grist.list_records('mytable')
except HTTPError:
# return values are missing but data have been retrieved anyway
print(grist.resp_code, grist.resp_content)
If you prefer to “look before you leap” instead, set the config key to N
and check the resulting status code:
grist.reconfig({'GRIST_RAISE_ERROR': 'N'})
st_code, res = grist.list_records('mytable')
if st_code >= 300:
# now the function is allowed to return even if an error occurred
print(st_code, res)
Finally note that, if an HTTPError occurred, the retrieved response will of course not conform to the “regular” Pygrister format for a successful request:
>>> grist.reconfig({'GRIST_RAISE_ERROR': 'N'})
>>> grist.list_records('bogus_table') # usually, response here is a list
(404, {'error': 'Table not found "bogus_table"'})
>>> grist.add_records('Table1', ['bogus_records']) # usually, resp. is None
(400, {'error': 'Invalid payload', 'details':
{'userError': 'Error: body.records[0] is not a NewRecord; (...)}})
In such cases, Pygrister will always return the original Grist API response without modification.
The ok
attribute.¶
As a shortcut, the GristApi.ok
attribute is set to True
if the last
request was successful (status code < 300), to False
if not.
This is useful especially if you choose not to raise errors: instead of
inspecting the status code, you may then write something like
grist.reconfig({'GRIST_RAISE_ERROR': 'N'})
st_code, res = grist.list_records('mytable')
if grist.ok: # equivalent to "if st_code < 300"
...
Additional arguments for the request.¶
You may pass optional arguments, not otherwise used by Pygrister, to the underlying
Requests call.
Simply pass a request_options
argument to the GristApi
constructor,
grist = GristApi(request_options={'timeout': 5})
or modify the property at runtime:
grist.request_options = {'timeout': 5}
The request_options
will then be injected into all subsequent Pygrister API
calls. The code above, for example, will set a timeout limit from now on.
Using Requests sessions in Pygrister.¶
Requests supports using sessions to persist connection data, and so does Pygrister.
Working with sessions is straightforward:
>>> grist = GristApi({...})
>>> grist.open_session() # open a new session
>>> grist.session # this is how you know you are in a session
<requests.sessions.Session object at ...>
>>> # ...Pygrister api calls are now "inside" the session...
>>> grist.close_session() # close the session
>>> grist.session # "session" attribute is now None
>>>
As long as you are in a session, all subsequent api calls will re-use the same
underlying connection, resulting in much faster interaction. From the
second api call on, if you inspect the request headers (grist.req_headers
),
you will notice a new 'Cookie'
element added by Requests to persist the
connection.
In Pygrister, session have no other use than for boosting performance, and they
are transparent to the rest of the api. Inside a session, you will use the
GristApi
class just the same: start a session, and then forget about it.
You may use sessions for performance, when you need to make several api calls in a row. However, keep in mind that Requests (and Pygrister) sessions are supplied “as it is” - your server may be configured to expire a session after a while, for instance.