The API call engine.

The code responsible for posting the API call to the Grist server lives in a separate pygrister.apicaller.ApiCaller class. The main GristApi class will load a default instance of ApiCaller at instantiation time. You may write your custom API call engine and pass it to GristApi.__init__, as the optional argument custom_apicaller: of course, this is not needed in normal usage.

Once the GristApi instance is ready, its internal API caller will be accessible via the GristApi.apicaller attribute. Accessing the API caller directly may help you with debugging.

(Why should you want to write a custom Api caller? For instance, our Gry command line tool makes use of a custom caller to enforce a different pattern of error handling when calling the APIs: since Gry is meant for the final user, we wanted to catch any possible connection error and present a nice message instead of the crude stacktrace that a normal GristApi instance would emit before crashing out. You may read the source code in gry.py to see a custom Api caller at work.)

API callers and configurators.

An ApiCaller class will need its own configurator: you may write a custom class, or simply use the default one:

>>> from pygrister.config import Configurator
>>> from pygrister.apicaller import ApiCaller
>>> from pygrister.api import GristApi
>>> class MyApiCaller(ApiCaller):
...    pass  # insert here your custom logic for api calling

>>> a = MyApiCaller()  # a custom api caller using the default configurator
>>> grist = GristApi(custom_apicaller=a)

Then, the GristApi class will also use the configurator provided with the api caller:

>>> grist.configurator is grist.apicaller.configurator
True

To ensure that both GristApi and its api caller will use the same configurator, you are prevented to pass both a custom_configurator and a custom_apicaller to the GristApi constructor:

>>> GristApi(custom_configurator=c, custom_apicaller=a) # throws an exception

Once GristApi is instantiated, it is possible to change the configurator (and/or the api caller) at runtime… in theory, but this is not really supported. You must always remember to ensure that GristApi and its own internal api caller will use the same configurator object, at any time. For instance,

>>> class MyConfigurator(Configurator): pass  # a custom configurator

>>> conf = MyConfigurator()
>>> grist.configurator = conf # change the configurator at runtime
>>> grist.apicaller.configurator = conf # change the apicaller's one too!

This is possible, if convoluted. If you really must swap configurator and/or api caller at runtime, it’s easier to create two separate instances of GristApi, with their own different internals, then swap between them instead.