Skip to content

Instantly share code, notes, and snippets.

@con-f-use
Last active November 25, 2022 14:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save con-f-use/1165b555a187c96c6ab0201fade02958 to your computer and use it in GitHub Desktop.
Save con-f-use/1165b555a187c96c6ab0201fade02958 to your computer and use it in GitHub Desktop.
dynamic rest client concept (abuse python's getattr and getitem)
import urllib.parse
class Endpoint:
def __init__(self, uri, api):
self.m4ngl3 = dict(uri=uri, api=api) # prevent name collisions (with actual members called "uri" or "api", endpoints of these names couldn't be used - we can probably live without ones named "m4ngl3")
def __getitem__(self, name):
api, uri = self.m4ngl3["api"], self.m4ngl3["uri"]
key = api.canonize(uri, name)
return api._resources.setdefault(key, Endpoint(key, api))
def __getattr__(self, name):
if name in ["get", "post"]:
return self._request(name)
return self.__getitem__(name)
def _request(self, name):
def abstract_request(*args, **kwargs):
print(f"simulated {name}-request to {self.m4ngl3['uri'].geturl()} with args: {args}; kwargs: {kwargs}") # requests.request(name, self.m4ngl3["uri"].geturl(), *args, **kwargs) # for real
return abstract_request
class Rest(Endpoint):
def __init__(self, base_url):
self._resources = {} # cache enpoints here on first use (performance!)
super().__init__(urllib.parse.urlsplit(base_url), self)
@staticmethod
def canonize(source, addition):
new = urllib.parse.urlsplit(addition)._asdict()
new["path"] = f"{source.path.strip('/')}/{new['path']}"
del new["scheme"]; del new["netloc"]
return source._replace(**new)
if __name__ == "__main__":
# Usage examples:
rest = Rest("https://example.com/v1")
rest.foo.bar.blubb.get("something", key="word")
creator = rest.user.create
for x in ["user1", "user2", "user3"]:
creator[x].post("extradata")
creator["multi/component/url"].post()
@con-f-use
Copy link
Author

con-f-use commented Jan 24, 2022

    # Usage examples:
    rest = Rest("https://example.com/v1")
    rest.foo.bar.blubb.get("something", key="word")

    creator = rest.user.create
    for x in ["user1", "user2", "user3"]:
        creator[x].post("extradata")

    creator["multi/component/url"].post()

Output:

$ python dynamic_rest_client.py
simulated get-request to https://example.com/v1/foo/bar/blubb with args: ('something',); kwargs: {'key': 'word'}
simulated post-request to https://example.com/v1/user/create/user1 with args: ('extradata',); kwargs: {}
simulated post-request to https://example.com/v1/user/create/user2 with args: ('extradata',); kwargs: {}
simulated post-request to https://example.com/v1/user/create/user3 with args: ('extradata',); kwargs: {}
simulated post-request to https://example.com/v1/user/create/multi/component/url with args: (); kwargs: {}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment