PK ! f^ ^ formation/__init__.pyfrom .__version__ import __version__ # noqa from .formation import wrap __all__ = ["wrap"] PK ! b[y formation/__version__.py__version__ = "0.1.26"PK ! 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) base_uri = kwargs.get("base_uri", self.__class__.base_uri) self.request = build( middleware=kwargs.get("middleware", self.__class__.middleware), base_uri=base_uri, ) self.base_uri = 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 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 !H9VW X formation-0.1.26.dist-info/WHEEL A н#f."jm)!fb҅~ܴA,mTD}En0H饹*|D[¬ci=0(q3PK !HO] ? # formation-0.1.26.dist-info/METADATAVnF}WL k뤈Q'Mm}kҒZ{-Ĩ`93sf))4JN1eVa{y$USU®tJ9k*i*%eKaάp6opa譩xP k7sf~4Z?SP)kI3Jp(tnly3yE "16^hz/<p%T9.ЯpDgT4/WtZܲ`}kr+J.@AU!\O}{[<{[Xłm}< 'w Q2Gw}~Lok7vuƯ٥VzpfGd]c<fu\TbB6uދU?B4AIPibE٬^G5aAW'RUm'mbҊ{kM>$gǩ_)eG_mzzCe !,#%Н@TGuE`kOdc۔jw07;Bzilyt.fB
"A'жQS!nyUpm,ؗyJZ kHelƭƃ@sVZ6P+PixXhA!FfRpPi8P78ܝq4
ð*TaDBo)tfUy89M_u:1&C/+f^y:\$~廍5շX*߇.PK !Hc㺜 b ! formation-0.1.26.dist-info/RECORD}r@ }5tY
B \fC
='USMvSuh+<~MSV!MBw(!? cjdάW=q9߲,?