diff --git a/.gitignore b/.gitignore index 5c1d549..35379c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ dist *.pyc +*swp +MANIFEST diff --git a/MANIFEST b/MANIFEST deleted file mode 100644 index 69e500c..0000000 --- a/MANIFEST +++ /dev/null @@ -1,5 +0,0 @@ -# file GENERATED by distutils, do NOT edit -README.txt -setup.py -bin/pythonpy -pythonpy/__init__.py diff --git a/README.rst b/README.rst index ceab535..c662756 100644 --- a/README.rst +++ b/README.rst @@ -15,18 +15,19 @@ For a permanent alias (For Bash users): :: -Float arithmetic ----------------- +Float Arithmetic +~~~~~~~~~~~~~~~~ :: $ py '3 * 1.5' 4.5 + :: Exponentiation --------------- +~~~~~~~~~~~~~~ :: @@ -36,7 +37,7 @@ Exponentiation :: Number sequence ---------------- +~~~~~~~~~~~~~~~ :: @@ -48,7 +49,7 @@ Number sequence :: List comprehensions -------------------- +~~~~~~~~~~~~~~~~~~~ :: @@ -61,7 +62,7 @@ List comprehensions :: Math library usage ------------------- +~~~~~~~~~~~~~~~~~~ :: @@ -71,7 +72,7 @@ Math library usage :: Random library usage --------------------- +~~~~~~~~~~~~~~~~~~~~ :: @@ -81,7 +82,7 @@ Random library usage :: Multiply each line of input by 7. ---------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: @@ -93,7 +94,7 @@ Multiply each line of input by 7. :: Append ".txt" to each line of input ------------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: @@ -112,7 +113,7 @@ Reverse a list :: - $ py 'range(4)' | py -l 'sorted(l, reverse=True)' + $ py 'range(4)' | py -l 'l[::-1]' 3 2 1 @@ -121,7 +122,7 @@ Reverse a list :: Sum a list of numbers ---------------------- +~~~~~~~~~~~~~~~~~~~~~ :: @@ -131,7 +132,7 @@ Sum a list of numbers :: Count the lines of input ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ :: @@ -214,7 +215,21 @@ Get long palindromes :: -Ignore AttributeErrors if they pop up with (--i) +Count words beginning with each letter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + $ cat /usr/share/dict/words | py -x 'x[0].lower()' | py -l 'Counter(l).most_common(5)' + ('s', 11327) + ('c', 9521) + ('p', 7659) + ('b', 6068) + ('m', 5922) + +:: + +Keep going if some rows raise Errors with (--i). ------------------------------------------------ Get the local network ip @@ -226,3 +241,7 @@ Get the local network ip 192.168.1.41 :: + + +If you haven't had enough yet, check out the `wiki `__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/README.txt b/README.txt deleted file mode 100644 index 27aef0c..0000000 --- a/README.txt +++ /dev/null @@ -1,104 +0,0 @@ -# install -sudo pip install pythonpy; alias py='pythonpy' - -# float arithmetic -$ py '3 * 1.5' -4.5 - -# exponentiation -$ py '7**3' -343 - -# number sequence -$ py 'range(3)' -0 -1 -2 - -# list comprehensions -$ py '[x**2 for x in range(1,5)]' -1 -4 -9 -16 - -# math library usage -$ py 'math.exp(1)' -2.71828182846 - -# random library usage -$ py 'random.random()' -0.103173957713 - -# multiply each line of input by 7. -$ py 'range(3)' | py -x 'int(x)*7' -0 -7 -14 - -# append ".txt" to each line of input -$ py 'range(3)' | py -x 'x + ".txt"' -0.txt -1.txt -2.txt - -# Sometimes you want to treat the input as a python list. -# reverse a list -$ py 'range(4)' | py -l 'sorted(l, reverse=True)' -3 -2 -1 -0 - -# sum a list of numbers -$ py 'range(4)' | py -l 'sum(int(x) for x in l)' -6 - -# count the lines of input -$ py 'range(17)' | py -l 'len(l)' -17 - -# Other times you just want to filter out lines from the input. -# get only even numbers -$ py 'range(8)' | py -x 'x if int(x)%2 == 0 else None' -0 -2 -4 -6 - -# The shorthand -fx (filter on x) is also available. -# get only odd numbers -$ py 'range(8)' | py -fx 'int(x)%2 == 1' -1 -3 -5 -7 - -# get words starting with "and" -$ cat /usr/share/dict/words | py -fx 're.match(r"and", x)' | head -5 -and -andante -andante's -andantes -andiron - -#get verbs starting with ba -$ cat /usr/share/dict/words | py -fx 're.match(r"ba.*ing$", x)' | head -5 -baaing -babbling -babying -babysitting -backbiting - -# get long palindromes -$ cat /usr/share/dict/words | py -fx 'x==x[::-1] and len(x) >= 5' | head -5 -civic -deified -kayak -level -ma'am - -# ignore AttributeErrors if they pop up with (--i). -# get the local network ip -$ ifconfig | py -x --i 're.search(r"192\.168[\d\.]+", x).group()' -192.168.1.41 diff --git a/bin/pythonpy b/bin/pythonpy deleted file mode 100755 index 4f03da5..0000000 --- a/bin/pythonpy +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python -import argparse -import glob -import itertools -import json -import math -import os -import random -import re -import shutil -import sys - -parser = argparse.ArgumentParser() -parser.add_argument('evaluation', nargs='?', default='None') -parser.add_argument('-c', '--cmd') -parser.add_argument('--ji' '--json_in', - dest='json_input', action='store_const', - const=True, default=False) -parser.add_argument('--jo' '--json_out', - dest='json_output', action='store_const', - const=True, default=False) -parser.add_argument('-x' '--line_by_line', - dest='line_by_line', action='store_const', - const=True, default=False, - help='sum the integers (default: find the max)') -parser.add_argument('-sv' '--split_values', - dest='split_values', - default=False, - help='sum the integers (default: find the max)') -parser.add_argument('-l', '--list_of_stdin', - dest='list_of_stdin', action='store_const', - const=True, default=False) -parser.add_argument('-fx', '--filter', - dest='filter_result', action='store_const', - const=True, default=False) -parser.add_argument('--i', '--ignore_exceptions', - dest='ignore_exceptions', action='store_const', - const=True, default=False) - -args = parser.parse_args() - -if args.json_input: - stdin = (json.loads(x.rstrip()) for x in sys.stdin) -else: - stdin = (x.rstrip() for x in sys.stdin) - -if args.evaluation: - args.evaluation = args.evaluation.replace("`", "'") -if args.cmd: - args.cmd = args.cmd.replace("`", "'") - -if args.cmd: - exec(args.cmd) - -if args.line_by_line: - if args.ignore_exceptions: - def safe_eval(text, x): - try: - return eval(text) - except: - return None - result = (safe_eval(args.evaluation, x) for x in stdin) - else: - result = (eval(args.evaluation) for x in stdin) -elif args.list_of_stdin: - l = list(stdin) - result = eval(args.evaluation) -elif args.filter_result: - result = (x for x in stdin if eval(args.evaluation)) -elif args.split_values: - result = (eval(args.evaluation) for sv in - (re.split(args.split_values, x) for x in stdin)) -else: - result = eval(args.evaluation) - -if hasattr(result, '__iter__'): - for x in result: - if x is not None: - if args.json_output: - print json.dumps(x) - else: - print x -elif result is not None: - if args.json_output: - print json.dumps(result) - else: - print result diff --git a/pythonpy b/pythonpy new file mode 100755 index 0000000..6923642 --- /dev/null +++ b/pythonpy @@ -0,0 +1,129 @@ +#!/usr/bin/env python +from __future__ import (unicode_literals, absolute_import, + print_function, division) +import argparse +import sys +import json +import re + +def lazy_imports(expression, pre_cmd, post_cmd): + query = ((expression if expression else '') + + (pre_cmd if pre_cmd else '') + + (post_cmd if post_cmd else '')) + if 'base64' in query: global base64; import base64 + if 'calendar' in query: global calendar; import calendar + if 'csv' in query: global csv; import csv + if 'datetime' in query: global datetime; import datetime + if 'hashlib' in query: global hashlib; import hashlib + if 'glob' in query: global glob; import glob + if 'itertools' in query: global itertools; import itertools + if 'json' in query: global json; import json + if 'math' in query: global math; import math + if 'os' in query: global os; import os + if 'random' in query: global random; import random + if 're' in query: global re; import re + if 'shutil' in query: global shutil; import shutil + if 'tempfile' in query: global tempfile; import tempfile + if 'Counter' in query: global Counter; from collections import Counter + if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict + if 'groupby' in query: global groupby; from itertools import groupby + if 'uuid4' in query: global uuid4; from uuid import uuid4 + +parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter) + +parser.add_argument('expression', nargs='?', default='None') +parser.add_argument('-x', dest='lines_of_stdin', action='store_const', + const=True, default=False, + help='treat each row as x') +parser.add_argument('-fx', dest='filter_result', action='store_const', + const=True, default=False, + help='keep rows satisfying condition(x)') +parser.add_argument('-l', dest='list_of_stdin', action='store_const', + const=True, default=False, + help='treat list of stdin as l') +parser.add_argument('-c', dest='pre_cmd', help='run code before expression') +parser.add_argument('-C', dest='post_cmd', help='run code after expression') +parser.add_argument('--i', '--ignore_exceptions', + dest='ignore_exceptions', action='store_const', + const=True, default=False, + help='') +parser.add_argument('--si', '--split_input', dest='split_input', + help='pre-process each row with re.split(delimiter, row)') +parser.add_argument('--so', '--split_output', dest='split_output', + help='post-process each row with delimiter.join(row)') +parser.add_argument('--ji' '--json_input', + dest='json_input', action='store_const', + const=True, default=False, + help='pre-process each row with json.loads(row)') +parser.add_argument('--jo' '--json_output', + dest='json_output', action='store_const', + const=True, default=False, + help='post-process each row with json.dumps(row)') + +args = parser.parse_args() + +if args.json_input: + stdin = (json.loads(x.rstrip()) for x in sys.stdin) +elif args.split_input: + stdin = (re.split(args.split_input, x.rstrip()) for x in sys.stdin) +else: + stdin = (x.rstrip() for x in sys.stdin) + +if args.expression: + args.expression = args.expression.replace("`", "'") +if args.pre_cmd: + args.pre_cmd = args.pre_cmd.replace("`", "'") +if args.post_cmd: + args.post_cmd = args.post_cmd.replace("`", "'") + +lazy_imports(args.expression, args.pre_cmd, args.post_cmd) + +if args.pre_cmd: + exec(args.pre_cmd) + +def safe_eval(text, x): + try: + return eval(text) + except: + return None + +if args.lines_of_stdin: + if args.ignore_exceptions: + result = (safe_eval(args.expression, x) for x in stdin) + else: + result = (eval(args.expression) for x in stdin) +elif args.filter_result: + if args.ignore_exceptions: + result = (x for x in stdin if safe_eval(args.expression, x)) + else: + result = (x for x in stdin if eval(args.expression)) +elif args.list_of_stdin: + l = list(stdin) + result = eval(args.expression) +else: + result = eval(args.expression) + +def format(output): + if output == None: + return None + elif args.json_output: + return json.dumps(output) + elif args.split_output: + return args.split_output.join(output) + else: + return output + + +if hasattr(result, '__iter__'): + for x in result: + formatted = format(x) + if formatted is not None: + print(formatted) +else: + formatted = format(result) + if formatted is not None: + print(formatted) + +if args.post_cmd: + exec(args.post_cmd) diff --git a/setup.py b/setup.py index 82f664a..3b1bd9e 100644 --- a/setup.py +++ b/setup.py @@ -3,9 +3,10 @@ setup( name='pythonpy', - version='0.1.5', - description='Command line utility for Python', - scripts=[os.path.join('bin', 'pythonpy')], - license='Creative Commons Attribution-Noncommercial-Share Alike license', - long_description=open('README.rst').read(), + version='0.2.4', + description='Take advantage of your python skills from the command line', + scripts=['pythonpy'], + license='MIT', + url='https://github.com/Russell91/pythonpy', + long_description='', ) diff --git a/test/test_pythonpy.py b/test/test_pythonpy.py index a9aa9d1..a1768e7 100644 --- a/test/test_pythonpy.py +++ b/test/test_pythonpy.py @@ -8,6 +8,44 @@ def test_empty(self): def test_numbers(self): self.assertEqual(check_output(['pythonpy', '3 * 4.5']),'13.5\n') + def test_range(self): + self.assertEqual(check_output(['pythonpy', 'range(3)']), '\n'.join(map(str, range(3))) + '\n') + + def test_split_input(self): + self.assertEqual(check_output(["""echo a,b | pythonpy -x 'x[1]' --si ,"""], shell=True), 'b\n') + + def test_split_output(self): + self.assertEqual(check_output(["""echo abc | pythonpy -x x --si '' --so ','"""], shell=True), 'a,b,c\n') + + def test_ignore_errors(self): + self.assertEqual(check_output("""echo a | pythonpy -x --i 'None.None'""", shell=True), '') + self.assertEqual(check_output("""echo a | pythonpy -fx --i 'None.None'""", shell=True), '') + + def test_statements(self): + self.assertEqual(check_output("""pythonpy -c 'a=5' -C 'print(a)'""", shell=True), '5\n') + self.assertEqual(check_output("""echo 3 | pythonpy -c 'a=5' -x x -C 'print(a)'""", shell=True), '3\n5\n') + + def test_imports(self): + check_output("pythonpy 'math'", shell=True) + check_output("pythonpy 'base64'", shell=True) + check_output("pythonpy 'calendar'", shell=True) + check_output("pythonpy 'csv'", shell=True) + check_output("pythonpy 'datetime'", shell=True) + check_output("pythonpy 'hashlib'", shell=True) + check_output("pythonpy 'glob'", shell=True) + check_output("pythonpy 'itertools'", shell=True) + check_output("pythonpy 'json'", shell=True) + check_output("pythonpy 'math'", shell=True) + check_output("pythonpy 'os'", shell=True) + check_output("pythonpy 'random'", shell=True) + check_output("pythonpy 're'", shell=True) + check_output("pythonpy 'shutil'", shell=True) + check_output("pythonpy 'tempfile'", shell=True) + check_output("pythonpy -c 'Counter'", shell=True) + check_output("pythonpy -c 'OrderedDict'", shell=True) + check_output("pythonpy -c 'groupby'", shell=True) + check_output("pythonpy 'uuid4'", shell=True) + if __name__ == '__main__': unittest.main()