Things that still need to be done, or at least might be useful if done.
I instrumented the code to determine how much time is taken by all the dynamic construction for the APIInfo, _getAPI, _makeAPIContext, etc stuff. On my mac that contributes about 189 microseconds (i.e., less than 1 millisecond) of overhead. It's just not a factor versus the round-trip times to the NumerousApp server.
If you time the end-to-end calls you will be up in the 280 millisecond
range with the bulk of this being actual on-the-wire time (i.e.,
beyond our control). The requests
library conveniently
puts the on-the-wire elapsed time into the response object which the
Numerous class makes available via the statistics
(see
below). Thus the maximum theoretical API rate is about 3 to 4 requests
per second back and forth to the Numerous server from a single
thread. You can definitely get more with multiple
threads/processes. Your mileage may vary of course depending on
network conditions.
By default the round-trip time (in floating point seconds) of the most
recent API request is available in nr.statistics['serverResponseTimes']
.
If you force that element to be an array then the code will keep the most recent N round-trip times (as determined by the size of the array). So, for example:
nr.statistics['serverResponseTimes'] = [0]*10
If you hand instrument the code and perform more experiments you will
find there is 4-6 milliseconds of extra overhead buried somewhere in
the requests
module itself. I haven't been able to figure out what
that is; I'm guessing it has to do with sockets being closed when
objects go out of scope.
None of this is remotely significant in the face of 280msec on-the-wire time.
By design the Numerous and NumerousMetric classes bubble up most errors as Exceptions and do not hide low-level errors some of which are a little silly.
For example, deleting a metric photo when the metric has no photo causes a NumerousError exception, code 404. Arguably that error should be ignored. Along the same lines, deleting an interaction that doesn't exist causes a NumerousError exception with code 200 (OK). This is because the server returns code 204 (No Response) when an interaction is successfully deleted (so this is used as the "expected good response" code) and returns code 200 ("OK") when the specified interaction doesn't exist. This is understandable from a semantic invariant point of view - the interaction doesn't exist in either case after the call, so "OK" is a perfectly reasonable response.
However it's worth noting that the server is quite inconsistent on this (e.g., 404 for deleting a metric photo that doesn't exist vs 200 for deleting an event that doesn't exist).
It seemed best to allow all these exceptions and raw idiosyncrasies to bubble up in case you needed to act on them for some reason. However, in the cases where you don't really want to know all this detail it can be a pain. And arguably some of it might be server-implementation-specific and hiding some of those details might be a Good Thing.
So I think there should be two subclasses, NumerousWrap and NumerousMetricWrap, that would do things like this:
class NumerousWrap(Numerous):
def ping(self):
try:
Numerous.ping(self)
except NumerousAuthError:
return False
return True
and so forth, overriding (wrapping) just those methods that raise exceptions and handling the "we don't really care" cases accordingly.