PK!f^^formation/__init__.pyfrom .__version__ import __version__ # noqa from .formation import wrap __all__ = ["wrap"] PK!formation/__version__.py__version__ = "0.1.15"PK!formation/for_requests.pyimport requests from .formation import wrap, _REQ_HTTP, _RES_HTTP, _SESSION from attr import attrib, attrs, fields, asdict __all__ = ["build_sender"] @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 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!6aformation/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) return ctx call = breaker(call) try: ctx = call(ctx) return ctx except pybreaker.CircuitBreakerError: pass 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.15.dist-info/WHEEL A н#f."jm)!fb҅~ܴA,mTD}E n0H饹*|D[¬c i=0(q3PK!HVr" ? #formation-0.1.15.dist-info/METADATAVnF}WL I뤈Q'Mm} kkҒZ{-Ĩ`93sf ))GtED#ʌh09JN]rlUJYӀ%UJʒ2)YmRF';S9n4,IM55Z?QP)kq3Jp:7}6w/4}ES QUyD3p*S 5藫s:kkX{ڼ5UtN O?Cɳ[<[f:X*1L{:Ei* !$4f>tZ̚ث6֓6MffaEݽ6f&gE3/"¯PTya=ǽ!@E|oZna B}#_кޠh'm@iM Z T!n4TP̄ m$3vTh( \hwKz S2h 2 KѬQ -a}wƵl2hEuzcv̙i|+-4V< , #3.;kl(L$(VG8BuaZNv* ]0"!ޣp zeU^`l/)N{t|x&@Pdǡ3M.~z{/F[,CDPK!Hyb!formation-0.1.15.dist-info/RECORD}r@} e1 D4Ej. yI$_}u*_!$%!\?tL":=!Fn\GuH?*2R2֐W=qj?`RfWWd9v3-+MS isO[xnޣMe]7yfEOf ,rXZp { PJl5<^{Q2(]~RxW$#j` 4vѰ!*|>̞m&fC7ߧ(̏G3+vłUB&%^7M_1uT1n*7'GmYzMHz ti$ښl_/w716ȝ Ǒd)p >_WIjR_PK!f^^formation/__init__.pyPK!formation/__version__.pyPK!formation/for_requests.pyPK!2formation/formation.pyPK!6a formation/middleware.pyPK!H9VWX formation-0.1.15.dist-info/WHEELPK!HVr" ? #formation-0.1.15.dist-info/METADATAPK!Hyb!formation-0.1.15.dist-info/RECORDPKG