PK!f^^formation/__init__.pyfrom .__version__ import __version__ # noqa from .formation import wrap __all__ = ["wrap"] PK!fformation/__version__.py__version__ = "0.1.22"PK!R@ formation/for_requests.pyimport requests from requests.compat import urljoin from .formation import wrap, _REQ_HTTP, _RES_HTTP, _SESSION from attr import attrib, attrs import datetime __all__ = ["build_sender", "build", "client"] def client(cls=None): def client_decorator(cls): original_init = cls.__init__ def now_iso(self): return datetime.datetime.utcnow().isoformat() def path(self, p): return requests.compat.urljoin(self.base_uri, p) def init(self, *args, **kwargs): original_init(self, *args, **kwargs) self.request = build( middleware=kwargs.get("middleware", self.__class__.middleware) ) self.base_uri = kwargs.get("base_uri", self.__class__.base_uri) cls.path = path cls.now_iso = now_iso cls.__init__ = init return cls if cls: return client_decorator(cls) return client_decorator @attrs class FormationHttpRequest(object): url = attrib() method = attrib(default="get") headers = attrib(default={}) params = attrib(default={}) auth = attrib(default=None) data = attrib(default=None) def build_sender(middleware=[]): wrapped = wrap(requests_adapter, middleware=middleware) def sender(method, url, session_context={}, **kwargs): ctx = { _REQ_HTTP: FormationHttpRequest(url=url, method=method, **kwargs), _SESSION: session_context, } ctx = wrapped(ctx) return ctx[_RES_HTTP] return sender class Sender(object): def __init__(self, middleware=[], base_uri=None): self.base_uri = base_uri self.send = build_sender(middleware) def send(self, method, path, session_context={}, **kwargs): ctx = { _REQ_HTTP: FormationHttpRequest( url=urljoin(self.base_uri, path), method=method, **kwargs ), _SESSION: session_context, } ctx = self.wrapped(ctx) return ctx[_RES_HTTP] def get(self, path, **kwargs): return self.send("get", path, **kwargs) def post(self, path, **kwargs): return self.send("post", path, **kwargs) def put(self, path, **kwargs): return self.send("put", path, **kwargs) def build(middleware=[], base_uri=None): return Sender(middleware=middleware, base_uri=base_uri) # TODO: pass more requests vars via req (e.g. timeout, retry) def requests_adapter(ctx): req = ctx[_REQ_HTTP] meth = getattr(requests, req.method.lower()) # TODO ship var as kwargs and not explicitly res = meth( req.url, headers=req.headers, params=req.params, auth=req.auth, data=req.data ) ctx[_RES_HTTP] = res return ctx PK!formation/formation.pyfrom toolz import reduce _REQ_HTTP = "fmtn.req.http" _RES_HTTP = "fmtn.res.http" _CONTEXT = "fmtn.context" _SESSION = "fmtn.session" _RETRY = "fmtn.retry" _REQ_ID = "req.id" _UID = "uid" _REQ_PARENT_ID = "req.parent.id" _REQ_DURATION = "req.duration_us" def wrap(call, middleware=[]): return reduce( lambda acc, m: lambda ctx: m(ctx, acc), reversed(middleware), lambda ctx: call(ctx), ) PK! /m]formation/middleware.pyfrom .formation import ( _REQ_HTTP, _CONTEXT, _SESSION, _RES_HTTP, _RETRY, _REQ_ID, _REQ_PARENT_ID, _REQ_DURATION, _UID, ) from six.moves import _thread as thread import datetime from uuid import uuid4 from toolz.curried import valfilter, get_in import pybreaker import os def default_stack(logger): return [ create_request_id(), create_context(), create_request_duration(), create_request_logger(logger), ] def create_request_id(key="x-request-id", idgen=uuid4): def requests_id(ctx, next): headers = ctx[_REQ_HTTP].headers headers[key] = headers.get(key, str(idgen())) ctx[_REQ_ID] = headers[key] ctx = next(ctx) return ctx return requests_id def create_request_duration(now=datetime.datetime.now): def request_duration(ctx, next): start = now() ctx = next(ctx) end = now() - start ctx["req.duration_us"] = end.microseconds return ctx return request_duration def get_context( request_id=None, request_parent_id=None, namespace="service", env="local", sha="dev", version="0.0.1", scope="service", uid=None, getpid=os.getpid, gettid=thread.get_ident, ): pid = getpid() tid = gettid() return { "v": version, "sha": sha, "env": env, "pid": pid, "tid": tid, "uid": uid, "scope": scope, "ns": namespace, "rid": request_id, "rid_p": request_parent_id, } def create_context( context_fn=get_context, namespace="service", scope="all", env="local", sha="dev", version="0.01", getpid=os.getpid, gettid=thread.get_ident, ): def context(ctx, call): request_id = ctx[_REQ_ID] request_parent_id = ctx.get(_REQ_PARENT_ID, None) uid = get_in([_SESSION, _UID], None) ctx[_CONTEXT] = context_fn( env=env, sha=sha, version=version, request_id=request_id, request_parent_id=request_parent_id, scope=scope, uid=uid, getpid=getpid, gettid=gettid, ) ctx = call(ctx) return ctx return context def create_request_logger(logger): no_nones = valfilter(lambda x: x) def request_logger(ctx, next): req = ctx[_REQ_HTTP] context = ctx.get(_CONTEXT, {}) msg = "request.http" log = logger.bind(**context) log.info(msg, url=req.url, method=req.method, params=no_nones(req.params)) log.debug(msg, headers=req.headers) ctx = next(ctx) res = ctx[_RES_HTTP] msg = "response.http" log.info( msg, url=res.request.url, status=res.status_code, method=res.request.method, elapsed=res.elapsed, size=len(res.content), duration_us=ctx.get(_REQ_DURATION, None), ) log.debug(msg, headers=res.headers) return ctx return request_logger def breaker_logger(logger): class LogListener(pybreaker.CircuitBreakerListener): "Listener used to log circuit breaker events." def state_change(self, cb, old_state, new_state): logger.warn( "circuitbreaker.state.changed", name=cb.name, old_state=old_state.name, new_state=new_state.name, ) return LogListener() def create_circuit_breaker(logger, name): breaker = pybreaker.CircuitBreaker(name=name, listeners=[breaker_logger(logger)]) def circuit_breaker(ctx, call): context = ctx.get(_CONTEXT, {}) log = logger.bind(**context) if breaker.current_state == "open": log.info("circuitbreaker.middleware.open", name=breaker.name) call = breaker(call) try: ctx = call(ctx) return ctx except pybreaker.CircuitBreakerError: return ctx return circuit_breaker def retry(max_retries=3): def retry_middleware(ctx, call): try: res = call(ctx) return res except Exception as ex: retries = ctx.get(_RETRY, 0) if retries > max_retries: raise ex ctx[_RETRY] = 1 + retries # TODO exponential backoff res = retry_middleware(ctx, call) return res return retry_middleware PK!H9VWX formation-0.1.22.dist-info/WHEEL A н#f."jm)!fb҅~ܴA,mTD}E n0H饹*|D[¬c i=0(q3PK!H? #formation-0.1.22.dist-info/METADATAVnF}WL I뤈Q'Mm} kҒZ{-Ĩ`93sf ))4JN1eVa{y$QtT1RΚJ)ktJIYRX&3+M#}X$zk*"Gڍ\'.T|륱ҁc)ܢ/? ?v {^ivLދ6\ U*<"8)9ֵ5,=Xm_b܊R: lpwUWgx|_|Omݽ-E@*nfS.uer~L{(dq}r1_uasb>I;[(;P_ >r]^}Mڷt|bɵq:Rp=83#`1kW~I_HQ`zW,нMjsMPhTGl6QfMEXPUTUIe%}^y"qW}JEYW[(*~輰^ z٢wd-w0HIt?U?!6/Gh]oQ4m#6kZ4Ͷ^[],/>tuo.5>IΘ z#Q邮B2QtUYRת}Pdd{ imu|T6Za9$ס6 I*azoÓrc3 8e=K|< ))\d G2,!EPe\mSsU*Jv0J+Q%0Qe$!l۟sn7 cCY*go^vg,TZcbQFLJn%!ԷɇQ\v+T]C|;.3j-ź+X vo҅=,N|F ^'=Rm6ͮnsae 뚐Ԏ+*Ӎ(Cn&tГC+3$2Lx$4/9* fBbqT 9|Pp`* m.T['=)gjx\K%jhިR;HZ6Re[q":1;P4 T T+Zzm{B/51&{ #w~~ p0@ 'D;tA8L*/Wv=<.P<p%tLӋ7\Ǜį|QƢK%ѿPK!H?$b!formation-0.1.22.dist-info/RECORD}ѻn@>2v` -07s 161 ܞ~#6Mt7]q0x΀ d?d"J>OBFi\F?!zgA#|e YG;Xh{lriݭĜVdNuv;p}UeGFz҃H~ .\+SOa1cgoTj uwN {~Պ & #>(|Y,9ތӾiI6G]6[u=}Vf\b] 0,6ԖBM7kxTS[dClYپ W߶,=,7z_E_/Z#7*~f SNjǢ=kQ.^d^PK!f^^formation/__init__.pyPK!fformation/__version__.pyPK!R@ formation/for_requests.pyPK! formation/formation.pyPK! /m] formation/middleware.pyPK!H9VWX formation-0.1.22.dist-info/WHEELPK!H? #6 formation-0.1.22.dist-info/METADATAPK!H?$b!%formation-0.1.22.dist-info/RECORDPKGH'