PK! qpyfil/__init__.py__version__ = '0.1.0' PK!w&&pyfil/pyfil.py#!/usr/bin/env python3 ''' Use python as a filter on stdin. If the expression iterator, print each item on its own line. If the value is a builtin container type, attempt to serialize it as json before printing. pyfil automatically imports any modules used in expressions. If you'd like to create any other objects to use in the execution environment ~/.config/pyfil-env.py and put things in it. default objects: l = [] d = {} These are empty containers you might wish to add items to during iteration, for example. x is always the return value of the previous expression unless --exec. The execution environment also has a special object for stdin, creatively named "stdin". This differs from sys.stdin in that it removes trailing newlines when you iterate over it, and it has a property, stdin.l, which returns a list of the lines, rather than an iterator. Certain other flags; --loop (or anything that implies --loop), --json, --split or --field_sep; may create additional objects. Check the flag descriptions for further details. Home: https://github.com/ninjaaron/pyfil ''' import collections import sys import json import os import re import ast import argparse from functools import update_wrapper class LazyDict(dict): __getattr__ = dict.__getitem__ __setattr__ = dict.__setattr__ __delattr__ = dict.__delitem__ class reify(object): '"stolen" from Pylons' def __init__(self, wrapped): self.wrapped = wrapped update_wrapper(self, wrapped) def __get__(self, inst, objtype=None): if inst is None: return self val = self.wrapped(inst) setattr(inst, self.wrapped.__name__, val) return val class NameSpace(dict): 'namespace that imports modules lazily.' def __missing__(self, name): try: return __import__(name) except ImportError: raise NameError("name '{}' is not defined".format(name)) class StdIn: 'class for wrapping sys.stdin' def __init__(self): self.lines = (line.rstrip('\n') for line in sys.stdin) def __iter__(self): return self.lines @reify def l(self): return sys.stdin.read().splitlines() def __next__(self): return next(self.lines) def __getattr__(self, name): return getattr(sys.stdin, name) class SafeList(collections.UserList): 'class for getting fields from stdin without raising errors' def __getitem__(self, index): try: return self.data[index] except IndexError: return '' def __iter__(self): return iter(self.data) def handle_errors(exception, args): 'stupid simple error handling' if args.raise_errors: raise exception elif args.silence_errors: pass else: print( '\x1b[31m{}\x1b[0m:'.format(exception.__class__.__name__), exception, file=sys.stderr ) class SafeListEncode(json.JSONEncoder): def default(self, obj): if isinstance(obj, SafeList): return obj.data return json.JSONEncoder.default(self, obj) def print_obj(obj, indent=None): "print strings, serialize other stuff to json, or don't" if isinstance(obj, str): print(obj) else: try: print(json.dumps(obj, ensure_ascii=False, indent=indent, cls=SafeListEncode)) except TypeError: print(obj) def run(expressions, args, namespace={}): func = exec if args.exec else eval for expr in expressions: if args.exception_handler: exception, handler = tuple( i.strip() for i in args.exception_handler.split(':', maxsplit=1)) try: value = func(expr, namespace) except __builtins__[exception]: try: value = func(handler, namespace) except Exception as e: value = handle_errors(e, args) continue except Exception as e: value = handle_errors(e, args) continue else: try: value = func(expr, namespace) except Exception as e: value = handle_errors(e, args) continue if not args.exec: namespace.update(x=value) if not (args.quiet or args.exec): if args.join is not None and isinstance(value, collections.Iterable): print(ast.literal_eval("'''" + args.join.replace("'", r"\'") + "'''").join(map(str, value))) elif value is None: pass elif isinstance(value, collections.Iterator): for i in value: print_obj(i) else: indent = None if (args.loop or args.force_oneline_json) else 2 print_obj(value, indent) def main(): ap = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument('expression', nargs='+', help='expression(s) to be ' 'executed. If multiple expression arguments are given, ' 'and --exec is not used, the value of the previous ' "expression is available as 'x' in the following " 'expression. if --exec is used, all assignment must be ' 'explicit.') ap.add_argument('-l', '--loop', action='store_true', help='for n, i in enumerate(stdin): expressions') ap.add_argument('-x', '--exec', action='store_true', help='use exec instead of eval. statements are allowed, ' 'but automatic printing is lost. ' "doesn't affect --post") ap.add_argument('-q', '--quiet', action='store_true', help="suppress automatic printing. doesn't affect --post") ap.add_argument('-j', '--json', action='store_true', help="load stdin as json into object 'j'; If used with " '--loop, treat each line of stdin as a new object') ap.add_argument('-J', '--real-dict-json', action='store_true', help='like -j, but creates real dictionaries instead of ' 'the wrapper that allows dot syntax.') ap.add_argument('-o', '--force-oneline-json', action='store_true', help='outside of loops and iterators, objects serialzed ' 'to json print with two-space indent. this forces ' 'this forces all json objects to print on a single ' 'line.') ap.add_argument('-b', '--pre', help='statement to evaluate before expression args. ' "multiple statements may be combined with ';'. " 'no automatic printing') ap.add_argument('-e', '--post', help='expression to evaluate after the loop. always ' 'handeled by eval, even if --exec, and always prints ' 'return value, even if --quiet. implies --loop') ap.add_argument('-s', '--split', action='store_true', help="split lines from stdin on whitespace into list 'f'. " 'implies --loop') ap.add_argument('-F', '--field-sep', metavar='PATTERN', help="regex used to split lines from stdin into list 'f'. " "implies --loop") ap.add_argument('-n', '--join', metavar='STRING', help='join items in iterables with STRING') ap.add_argument('-R', '--raise-errors', action='store_true', help='raise errors in evaluation and stop execution ' '(default: print message to stderr and continue)') ap.add_argument('-S', '--silence-errors', action='store_true', help='suppress error messages') ap.add_argument('-H', '--exception-handler', help='specify exception handler with the format ' "'Exception: alternative expression to eval'") a = ap.parse_args() func = 'exec' if a.exec else 'eval' expressions = [compile( e if a.exec else "(%s)" % e, '', func) for e in a.expression] user_env = os.environ['HOME'] + '/.config/pyfil-env.py' namespace = NameSpace(__builtins__) namespace.update(stdin=StdIn(), l=[], d={}) if os.path.exists(user_env): exec(open(user_env).read(), namespace) if a.json: jdecode = json.JSONDecoder(object_hook=LazyDict).decode elif a.real_dict_json: jdecode = json.loads a.json = True if a.post or a.split or a.field_sep: a.loop = True if a.loop: if a.pre: exec(a.pre, namespace) for n, i in enumerate(map(str.rstrip, sys.stdin)): namespace.update(i=i, n=n) if a.json: namespace.update(j=jdecode(i)) if a.field_sep: if len(a.field_sep) == 1: f = SafeList(i.split(a.field_sep)) else: f = SafeList(re.split(a.field_sep, i)) namespace.update(f=f) elif a.split: namespace.update(f=SafeList(i.split())) run(expressions, a, namespace) if a.post: if a.quiet or a.exec: a.loop, a.quiet, a.exec = None, None, None run(('(%s)' % a.post,), a, namespace) else: if a.pre: exec(a.pre, namespace) if a.json: namespace.update(j=jdecode(sys.stdin.read())) run(expressions, a, namespace) if __name__ == '__main__': main() PK!H>&*?&pyfil-1.9.1.dist-info/entry_points.txtN+I/N.,()*Ḻz`*713(S PK!rW|pyfil-1.9.1.dist-info/LICENSECopyright (c) 2016, Aaron Christianson All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK!H STTpyfil-1.9.1.dist-info/WHEEL 1 0 нR. \I$ơ7.ZON `h6oi14m,b4>4ɛpK>X;baP>PK!H CnSpyfil-1.9.1.dist-info/METADATA\mƑlUtv|yYRIr+IAbHbh,v~=\YN%t{;[I\_mYE>1xm'f[Y޸}[/:.wlW 4 ^YSl_6i֦XgL' -6KM59>^Eb}yE~ &KaPjY!6kLJ̢,ֲ.Zkh&Ob,:nǘh+%ZKٸ*(5-0/i@IIL( t.jQ[ᢙ&V+a&'yT Q>1YZO[L:}LߠUa!ƞ4=N_gh |)J`N31s0&`@`q0"E9e8 ZpRYN$I-[eUb #<6 *3gO!L*CUt^H$gb7B^@[UYz:dU:%] zQU^s4Cs)"?^J*۲T[Jmibq7PiԒRg_2w(fO zO q[%>D/~WNɞ?L2wg*"Tc @B߽6@(#΍%b^Ei9V< 'mhYqS7kFe8;ob9F+YmIl6pбg@n~Y3:0q\cܞ78aʂ YU2s:eL%Uvt\XrW ^2\'<{~Q^Sŗo̳/_>%ܼxs˷oO<{GQZ!ԗF *_wuX/ye Vau#s_2rwrN6'q/"bnE,15ucp5m-,+nʳNkr;qaZx9DC gVVY (Jk>(:USksRBb*Gjg{zt㜊&!)/Cv`):$>b+Nju7 Gf#=So*+`L|y[Z[SY%r1qOcש џ gak&h,}M kT;GW|B֓uL;/]ηٜ;/{zЈʖE ,99ɼm=cDNީ@$S墔[gޟG:Ҙ ۦC*Twu{.?xϳ pRcDrs؊(+oK{A⅃ @yk/jHEc@"3bsQ:qZЖ%$V0w^((Yab1aΛ*FX\dtӼ#BB6P>i\ӈ:[M3aK'am \21't0F qz iDT9 a?nYg`I7H\S8K"OjSv@^dkq 7_KGmv@i$Dʗ밶My|]3.mQIN+j%ك$qo8t,F\Jv?er;lrI9g+9#E??qBm,ؠTLހ-{RzE",RíjADxpT ΅FJnӵ.׻_;ʘ'*kIQ39 F#qbRH@Xv>cEG=Z8&4+;"NQ ?աLp5S;`҅PpPi꘳V!f/xKFhȋV˜=,vUS#tSs2C F)S^tz1=j'ZLP@Õ_6̴*VoM)&gT s'>#|3N.uPn#arMT{߉'䚉1_:K4 WLʬc(7/l0|56_Κ4Clvcp'; pz6l&>^vw9RS\T21́{x~>ۙOao3FNވ<0<vM~|S;YozXGy^z')|]~Wa#M|DŽO}{W##[t۶j4hݧhP96MsOۜ)4Ms/-{a"iVc|-9s}6(P8|(XPUIeh`r3FUmԅwU},_R4Ԗ.RCv 6BZI˸%Ѣۢdk(ʵ *8y;P FLaۜfzS43V譺NG.c(SfZӫ.EzU_}) $PAU%\}2w,tfKmu nQ <9t\э*sl޺7%ϞI?<}X>=:~8}P?|SDCmBj 0io|VL m4wao6P7M1zo7%ǮA ׮l˴T6U;AϰOåBzgjquԲؑ۾jO´ m5/2}z:eZylk&y5,fhYz]VmKo$l_xgJYB-:#V׊6_[ѥ;a%VoFQ/[eɳ4QMKCDڧs]քD~wk_pEߋIº (%~㮶%U n%m/ya*a:4)q}=e!1E+4X]Z\`N>٪i<5/L-x}1˃m %w,WH!UF+IfɚKBdP~is\&REQu{6׬;g2f;w{ų̋d ĕK51ϹJwS .&B{eڨ⊱uvAEEx*AxգFK6Gl/o[xdz/mImi6G9oaM_oಷ %SAPX7SV=b3dGTvYIÂ{Ї <=C2]U7`A>a,N|豒J"BW5yn)!:`SFnjUNTcp{uΟ>λ/ٻ#(&J+$dsYeJ}TEf^ ]뉽%u; ɤ rlzr 6SWaI\KYk\ˋr 8PXn#~sޫ7 V{Ehstb4-˔v+<,$5Uw.@@9uHcΛY'I.8#1rU׸,S2e &xQ]9v7-cUN_F\+3v ͠Ek-PIbUG.)V+GڛEW*^FA#v-FjbPZE|`q2ÿǸS Flx@N͓Vbg0b yw OiR[ ǃ,s{2ͤRşIrQYUdҒpG0 F<^;)>PյDp z*}oQ3P k`{P 0׳$6WPTٸG?3%F? vHGjiW'm;k=c8 X)` P _[8to^7#p_O:ZE˯F cS7G LYj ,/@f{9=–8ZoO;.*i &Va{CB(T{ fxP}Jgխ84ob8vt,5Ůe1xvɒ6 (c$6&5Je6mO@o-u,\&z5[F?&.G;$?7;ڡGbJwtH[3s<㖠դ+3 M$;(xB{,݁G b gˣlJp\i|[2 Pj,[&r[kx,iۤ 9s\ 'fp5eV XG`c~ٝ ε7Hg/t&SQwom.ݾH|=y3>yqቹ{29w'w7)x۝cѩkl.-C1i=}G2.^oZ*|E'ü90$:ab7c5MddžPݒ&.`?~}vxEKf{TB瞹orwK{D0#f};ҌixFeFƫ(z$Gٓİu^ o`p2pn͍شQ4pJEf.xo{ug}\E%iY^B/n!;ANP&6Ek4d~^`Din*gZ{\}7e_z0PBB\ծ Fc%\r:doL{)hb۾uyIIC@r{:Na1(^PUZt^)|v&ruP&OQ8. |5jKb,2hjݺ"A{K"7$vQT1@T$qɽ?kZ |UT6zsjhZi߼ (,_ޢ;c Ms`BK\d;P%oIުd=m:a+9?7b4vG&r%Z~:k`VJz5n_{7]٦C՚O0ާOtjyHg Op(Aӊ eAs5R -\ D&XYtyc|(h޸2TŒu2\k4v5N[V$zJvPOI$QV/PK! qpyfil/__init__.pyPK!w&&Epyfil/pyfil.pyPK!H>&*?&*'pyfil-1.9.1.dist-info/entry_points.txtPK!rW|'pyfil-1.9.1.dist-info/LICENSEPK!H STT,pyfil-1.9.1.dist-info/WHEELPK!H CnSz-pyfil-1.9.1.dist-info/METADATAPK!HbV Lpyfil-1.9.1.dist-info/RECORDPK*N