Package: basic-api
Basic API ClientBasicAPIis a Python API client that knows nothing about any specific APIs.PurposeI've used a lot of complicated python clients that are of course useuful,
but my favorite clients are very basic.  They'reso not-smartthat the question
"Is this a problem with the API, or a problem with the python client?"
should be more on the API than the client.So,BasicAPIis very slim.  After a bit of understanding of how it works,
generally you should read the API docs instead of client docs.It's intended to be extended by subclasses which may have varying degrees
of knowledge of their target API, including auth and/or convenience methods.ExampleAn example subclass can be found inexamples/basic_github.pywhich is used for cutting releases ofbasic-apiinrelease.py.Usagepip install requests basic-apifrombasic_apiimportBasicAPIapi=BasicAPI('https://api.example.com')All of the below are equivalent.
They make a GET request tohttps://api.example.com/cool/pathapi.get('/cool/path')api.get.cool.path()api.get['cool']['path']()api.get['cool/path']()api['get'].cool['path']()The[item]syntax is useful for variables, and paths that include.s.Example POST request:api.post('/cool/path',json={'some':'data'})This also works the same as the above, by happenstance:api.postapi.cool['path']api(json={'some':'data'})SeeHeads Up -> Thread Safetybelow.Request adapterThe default adapter isrequests.
It is not a hard requirement for folks who wish to keep requirements to the bare minimum,
but there is no fallback adapter, so either installrequestsorbasic-api[adapter](which includes requests) or pass in some specific adapter.All keyword arguments aside frombase_urlandadapterwill be passed into the adapter call.For example, you may wish to include the same header on all API calls:api=BasicAPI('https://api.example.com',headers={'User-Agent':'fancy'})Overlapping kwargsIf you include the same keyword in subsequent calls,BasicAPIwill do a not-so-basic thing and attempt to merge them together,
preferring the value(s) in the API call, so withapi=BasicAPI('https://api.example.com',headers={'User-Agent':'fancy'})api.get('/cool/path',headers={'User-Agent':'super fancy'})thegetcall will override the previousUser-Agentheader,
resulting in a "super fancy" user agent, andapi.get('/cool/path',headers={'another':'header'})will result in both the original "fancy" user agentandanotherheader.This behavior is sorta-tested; it's the only part of this thing I'm worried about.
It's probably fine, but no promises.  Your own integration tests
andlogging.basicConfig(level=logging.DEBUG)are your friends.Please tell me how wrong I am about my recursivemergefunction.SessionsFor APIs (or API-like things) that support it, you may pass in arequests.Session()as theadapter, since it has (mostly) the same interface asrequestsitself.sesh=requests.Session()sesh.headers={'User-Agent':'my fancy app'}api=BasicAPI('https://api.example.com',adapter=sesh)# api.post.something.something.cookies()api.post('/cool/path')AdvancedTheadaptercan be any object with callable attributes.If you are advanced, you can probably figure out how to do exceptional stuff with this basic thing.Heads UpThread SafetyBasicAPI isnotthread safe (it is quite basic, after all).Instantiate one per thread if you are multithreading.
They don't cost much.