8
\$\begingroup\$

I am currently trying to create an API-wrapper. For that, I connect to the endpoint (for example https://host.example.com/api/accounts/open) with the request module. Since there are a bunch of endpoints, I need a way to sort them. Currently I have a constants file, where most of the endpoints are created like this:

HOST = 'https://wallet.shiftnrg.org' START = ''.join([HOST, '/api']) ACCOUNTS = ''.join([START, '/accounts']) ACCOUNTS_OPEN = ''.join([ACCOUNTS, '/open']) ... LOADER = ''.join([START, '/loader']) LOADER_GET_STATUS = ''.join([LOADER, '/status']) 

And I access them like this (one example):

def accounts_get_balance(address): payload = {'address': address} response = requests.get(constants.ACCOUNTS_GET_BALANCE, params=payload, timeout=constants.TIMEOUT ) 

While this is working, I was wondering if there is a more efficient, pythonic way of doing this, since I am just a beginner (especially the constants.py, but if you find anything else, feel free to tell me as well!)

\$\endgroup\$

    2 Answers 2

    9
    \$\begingroup\$

    A tip on the second part, is looking to what Brendan Rhodes has to say about Clean Architecture and bring your IO out as much as possible. This will make testing everything a lot easier, and limit the number of God-methods that do everything from constructing the query, getting the response to parsing and persisting the result

    For the first part, I would work with a dict of destinations, that you can persist via json or so

    settings = { 'host': 'https://wallet.shiftnrg.org', 'entry-points':{ 'start': ('api',), 'accounts': ('_start', 'accounts'), 'accounts_open': ('_accounts', 'open'), 'loader': ('_start', 'loader') } } 

    that you can dump to and load from json

    For the entry-points, I would parse this somewhat like this:

    def parse_entry_points(entry_points): for name, path in entry_points.items(): yield name, list(expand_path(entry_points, name)) def expand_path(entry_points, item): path = entry_points[item] for item in path: if item[0] != '_': yield item else: yield from expand_path(entry_points, item[1:]) ENTRY_POINTS = dict(parse_entry_points(entry_points=settings['entry-points'])) 

    Off course you can/should replace the _-placeholder with a unicode symbol that will not figure in the eventual URLs

    {'accounts': ['api', 'accounts'], 'accounts_open': ['api', 'accounts', 'open'], 'loader': ['api', 'loader'], 'start': ['api']} 

    assembling the URL can then be as simple as

    from urllib.parse import urljoin def assemble_url(enty_point): parts = constants.ENTRY_POINTS[entry_point] return urljoin(constants.HOST, parts) 
    \$\endgroup\$
      6
      \$\begingroup\$

      If the API routes are consistent, I'd suggest making use of the __getattr__ metamethod.

      A simple example would be

      class APIRoutes: _BASE = "https://wallet.shiftnrg.org/" _BASE_API = _BASE + "api/" def __getattr__(self, route): return self._BASE_API + route.lower().replace('_', '/') 

      Now, in your code:

      api_routes = APIRoutes() . . ... def accounts_get_balance(address): payload = {'address': address} response = requests.get(api_routes.ACCOUNTS_GET_BALANCE, params=payload, timeout=constants.TIMEOUT ) 

      where api_routes.ACCOUNTS_GET_BALANCE will generate

      https://wallet.shiftnrg.org/api/accounts/get/balance 

      If for eg. you think that writing api_routes.A_B_C_D_E is too cumbersome for a specific path, you can override that in the class itself to a shorter version.

      class APIRoutes: _BASE = "https://wallet.shiftnrg.org/" _BASE_API = _BASE + "api/" SHORT = _BASE_API + "/some/long/route/for/api" def __getattr__(self, route): return self._BASE_API + route.lower().replace('_', '/') 
      \$\endgroup\$
      2
      • \$\begingroup\$Could you clarify how I would override that in the class to a shorter version?\$\endgroup\$
        – strontiux
        CommentedMar 26, 2018 at 13:59
      • \$\begingroup\$@strontiux updated\$\endgroup\$CommentedMar 26, 2018 at 14:04

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.