2 ms·
Hi, what I'd really like is to test my conjecture - would you (or anyone reading) be able to email me a (< 500 line, ideally not super-deep-library-code) bit of
by leontrolski 6y ago
Hi, what I'd really like is to test my conjecture - would you (or anyone reading) be able to email me a (< 500 line, ideally not super-deep-library-code) bit of code (constructed or otherwise) that you think is "so innately OO friendly that the conjecture won't hold".
Given some examples, I'll do a follow-up blog post.
- zmmmmm 6y agoYou could take the example as-is with the new requirement that the remote end point may require up front authentication. How the authentication works depends on the service, and it needs to be done on a per-URL basis. The authentication protocol may return some kind of token or key needed to use with future requests. However what this is and exactly how it is used is service dependent (for example, it might be supplied verbatim in an auth header or it might be a signing key and it might even be salted with some credentials etc). So challenge that is introduced is that you now have hidden state that is dependent on the public state but which cannot be computed by users of the client or the parent class. In fact, code in the parent class can't even know ahead of time what state might be needed to be retained. I am guessing you will probably be able to propose that authentication be supported by some kind of pluggable authentication interface, but it will still be difficult to deal with the hidden state without introducing assumptions about the auth protocol or the type / nature of the state retained and without having that protrude into the parent interfaces.
- curryst 6y agoYou can just define them as functions that are part of the class. Something like: @dataclass class AuthMethod: credentials: typing.Any cache: typing.Any apply_to_request: typing.Callable[[typing.Any, typing.Any, Request], None] @dataclass class AuthMapping: method: AuthMethod matches: typing.Callable[[str], bool] class Client: root_url: str url_format: str auth_methods: typing.List[AuthMethod] def auth_request(client: Client, req: Request): applies = filter(client.auth_methods, lambda x: x.matches(req)) if len(applies) > 1: raise Exception("multiple auth methods found") elif len(applies) == 1: method = applies[0] method.apply_to_request(method.credentials, method.cache, req) Then you would statically define your service's auth methods by URL, so users can just do: my_client = Client(auth_methods=MyServicesAuthMethods) or you can create a helper function like: def create_client_a(base_url: str): return Client(base_url, auth_methods=[...]) Alternately I think you can use closures, but I'm not totally positive about the scoping. AuthMethod = typing.Callable[[Request], None] def auth_method_a(username, password) -> AuthMethod: my_token = None def __auth_method_a(req: Request): if my_token is None: my_token = do_login(username, password) req.headers['token'] = my_token return __auth_method That creates a standard interface (a function that accepts a Request), without assuming anything about the underlying protocol.