From 16f16b9c6a06a39af02d282d46e12edbcfac9cf9 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 00:41:27 -0700 Subject: [PATCH 001/254] Changed license to MIT. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 82f664a..5fbedfb 100644 --- a/setup.py +++ b/setup.py @@ -6,6 +6,6 @@ version='0.1.5', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], - license='Creative Commons Attribution-Noncommercial-Share Alike license', + license='MIT', long_description=open('README.rst').read(), ) From 7ee3e62bd6d82476e3f76f596694d2a7059c3c97 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 11:30:24 -0700 Subject: [PATCH 002/254] 0.1.6 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5fbedfb..2bd295f 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.1.5', + version='0.1.6', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From 148b562b9107bd4a4ab801405dab62794e0c6033 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 11:37:39 -0700 Subject: [PATCH 003/254] Fixed readme issue for 0.1.7 --- MANIFEST | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MANIFEST b/MANIFEST index 69e500c..c217127 100644 --- a/MANIFEST +++ b/MANIFEST @@ -2,4 +2,4 @@ README.txt setup.py bin/pythonpy -pythonpy/__init__.py +test/test_pythonpy.py diff --git a/setup.py b/setup.py index 2bd295f..2ecc3c4 100644 --- a/setup.py +++ b/setup.py @@ -3,9 +3,9 @@ setup( name='pythonpy', - version='0.1.6', + version='0.1.7', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', - long_description=open('README.rst').read(), + #long_description=open('README.rst').read(), ) From 8c0f6d95a1cd20600f96bd05e3ba6787f1d05538 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 11:42:03 -0700 Subject: [PATCH 004/254] Added readme back to setup.py --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 2ecc3c4..0be56af 100644 --- a/setup.py +++ b/setup.py @@ -3,9 +3,9 @@ setup( name='pythonpy', - version='0.1.7', + version='0.1.8dev', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', - #long_description=open('README.rst').read(), + long_description=open('README.txt').read(), ) From 031dc71ea697d3b10c666e1920ba324158e891d7 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 11:50:56 -0700 Subject: [PATCH 005/254] Added README.txt back to setup.py for 0.1.8 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0be56af..182634a 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.1.8dev', + version='0.1.8', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From eda9fc713b8a29d0e3d754a7885273be30907428 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 16:22:48 -0700 Subject: [PATCH 006/254] Changed initial example in README to be more interesting. --- README.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/README.txt b/README.txt index 27aef0c..80d6a2f 100644 --- a/README.txt +++ b/README.txt @@ -1,13 +1,9 @@ # install sudo pip install pythonpy; alias py='pythonpy' -# float arithmetic -$ py '3 * 1.5' -4.5 - -# exponentiation -$ py '7**3' -343 +# arithmetic +$ py '24 * 60 ** 2' +86400 # number sequence $ py 'range(3)' From f94a5e33cfaf73934819704cd5833af1d8666982 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 16:25:02 -0700 Subject: [PATCH 007/254] Added floating point example back to readme. --- README.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.txt b/README.txt index 80d6a2f..0e22ea5 100644 --- a/README.txt +++ b/README.txt @@ -5,6 +5,10 @@ sudo pip install pythonpy; alias py='pythonpy' $ py '24 * 60 ** 2' 86400 +# floating point numbers +$ py '1.0/98' +0.010204081632 + # number sequence $ py 'range(3)' 0 From 2012ebdfb2c3ced75c09d528e2f50b97f36a3832 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 16:29:33 -0700 Subject: [PATCH 008/254] Updated README.rst to match README.txt --- README.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index ceab535..07e4751 100644 --- a/README.rst +++ b/README.rst @@ -15,23 +15,23 @@ For a permanent alias (For Bash users): :: -Float arithmetic +Arithmetic ---------------- :: - $ py '3 * 1.5' - 4.5 + $ py '24 * 60 ** 2' + 86400 :: -Exponentiation +Floating point -------------- :: - $ py '7**3' - 343 + $ py '1.0/98' + 0.010204081632 :: From 9307b2f80b9c7383f65b5912904227a6f7f42e33 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 19:27:00 -0700 Subject: [PATCH 009/254] Changed -sv system to --si and --so system This will make it easier to understand --so and --si as they behave analogously to --jo and --ji --- bin/pythonpy | 57 ++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index 4f03da5..896572a 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -13,34 +13,34 @@ 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('-l', '--list_of_stdin', + dest='list_of_stdin', action='store_const', + const=True, default=False) parser.add_argument('--i', '--ignore_exceptions', dest='ignore_exceptions', action='store_const', const=True, default=False) +parser.add_argument('--si', '--split_input', dest='split_input') +parser.add_argument('--so', '--split_output', dest='split_output') +parser.add_argument('--ji' '--json_input', + dest='json_input', action='store_const', + const=True, default=False) +parser.add_argument('--jo' '--json_output', + dest='json_output', 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) +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) @@ -67,21 +67,26 @@ elif args.list_of_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) +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: - 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 + formatted = format(x) + if formatted: + print formatted +else: + formatted = format(result) + if formatted: + print formatted From 185c0a2c6264e449086f0bc649c6ef941cbf213a Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 19:45:54 -0700 Subject: [PATCH 010/254] Improved the help descriptions --- bin/pythonpy | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index 896572a..5739a03 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -12,28 +12,32 @@ import sys parser = argparse.ArgumentParser() parser.add_argument('evaluation', nargs='?', default='None') -parser.add_argument('-c', '--cmd') -parser.add_argument('-x' '--line_by_line', - dest='line_by_line', action='store_const', +parser.add_argument('-x', dest='lines_of_stdin', action='store_const', const=True, default=False, - help='sum the integers (default: find the max)') -parser.add_argument('-fx', '--filter', - dest='filter_result', action='store_const', - const=True, default=False) -parser.add_argument('-l', '--list_of_stdin', - dest='list_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='cmd', help='run code before expression') parser.add_argument('--i', '--ignore_exceptions', dest='ignore_exceptions', action='store_const', - const=True, default=False) -parser.add_argument('--si', '--split_input', dest='split_input') -parser.add_argument('--so', '--split_output', dest='split_output') + const=True, default=False, + help='') +parser.add_argument('--si', '--split_input', dest='split_input', + help='pre-process each row with re.split(delimiter)') +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) + 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) + const=True, default=False, + help='post-process each row with json.dumps(row)') args = parser.parse_args() @@ -52,7 +56,7 @@ if args.cmd: if args.cmd: exec(args.cmd) -if args.line_by_line: +if args.lines_of_stdin: if args.ignore_exceptions: def safe_eval(text, x): try: From b072a33b22de4c0b348c5550288de005c796cacb Mon Sep 17 00:00:00 2001 From: Low Kian Seong Date: Thu, 12 Jun 2014 15:48:27 +0800 Subject: [PATCH 011/254] Update setup.py I forgot to add this the other day for you, the url that links from pypi to github. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 182634a..9aea9f0 100644 --- a/setup.py +++ b/setup.py @@ -8,4 +8,5 @@ scripts=[os.path.join('bin', 'pythonpy')], license='MIT', long_description=open('README.txt').read(), + url='https://github.com/Russell91/pythonpy', ) From 3c4a5f155ad5ad18a25aafee38dc8125e919477f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 19:39:06 -0700 Subject: [PATCH 012/254] Changed back first to readme examples and changed rst formatting. --- README.rst | 33 +++++++++++++++++---------------- README.txt | 12 ++++++------ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/README.rst b/README.rst index 07e4751..9805214 100644 --- a/README.rst +++ b/README.rst @@ -15,28 +15,29 @@ For a permanent alias (For Bash users): :: -Arithmetic ----------------- +Float Arithmetic +~~~~~~~~~~~~~~~~ :: - $ py '24 * 60 ** 2' - 86400 + $ py '3 * 1.5' + 4.5 + :: -Floating point --------------- +Exponentiation +~~~~~~~~~~~~~~ :: - $ py '1.0/98' - 0.010204081632 + $ py '7**3' + 343 :: 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 ------------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: @@ -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 ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ :: diff --git a/README.txt b/README.txt index 0e22ea5..27aef0c 100644 --- a/README.txt +++ b/README.txt @@ -1,13 +1,13 @@ # install sudo pip install pythonpy; alias py='pythonpy' -# arithmetic -$ py '24 * 60 ** 2' -86400 +# float arithmetic +$ py '3 * 1.5' +4.5 -# floating point numbers -$ py '1.0/98' -0.010204081632 +# exponentiation +$ py '7**3' +343 # number sequence $ py 'range(3)' From e5f67714946c01b6afc3d1fd80bfe097ba08b639 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 20:49:09 -0700 Subject: [PATCH 013/254] Changed AttributeError to Error in README. --- README.rst | 2 +- README.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 9805214..a6b01bb 100644 --- a/README.rst +++ b/README.rst @@ -215,7 +215,7 @@ Get long palindromes :: -Ignore AttributeErrors if they pop up with (--i) +Ignore Errors if they pop up with (--i) ------------------------------------------------ Get the local network ip diff --git a/README.txt b/README.txt index 27aef0c..e98b613 100644 --- a/README.txt +++ b/README.txt @@ -98,7 +98,7 @@ kayak level ma'am -# ignore AttributeErrors if they pop up with (--i). +# ignore Errors 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 From 352785d7540ea8ddaf9f537341c59ce6bae08a54 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 20:51:24 -0700 Subject: [PATCH 014/254] Changed reverse a list example in readme. --- README.rst | 2 +- README.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index a6b01bb..b29d258 100644 --- a/README.rst +++ b/README.rst @@ -113,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 diff --git a/README.txt b/README.txt index e98b613..7a4a606 100644 --- a/README.txt +++ b/README.txt @@ -44,7 +44,7 @@ $ py 'range(3)' | py -x 'x + ".txt"' # Sometimes you want to treat the input as a python list. # reverse a list -$ py 'range(4)' | py -l 'sorted(l, reverse=True)' +$ py 'range(4)' | py -l 'l[::-1]' 3 2 1 From 95dbdb1dd0034f6957c04484834eeebd60aa0d18 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 23:30:18 -0700 Subject: [PATCH 015/254] 0.1.9dev --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9aea9f0..42650a0 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.1.8', + version='0.1.9dev', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From 4dcc2e26394c13ce6efc8099e264a4d28e15fda6 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 23:41:57 -0700 Subject: [PATCH 016/254] Relasing 0.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 42650a0..5294fa6 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.1.9dev', + version='0.2.0', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From 68a350bc348e57e6d65fab0a7daa4d1be86ef778 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 10 Jun 2014 23:45:34 -0700 Subject: [PATCH 017/254] Bugfix for not displaying values evaluating to False --- bin/pythonpy | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index 5739a03..357a95d 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -88,9 +88,9 @@ def format(output): if hasattr(result, '__iter__'): for x in result: formatted = format(x) - if formatted: + if formatted is not None: print formatted else: formatted = format(result) - if formatted: + if formatted is not None: print formatted diff --git a/setup.py b/setup.py index 5294fa6..28be197 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.0', + version='0.2.1', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From 8832b72673add3d855006c37a2256169f5e25a30 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 22:44:13 -0700 Subject: [PATCH 018/254] Changed Error message in tutorial again. --- README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.txt b/README.txt index 7a4a606..e7e09f3 100644 --- a/README.txt +++ b/README.txt @@ -98,7 +98,7 @@ kayak level ma'am -# ignore Errors if they pop up with (--i). +# keep going if row raises Error with (--i). # get the local network ip $ ifconfig | py -x --i 're.search(r"192\.168[\d\.]+", x).group()' 192.168.1.41 From 339a9d156927449983fd43f7eab88825ed00198c Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 22:48:57 -0700 Subject: [PATCH 019/254] 0.2.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 28be197..46ef54c 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.1', + version='0.2.2dev', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From 360a0b655729d8e3a34cbf00c7b0afe5306d0e84 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 22:52:52 -0700 Subject: [PATCH 020/254] 0.2.2dev2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 46ef54c..0f4acbd 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.2dev', + version='0.2.2dev2', description='Command line utility for Python', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From 8fff08222fe275c725c7841feba5e73e4927ae34 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 22:57:01 -0700 Subject: [PATCH 021/254] Removed long_description from pip in favor of url only. --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 0f4acbd..d621fa2 100644 --- a/setup.py +++ b/setup.py @@ -4,9 +4,8 @@ setup( name='pythonpy', version='0.2.2dev2', - description='Command line utility for Python', + description='Take advantage of your python skills from the command line', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', - long_description=open('README.txt').read(), url='https://github.com/Russell91/pythonpy', ) From a01778e8cc00f0b79c4ac2d001cdb1fbd2252e3b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 22:57:20 -0700 Subject: [PATCH 022/254] 0.2.2dev3 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d621fa2..b072c9d 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.2dev2', + version='0.2.2dev3', description='Take advantage of your python skills from the command line', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From 1c9e18af75aa090716afb17bc75601e21e8aad0f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 22:59:03 -0700 Subject: [PATCH 023/254] Added blank long desciption to force pypi. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index b072c9d..6539fe9 100644 --- a/setup.py +++ b/setup.py @@ -8,4 +8,5 @@ scripts=[os.path.join('bin', 'pythonpy')], license='MIT', url='https://github.com/Russell91/pythonpy', + long_description='', ) From 7c5f6ce0f627bad8f88c251c563012193f40a246 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 22:59:21 -0700 Subject: [PATCH 024/254] 0.2.2dev4 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6539fe9..e7c4a88 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.2dev3', + version='0.2.2dev4', description='Take advantage of your python skills from the command line', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From 0c6d2dc402978549beec9bc130bd2e66589f04ba Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 23:10:57 -0700 Subject: [PATCH 025/254] Brought rst and txt readmes into equivalence. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b29d258..1402504 100644 --- a/README.rst +++ b/README.rst @@ -215,7 +215,7 @@ Get long palindromes :: -Ignore Errors if they pop up with (--i) +Keep going if some rows raise Errors with (--i). ------------------------------------------------ Get the local network ip From c24982983d4ef088b52fa07e59b8ff6fa2934ab9 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 23:33:49 -0700 Subject: [PATCH 026/254] Added safe_eval --i option to work for -fx in addition to -x mode. --- bin/pythonpy | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index 357a95d..bfc7671 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -56,21 +56,25 @@ if args.cmd: if args.cmd: exec(args.cmd) +def safe_eval(text, x): + try: + return eval(text) + except: + return None + if args.lines_of_stdin: 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.filter_result: + if args.ignore_exceptions: + result = (x for x in stdin if safe_eval(args.evaluation, x)) + else: + result = (x for x in stdin if eval(args.evaluation)) 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)) else: result = eval(args.evaluation) From 02b2c22a132c3d86756e22494e7ed53b4922b88b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 23:34:25 -0700 Subject: [PATCH 027/254] Updated .gitignore to ignore .swp files. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5c1d549..1bcc247 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ dist *.pyc +*swp From 6b7c9312f3e07d05b7a64c07ad417de5b24b7972 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 12 Jun 2014 23:47:00 -0700 Subject: [PATCH 028/254] Added some good __future__ statements to pythonpy. --- bin/pythonpy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index bfc7671..aab0141 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -1,4 +1,6 @@ #!/usr/bin/env python +from __future__ import (unicode_literals, absolute_import, + print_function, division) import argparse import glob import itertools @@ -93,8 +95,8 @@ if hasattr(result, '__iter__'): for x in result: formatted = format(x) if formatted is not None: - print formatted + print(formatted) else: formatted = format(result) if formatted is not None: - print formatted + print(formatted) From fef752e0825f1587eb707548a95f506b62b5307b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 00:28:40 -0700 Subject: [PATCH 029/254] Added a bunch more imports and a lazy import strategy for 25% perf boost. --- bin/pythonpy | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index aab0141..b468ce4 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -2,15 +2,30 @@ from __future__ import (unicode_literals, absolute_import, print_function, division) import argparse -import glob -import itertools +import sys import json -import math -import os -import random import re -import shutil -import sys + +def lazy_imports(evaluation, cmd): + query = (evaluation if evaluation else '' + + cmd if cmd else '') + 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 'datetime' in query: global datetime; import datetime + if 'hashlib' in query: global hashlib; import hashlib + if 'csv' in query: global csv; import csv + if 'base64' in query: global base64; import base64 + if 'calendar' in query: global calendar; import calendar + if 'Counter' in query: global Counter; from collections import Counter + if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict + if 'uuid4' in query: global uuid4; from uuid import uuid4 parser = argparse.ArgumentParser() parser.add_argument('evaluation', nargs='?', default='None') @@ -55,6 +70,8 @@ if args.evaluation: if args.cmd: args.cmd = args.cmd.replace("`", "'") +lazy_imports(args.evaluation, args.cmd) + if args.cmd: exec(args.cmd) From 7fcd56664d721c32fd6a1efedaea14200e596be6 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 00:29:02 -0700 Subject: [PATCH 030/254] Added a few more unit tests. --- test/test_pythonpy.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/test_pythonpy.py b/test/test_pythonpy.py index a9aa9d1..ad7043d 100644 --- a/test/test_pythonpy.py +++ b/test/test_pythonpy.py @@ -8,6 +8,17 @@ 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_range(self): + self.assertEqual(check_output(["""echo a,b | pythonpy -x 'x[1]' --si ,"""], shell=True), 'b\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), '') + + if __name__ == '__main__': unittest.main() From 220e5ef05dbc703f02e94534119e161890368a2f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 00:54:58 -0700 Subject: [PATCH 031/254] Changed implementation to use OOP :(. --- bin/pythonpy | 223 ++++++++++++++++++++++++++++----------------------- 1 file changed, 122 insertions(+), 101 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index b468ce4..b2595e5 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -6,114 +6,135 @@ import sys import json import re -def lazy_imports(evaluation, cmd): - query = (evaluation if evaluation else '' + - cmd if cmd else '') - 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 'datetime' in query: global datetime; import datetime - if 'hashlib' in query: global hashlib; import hashlib - if 'csv' in query: global csv; import csv - if 'base64' in query: global base64; import base64 - if 'calendar' in query: global calendar; import calendar - if 'Counter' in query: global Counter; from collections import Counter - if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict - if 'uuid4' in query: global uuid4; from uuid import uuid4 +class PythonPy(object): + def __init__(self): + self.parser = self.get_parser() -parser = argparse.ArgumentParser() -parser.add_argument('evaluation', 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='cmd', help='run code before 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)') -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)') + def run(self): + self.args = self.parser.parse_args() + stdin = self.preprocess() + self.lazy_imports() + if self.args.cmd: + exec(self.args.cmd) + results = self.process(stdin) + self.post_process(results) -args = parser.parse_args() + def get_parser(self): + parser = argparse.ArgumentParser() + parser.add_argument('evaluation', 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='cmd', help='run code before 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)') + 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)') + return parser -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) + def lazy_imports(self): + query = (self.args.evaluation if self.args.evaluation else '' + + self.args.cmd if self.args.cmd else '') + 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 'datetime' in query: global datetime; import datetime + if 'hashlib' in query: global hashlib; import hashlib + if 'csv' in query: global csv; import csv + if 'base64' in query: global base64; import base64 + if 'calendar' in query: global calendar; import calendar + if 'Counter' in query: global Counter; from collections import Counter + if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict + if 'uuid4' in query: global uuid4; from uuid import uuid4 -if args.evaluation: - args.evaluation = args.evaluation.replace("`", "'") -if args.cmd: - args.cmd = args.cmd.replace("`", "'") + def preprocess(self): + if self.args.json_input: + stdin = (json.loads(x.rstrip()) for x in sys.stdin) + elif self.args.split_input: + stdin = (re.split(self.args.split_input, x.rstrip()) for x in sys.stdin) + else: + stdin = (x.rstrip() for x in sys.stdin) -lazy_imports(args.evaluation, args.cmd) + if self.args.evaluation: + self.args.evaluation = self.args.evaluation.replace("`", "'") + if self.args.cmd: + self.args.cmd = self.args.cmd.replace("`", "'") + return stdin -if args.cmd: - exec(args.cmd) + def process(self, stdin): + def safe_eval(text, x): + try: + return eval(text) + except: + return None -def safe_eval(text, x): - try: - return eval(text) - except: - return None + if self.args.lines_of_stdin: + if self.args.ignore_exceptions: + result = (safe_eval(self.args.evaluation, x) for x in stdin) + else: + result = (eval(self.args.evaluation) for x in stdin) + elif self.args.filter_result: + if self.args.ignore_exceptions: + result = (x for x in stdin if safe_eval(self.args.evaluation, x)) + else: + result = (x for x in stdin if eval(self.args.evaluation)) + elif self.args.list_of_stdin: + l = list(stdin) + result = eval(self.args.evaluation) + else: + result = eval(self.args.evaluation) + return result -if args.lines_of_stdin: - if args.ignore_exceptions: - result = (safe_eval(args.evaluation, x) for x in stdin) - else: - result = (eval(args.evaluation) for x in stdin) -elif args.filter_result: - if args.ignore_exceptions: - result = (x for x in stdin if safe_eval(args.evaluation, x)) - else: - result = (x for x in stdin if eval(args.evaluation)) -elif args.list_of_stdin: - l = list(stdin) - result = eval(args.evaluation) -else: - result = eval(args.evaluation) + def post_process(self, results): + def format(output): + if output == None: + return None + elif self.args.json_output: + return json.dumps(output) + elif self.args.split_output: + return self.args.split_output.join(output) + else: + return output -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(results, '__iter__'): + for x in results: + formatted = format(x) + if formatted is not None: + print(formatted) + else: + formatted = format(results) + if formatted is not None: + print(formatted) -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) + +def main(): + pythonpy = PythonPy() + pythonpy.run() + + +if __name__ == '__main__': + main() From f0786f825b1ac144ddf7dd67fb7fcadc17c25679 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 00:56:43 -0700 Subject: [PATCH 032/254] Simplified main() function. --- bin/pythonpy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index b2595e5..502f0ba 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -132,8 +132,7 @@ class PythonPy(object): def main(): - pythonpy = PythonPy() - pythonpy.run() + PythonPy().run() if __name__ == '__main__': From e4e16b17ba9098c2455ab0b20b12a03e4fdbe22e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 01:01:51 -0700 Subject: [PATCH 033/254] 0.2.2dev5 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e7c4a88..7f73e93 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.2dev4', + version='0.2.2dev5', description='Take advantage of your python skills from the command line', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From fa9733726d65fdfdf40119b156c83aac55e0bff0 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 01:07:09 -0700 Subject: [PATCH 034/254] 0.2.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7f73e93..0adc04b 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.2dev5', + version='0.2.2', description='Take advantage of your python skills from the command line', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From f68e6949c2fcb12571c8e90d72b540cbc912278b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 01:51:54 -0700 Subject: [PATCH 035/254] Rearranged imports in alphabetical order --- bin/pythonpy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index 502f0ba..5adf9a7 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -53,7 +53,12 @@ class PythonPy(object): def lazy_imports(self): query = (self.args.evaluation if self.args.evaluation else '' + self.args.cmd if self.args.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 'glob' in query: global glob; import glob + if 'hashlib' in query: global hashlib; import hashlib if 'itertools' in query: global itertools; import itertools if 'json' in query: global json; import json if 'math' in query: global math; import math @@ -62,11 +67,6 @@ class PythonPy(object): 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 'datetime' in query: global datetime; import datetime - if 'hashlib' in query: global hashlib; import hashlib - if 'csv' in query: global csv; import csv - if 'base64' in query: global base64; import base64 - if 'calendar' in query: global calendar; import calendar if 'Counter' in query: global Counter; from collections import Counter if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict if 'uuid4' in query: global uuid4; from uuid import uuid4 From 22bd488995026e4c79290f815bcb9f1afa579c86 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 01:52:06 -0700 Subject: [PATCH 036/254] Rearranged imports in alphabetical order --- bin/pythonpy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pythonpy b/bin/pythonpy index 5adf9a7..abcbdf0 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -2,9 +2,9 @@ from __future__ import (unicode_literals, absolute_import, print_function, division) import argparse -import sys import json import re +import sys class PythonPy(object): def __init__(self): From a605a600c515466aa3e37b732486a531967cf7d4 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 01:54:13 -0700 Subject: [PATCH 037/254] Removed redundant re and json lazy import. --- bin/pythonpy | 2 -- 1 file changed, 2 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index abcbdf0..0fd2364 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -60,11 +60,9 @@ class PythonPy(object): if 'glob' in query: global glob; import glob if 'hashlib' in query: global hashlib; import hashlib 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 From ea1adfa8423d019db7b69ea96145eb7ceab9da1a Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 02:01:47 -0700 Subject: [PATCH 038/254] Mentioned the wiki at the end of the README. --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 1402504..a8ed179 100644 --- a/README.rst +++ b/README.rst @@ -227,3 +227,7 @@ Get the local network ip 192.168.1.41 :: + + +Check out the wiki at github.com/Russell91/pythonpy/wiki for more advanced features +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From f41ef15851f6a768aa0db12edbd4a83d4aa6b30d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 02:02:42 -0700 Subject: [PATCH 039/254] Made wiki mention more optional. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a8ed179..a077e1c 100644 --- a/README.rst +++ b/README.rst @@ -229,5 +229,5 @@ Get the local network ip :: -Check out the wiki at github.com/Russell91/pythonpy/wiki for more advanced features +If you haven't had enough yet, check out the wiki at github.com/Russell91/pythonpy/wiki for more advanced features ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From e7a554233b6f5d5fb8fdf3a389c7d7467d93fe86 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 02:42:03 -0700 Subject: [PATCH 040/254] Fixed bug neutralizing -c argument. --- bin/pythonpy | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index 0fd2364..de468f8 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -12,11 +12,9 @@ class PythonPy(object): def run(self): self.args = self.parser.parse_args() - stdin = self.preprocess() + self.stdin = self.preprocess() self.lazy_imports() - if self.args.cmd: - exec(self.args.cmd) - results = self.process(stdin) + results = self.process() self.post_process(results) def get_parser(self): @@ -70,6 +68,7 @@ class PythonPy(object): if 'uuid4' in query: global uuid4; from uuid import uuid4 def preprocess(self): + if self.args.json_input: stdin = (json.loads(x.rstrip()) for x in sys.stdin) elif self.args.split_input: @@ -83,25 +82,28 @@ class PythonPy(object): self.args.cmd = self.args.cmd.replace("`", "'") return stdin - def process(self, stdin): - def safe_eval(text, x): - try: - return eval(text) - except: - return None + def safe_eval(self, text, x): + try: + return eval(text) + except: + return None + + def process(self): + if self.args.cmd: + exec self.args.cmd in globals(), locals() if self.args.lines_of_stdin: if self.args.ignore_exceptions: - result = (safe_eval(self.args.evaluation, x) for x in stdin) + result = (self.safe_eval(self.args.evaluation, x) for x in self.stdin) else: - result = (eval(self.args.evaluation) for x in stdin) + result = (eval(self.args.evaluation) for x in self.stdin) elif self.args.filter_result: if self.args.ignore_exceptions: - result = (x for x in stdin if safe_eval(self.args.evaluation, x)) + result = (x for x in self.stdin if self.safe_eval(self.args.evaluation, x)) else: - result = (x for x in stdin if eval(self.args.evaluation)) + result = (x for x in self.stdin if eval(self.args.evaluation)) elif self.args.list_of_stdin: - l = list(stdin) + l = list(self.stdin) result = eval(self.args.evaluation) else: result = eval(self.args.evaluation) From 945e7d3c04e35da2febf627349d63bc685a72b99 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 02:42:23 -0700 Subject: [PATCH 041/254] 0.2.3dev --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0adc04b..3f6475b 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.2', + version='0.2.3dev', description='Take advantage of your python skills from the command line', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From 4628b8878e669076d9e2d127c0eb151c34e60f40 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 11:42:35 -0700 Subject: [PATCH 042/254] Changed help description for --si. --- bin/pythonpy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pythonpy b/bin/pythonpy index de468f8..42b07f6 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -35,7 +35,7 @@ class PythonPy(object): const=True, default=False, help='') parser.add_argument('--si', '--split_input', dest='split_input', - help='pre-process each row with re.split(delimiter)') + 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', From fc6f6f276d968c55899e651e96ab36bdf30dda5d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 15:53:15 -0700 Subject: [PATCH 043/254] Got rid of OOP as it was messing up exec function. --- bin/pythonpy | 246 +++++++++++++++++++++++---------------------------- setup.py | 2 +- 2 files changed, 114 insertions(+), 134 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index 42b07f6..a1826b1 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -1,139 +1,119 @@ -#!/usr/bin/env python +#!/usr/bin/python from __future__ import (unicode_literals, absolute_import, print_function, division) import argparse +import sys import json import re -import sys - -class PythonPy(object): - def __init__(self): - self.parser = self.get_parser() - - def run(self): - self.args = self.parser.parse_args() - self.stdin = self.preprocess() - self.lazy_imports() - results = self.process() - self.post_process(results) - - def get_parser(self): - parser = argparse.ArgumentParser() - parser.add_argument('evaluation', 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='cmd', help='run code before 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)') - return parser - - def lazy_imports(self): - query = (self.args.evaluation if self.args.evaluation else '' + - self.args.cmd if self.args.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 'glob' in query: global glob; import glob - if 'hashlib' in query: global hashlib; import hashlib - if 'itertools' in query: global itertools; import itertools - 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 '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 'uuid4' in query: global uuid4; from uuid import uuid4 - - def preprocess(self): - - if self.args.json_input: - stdin = (json.loads(x.rstrip()) for x in sys.stdin) - elif self.args.split_input: - stdin = (re.split(self.args.split_input, x.rstrip()) for x in sys.stdin) - else: - stdin = (x.rstrip() for x in sys.stdin) - - if self.args.evaluation: - self.args.evaluation = self.args.evaluation.replace("`", "'") - if self.args.cmd: - self.args.cmd = self.args.cmd.replace("`", "'") - return stdin - - def safe_eval(self, text, x): - try: - return eval(text) - except: - return None - - def process(self): - if self.args.cmd: - exec self.args.cmd in globals(), locals() - - if self.args.lines_of_stdin: - if self.args.ignore_exceptions: - result = (self.safe_eval(self.args.evaluation, x) for x in self.stdin) - else: - result = (eval(self.args.evaluation) for x in self.stdin) - elif self.args.filter_result: - if self.args.ignore_exceptions: - result = (x for x in self.stdin if self.safe_eval(self.args.evaluation, x)) - else: - result = (x for x in self.stdin if eval(self.args.evaluation)) - elif self.args.list_of_stdin: - l = list(self.stdin) - result = eval(self.args.evaluation) - else: - result = eval(self.args.evaluation) - return result - - def post_process(self, results): - def format(output): - if output == None: - return None - elif self.args.json_output: - return json.dumps(output) - elif self.args.split_output: - return self.args.split_output.join(output) - else: - return output - - if hasattr(results, '__iter__'): - for x in results: - formatted = format(x) - if formatted is not None: - print(formatted) - else: - formatted = format(results) - if formatted is not None: - print(formatted) - - -def main(): - PythonPy().run() - -if __name__ == '__main__': - main() +def lazy_imports(evaluation, cmd): + query = (evaluation if evaluation else '' + + cmd if cmd else '') + 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 'datetime' in query: global datetime; import datetime + if 'hashlib' in query: global hashlib; import hashlib + if 'csv' in query: global csv; import csv + if 'base64' in query: global base64; import base64 + if 'calendar' in query: global calendar; import calendar + if 'Counter' in query: global Counter; from collections import Counter + if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict + if 'uuid4' in query: global uuid4; from uuid import uuid4 + +parser = argparse.ArgumentParser() +parser.add_argument('evaluation', 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='cmd', help='run code before 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)') +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.evaluation: + args.evaluation = args.evaluation.replace("`", "'") +if args.cmd: + args.cmd = args.cmd.replace("`", "'") + +lazy_imports(args.evaluation, args.cmd) + +if args.cmd: + exec(args.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.evaluation, x) for x in stdin) + else: + result = (eval(args.evaluation) for x in stdin) +elif args.filter_result: + if args.ignore_exceptions: + result = (x for x in stdin if safe_eval(args.evaluation, x)) + else: + result = (x for x in stdin if eval(args.evaluation)) +elif args.list_of_stdin: + l = list(stdin) + result = eval(args.evaluation) +else: + result = eval(args.evaluation) + +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) diff --git a/setup.py b/setup.py index 3f6475b..408921d 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.3dev', + version='0.2.4dev', description='Take advantage of your python skills from the command line', scripts=[os.path.join('bin', 'pythonpy')], license='MIT', From e74a2fc728f9379ef3ba7fadf9e5e4356470ae5d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 15:58:00 -0700 Subject: [PATCH 044/254] Rearranged imports and added from itertools import groupby. --- bin/pythonpy | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index a1826b1..c23f9f4 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -9,6 +9,11 @@ import re def lazy_imports(evaluation, cmd): query = (evaluation if evaluation else '' + cmd if 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 @@ -18,13 +23,9 @@ def lazy_imports(evaluation, cmd): 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 'datetime' in query: global datetime; import datetime - if 'hashlib' in query: global hashlib; import hashlib - if 'csv' in query: global csv; import csv - if 'base64' in query: global base64; import base64 - if 'calendar' in query: global calendar; import calendar 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 OrderedDict; from collections import OrderedDict if 'uuid4' in query: global uuid4; from uuid import uuid4 parser = argparse.ArgumentParser() From 7bd2f6403ff55832cf9fb85494f2b2d7bca1634c Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 16:01:28 -0700 Subject: [PATCH 045/254] Added -C option to run statement after expression. --- bin/pythonpy | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index c23f9f4..7cb3000 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -6,9 +6,10 @@ import sys import json import re -def lazy_imports(evaluation, cmd): +def lazy_imports(evaluation, pre_cmd, post_cmd): query = (evaluation if evaluation else '' + - cmd if cmd 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 @@ -39,7 +40,8 @@ parser.add_argument('-fx', dest='filter_result', action='store_const', 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='cmd', help='run code before expression') +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, @@ -68,13 +70,13 @@ else: if args.evaluation: args.evaluation = args.evaluation.replace("`", "'") -if args.cmd: - args.cmd = args.cmd.replace("`", "'") +if args.pre_cmd: + args.pre_cmd = args.pre_cmd.replace("`", "'") -lazy_imports(args.evaluation, args.cmd) +lazy_imports(args.evaluation, args.pre_cmd, args.post_cmd) -if args.cmd: - exec(args.cmd) +if args.pre_cmd: + exec(args.pre_cmd) def safe_eval(text, x): try: @@ -118,3 +120,6 @@ else: formatted = format(result) if formatted is not None: print(formatted) + +if args.post_cmd: + exec(args.post_cmd) From e9d683f4136f46950da939e467cc65f9ece84969 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 16:02:47 -0700 Subject: [PATCH 046/254] Removed README.txt, as it has been superceded by README.rst. --- README.txt | 104 ----------------------------------------------------- 1 file changed, 104 deletions(-) delete mode 100644 README.txt diff --git a/README.txt b/README.txt deleted file mode 100644 index e7e09f3..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 'l[::-1]' -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 - -# keep going if row raises Error with (--i). -# get the local network ip -$ ifconfig | py -x --i 're.search(r"192\.168[\d\.]+", x).group()' -192.168.1.41 From fc6b53f17a0cb31017f1cfd603ada50034e79555 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 16:07:17 -0700 Subject: [PATCH 047/254] Fixed groupby import typo. --- bin/pythonpy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pythonpy b/bin/pythonpy index 7cb3000..67facf3 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -26,7 +26,7 @@ def lazy_imports(evaluation, pre_cmd, post_cmd): 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 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() From 93e8911272d25af47c91b035d87af84d5e2a8e2b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 16:21:00 -0700 Subject: [PATCH 048/254] Fixed bug failing to import for -c/-C statements. --- bin/pythonpy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/pythonpy b/bin/pythonpy index 67facf3..86430d3 100755 --- a/bin/pythonpy +++ b/bin/pythonpy @@ -7,9 +7,9 @@ import json import re def lazy_imports(evaluation, pre_cmd, post_cmd): - query = (evaluation if evaluation else '' + - pre_cmd if pre_cmd else '' + - post_cmd if post_cmd else '') + query = ((evaluation if evaluation 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 From bfd99a2590e0f3676bb51d3f988d75d27a869a99 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 16:21:14 -0700 Subject: [PATCH 049/254] Added a few more unit tests!. --- test/test_pythonpy.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/test/test_pythonpy.py b/test/test_pythonpy.py index ad7043d..a1768e7 100644 --- a/test/test_pythonpy.py +++ b/test/test_pythonpy.py @@ -11,13 +11,40 @@ def test_numbers(self): def test_range(self): self.assertEqual(check_output(['pythonpy', 'range(3)']), '\n'.join(map(str, range(3))) + '\n') - def test_range(self): + 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__': From 724a6a16e48d22dd7d50d337118ffbd6f6222eac Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 16:39:17 -0700 Subject: [PATCH 050/254] Moved pythonpy out of bin into primary directory. --- bin/pythonpy => pythonpy | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename bin/pythonpy => pythonpy (99%) diff --git a/bin/pythonpy b/pythonpy similarity index 99% rename from bin/pythonpy rename to pythonpy index 86430d3..55b801b 100755 --- a/bin/pythonpy +++ b/pythonpy @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python from __future__ import (unicode_literals, absolute_import, print_function, division) import argparse diff --git a/setup.py b/setup.py index 408921d..3bc5b39 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name='pythonpy', version='0.2.4dev', description='Take advantage of your python skills from the command line', - scripts=[os.path.join('bin', 'pythonpy')], + scripts=['pythonpy'], license='MIT', url='https://github.com/Russell91/pythonpy', long_description='', From 9cc9f85c1bf51f3016cd486afff9f8e3251bd68d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 16:58:48 -0700 Subject: [PATCH 051/254] Changed "evaluation" to "expression" in source code. --- pythonpy | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/pythonpy b/pythonpy index 55b801b..0968d53 100755 --- a/pythonpy +++ b/pythonpy @@ -6,8 +6,8 @@ import sys import json import re -def lazy_imports(evaluation, pre_cmd, post_cmd): - query = ((evaluation if evaluation else '') + +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 @@ -29,8 +29,10 @@ def lazy_imports(evaluation, pre_cmd, post_cmd): if 'groupby' in query: global groupby; from itertools import groupby if 'uuid4' in query: global uuid4; from uuid import uuid4 -parser = argparse.ArgumentParser() -parser.add_argument('evaluation', nargs='?', default='None') +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') @@ -68,12 +70,12 @@ elif args.split_input: else: stdin = (x.rstrip() for x in sys.stdin) -if args.evaluation: - args.evaluation = args.evaluation.replace("`", "'") +if args.expression: + args.expression = args.expression.replace("`", "'") if args.pre_cmd: args.pre_cmd = args.pre_cmd.replace("`", "'") -lazy_imports(args.evaluation, args.pre_cmd, args.post_cmd) +lazy_imports(args.expression, args.pre_cmd, args.post_cmd) if args.pre_cmd: exec(args.pre_cmd) @@ -86,19 +88,19 @@ def safe_eval(text, x): if args.lines_of_stdin: if args.ignore_exceptions: - result = (safe_eval(args.evaluation, x) for x in stdin) + result = (safe_eval(args.expression, x) for x in stdin) else: - result = (eval(args.evaluation) for x in stdin) + 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.evaluation, x)) + result = (x for x in stdin if safe_eval(args.expression, x)) else: - result = (x for x in stdin if eval(args.evaluation)) + result = (x for x in stdin if eval(args.expression)) elif args.list_of_stdin: l = list(stdin) - result = eval(args.evaluation) + result = eval(args.expression) else: - result = eval(args.evaluation) + result = eval(args.expression) def format(output): if output == None: From e8e80e200ed6e3e6868aed7a9863f914ac26f6de Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 16:59:58 -0700 Subject: [PATCH 052/254] Changed help for --si. --- pythonpy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonpy b/pythonpy index 0968d53..bbd25c9 100755 --- a/pythonpy +++ b/pythonpy @@ -49,7 +49,7 @@ parser.add_argument('--i', '--ignore_exceptions', const=True, default=False, help='') parser.add_argument('--si', '--split_input', dest='split_input', - help='pre-process each row with re.split(delimiter)') + 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', From 5483b405524d7ad49508a87f1682e6ad6f9b3f86 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 17:01:52 -0700 Subject: [PATCH 053/254] 0.2.4dev2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3bc5b39..b36866f 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.4dev', + version='0.2.4dev2', description='Take advantage of your python skills from the command line', scripts=['pythonpy'], license='MIT', From 27d5adb13d374a072614abc230241006434495af Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 17:35:35 -0700 Subject: [PATCH 054/254] Updated README.rst. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index a077e1c..b7d71e2 100644 --- a/README.rst +++ b/README.rst @@ -229,5 +229,5 @@ Get the local network ip :: -If you haven't had enough yet, check out the wiki at github.com/Russell91/pythonpy/wiki for more advanced features -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If you haven't had enough yet, check out the wiki at github.com/Russell91/pythonpy/wiki +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From b2089bcd759ca641cc109dca4b6afc0df863ab0f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 18:37:09 -0700 Subject: [PATCH 055/254] 0.2.4 --- MANIFEST | 3 +-- setup.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/MANIFEST b/MANIFEST index c217127..acd664e 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,5 +1,4 @@ # file GENERATED by distutils, do NOT edit -README.txt +pythonpy setup.py -bin/pythonpy test/test_pythonpy.py diff --git a/setup.py b/setup.py index b36866f..3b1bd9e 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.4dev2', + version='0.2.4', description='Take advantage of your python skills from the command line', scripts=['pythonpy'], license='MIT', From 5315dbaee5b7df0ccc762309a8654393402745d7 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 20:20:40 -0700 Subject: [PATCH 056/254] Stopped tracking auto-generated MANIFEST with git. --- .gitignore | 1 + MANIFEST | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 MANIFEST diff --git a/.gitignore b/.gitignore index 1bcc247..35379c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ dist *.pyc *swp +MANIFEST diff --git a/MANIFEST b/MANIFEST deleted file mode 100644 index acd664e..0000000 --- a/MANIFEST +++ /dev/null @@ -1,4 +0,0 @@ -# file GENERATED by distutils, do NOT edit -pythonpy -setup.py -test/test_pythonpy.py From f870ef1b89ee7ec4eb055fa94766535bf0ec7639 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 20:25:10 -0700 Subject: [PATCH 057/254] Changed wiki to link. --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b7d71e2..024b11f 100644 --- a/README.rst +++ b/README.rst @@ -229,5 +229,6 @@ Get the local network ip :: -If you haven't had enough yet, check out the wiki at github.com/Russell91/pythonpy/wiki +If you haven't had enough yet, check out the `wiki `__ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + From d9e60f684a9607ccfcf422c01d23a540e0f9b563 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 20:26:52 -0700 Subject: [PATCH 058/254] Changed wiki link again --- README.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 024b11f..c2ec0ac 100644 --- a/README.rst +++ b/README.rst @@ -229,6 +229,5 @@ Get the local network ip :: -If you haven't had enough yet, check out the `wiki `__ -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - +If you haven't had enough yet, check out the `wiki `__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 445b193fccbe99e23db1b81dbcbb5e1631f671b0 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 20:39:54 -0700 Subject: [PATCH 059/254] Added letter count example to README. --- README.rst | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.rst b/README.rst index c2ec0ac..06ad488 100644 --- a/README.rst +++ b/README.rst @@ -215,6 +215,42 @@ Get long palindromes :: +Count words beginning with each letter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + $ cat /usr/share/dict/words | py -x 'x[0].lower()' | py -l 'Counter(l).items()' + ('\xc3', 17) + ('a', 5895) + ('c', 9521) + ('b', 6068) + ('e', 3851) + ('d', 5823) + ('g', 3456) + ('f', 4127) + ('i', 3667) + ('h', 3804) + ('k', 1163) + ('j', 1262) + ('m', 5922) + ('l', 3400) + ('o', 2270) + ('n', 2018) + ('q', 467) + ('p', 7659) + ('s', 11327) + ('r', 5356) + ('u', 1943) + ('t', 5085) + ('w', 2739) + ('v', 1576) + ('y', 421) + ('x', 53) + ('z', 281) + +:: + Keep going if some rows raise Errors with (--i). ------------------------------------------------ From 60b084ccf4506e1a95afa975bf77a6cc9af512b8 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 20:41:32 -0700 Subject: [PATCH 060/254] Shortened letter count example in README. --- README.rst | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/README.rst b/README.rst index 06ad488..c662756 100644 --- a/README.rst +++ b/README.rst @@ -220,34 +220,12 @@ Count words beginning with each letter :: - $ cat /usr/share/dict/words | py -x 'x[0].lower()' | py -l 'Counter(l).items()' - ('\xc3', 17) - ('a', 5895) + $ 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) - ('e', 3851) - ('d', 5823) - ('g', 3456) - ('f', 4127) - ('i', 3667) - ('h', 3804) - ('k', 1163) - ('j', 1262) ('m', 5922) - ('l', 3400) - ('o', 2270) - ('n', 2018) - ('q', 467) - ('p', 7659) - ('s', 11327) - ('r', 5356) - ('u', 1943) - ('t', 5085) - ('w', 2739) - ('v', 1576) - ('y', 421) - ('x', 53) - ('z', 281) :: From 77f425371851afad731009fc50dae9b847d46899 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 13 Jun 2014 20:48:20 -0700 Subject: [PATCH 061/254] Fixed bug not replacing tics inside post_cmd. --- pythonpy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pythonpy b/pythonpy index bbd25c9..6923642 100755 --- a/pythonpy +++ b/pythonpy @@ -74,6 +74,8 @@ 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) From 7e263c6e5f43526d0ab44010ea0d4f217b7306c4 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 19:23:52 -0700 Subject: [PATCH 062/254] Added ./py so alias is unnecessary. --- py | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100755 py diff --git a/py b/py new file mode 100755 index 0000000..6923642 --- /dev/null +++ b/py @@ -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 3b1bd9e..199da5c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name='pythonpy', version='0.2.4', description='Take advantage of your python skills from the command line', - scripts=['pythonpy'], + scripts=['pythonpy', 'py'], license='MIT', url='https://github.com/Russell91/pythonpy', long_description='', From 3a681112b8ff3710ecdd7499782bfd764ba49f18 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 19:24:09 -0700 Subject: [PATCH 063/254] 0.2.5dev --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 199da5c..8857a28 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.4', + version='0.2.5dev', description='Take advantage of your python skills from the command line', scripts=['pythonpy', 'py'], license='MIT', From aa8a9edea6c706027e379006154b2dda7e628f05 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 19:31:40 -0700 Subject: [PATCH 064/254] Added wpy for windows --- wpy | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100755 wpy diff --git a/wpy b/wpy new file mode 100755 index 0000000..6923642 --- /dev/null +++ b/wpy @@ -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) From ccf55930ad63e3df94bac9e7ea1d09a650a2688d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 19:33:14 -0700 Subject: [PATCH 065/254] Removed defunct alias info from readme. --- README.rst | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index c662756..70cf076 100644 --- a/README.rst +++ b/README.rst @@ -3,15 +3,7 @@ Installation :: - sudo pip install pythonpy && alias py='pythonpy' - -:: - -For a permanent alias (For Bash users): - -:: - - echo "alias py='pythonpy'" >> ~/.bashrc + sudo pip install pythonpy :: @@ -20,7 +12,7 @@ Float Arithmetic :: - $ py '3 * 1.5' + $ py '3 * 1.5' # use wpy on windows 4.5 From 878f87ad77c7cc76a265b1b333395efcc9bfbacc Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 19:33:46 -0700 Subject: [PATCH 066/254] 0.2.5dev2. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 8857a28..627bc77 100644 --- a/setup.py +++ b/setup.py @@ -3,9 +3,9 @@ setup( name='pythonpy', - version='0.2.5dev', + version='0.2.5dev2', description='Take advantage of your python skills from the command line', - scripts=['pythonpy', 'py'], + scripts=['pythonpy', 'py', 'wpy'], license='MIT', url='https://github.com/Russell91/pythonpy', long_description='', From aae781c4406465d66dc637bb84f257b3f8c40f24 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 19:36:29 -0700 Subject: [PATCH 067/254] 0.2.5 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 627bc77..a18a994 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.5dev2', + version='0.2.5', description='Take advantage of your python skills from the command line', scripts=['pythonpy', 'py', 'wpy'], license='MIT', From 4df631752b12ecb2ba7c97f793b52355392e0090 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 19:40:05 -0700 Subject: [PATCH 068/254] Updated README style. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 70cf076..ee19fc4 100644 --- a/README.rst +++ b/README.rst @@ -12,7 +12,7 @@ Float Arithmetic :: - $ py '3 * 1.5' # use wpy on windows + $ py '3 * 1.5' # For Windows use: wpy '3 * 1.5' 4.5 From 472569888b65335c45e6ea651257074be058f37b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 19:41:42 -0700 Subject: [PATCH 069/254] Moved alternat binary names to bin to keep home folder clean. --- pythonpy => bin/pythonpy | 0 wpy => bin/wpy | 0 setup.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename pythonpy => bin/pythonpy (100%) rename wpy => bin/wpy (100%) diff --git a/pythonpy b/bin/pythonpy similarity index 100% rename from pythonpy rename to bin/pythonpy diff --git a/wpy b/bin/wpy similarity index 100% rename from wpy rename to bin/wpy diff --git a/setup.py b/setup.py index a18a994..33475d1 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name='pythonpy', version='0.2.5', description='Take advantage of your python skills from the command line', - scripts=['pythonpy', 'py', 'wpy'], + scripts=['bin/pythonpy', 'py', 'bin/wpy'], license='MIT', url='https://github.com/Russell91/pythonpy', long_description='', From 4faa4bfc514728282a7d00f79d826348d03f0777 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 20:54:49 -0700 Subject: [PATCH 070/254] temp remove of py. --- py | 129 ------------------------------------------------------------- 1 file changed, 129 deletions(-) delete mode 100755 py diff --git a/py b/py deleted file mode 100755 index 6923642..0000000 --- a/py +++ /dev/null @@ -1,129 +0,0 @@ -#!/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) From 43d748392751a2d561de36361bca1425c007510d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 20:55:09 -0700 Subject: [PATCH 071/254] Moved pythonpy to py. --- bin/pythonpy => py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bin/pythonpy => py (100%) diff --git a/bin/pythonpy b/py similarity index 100% rename from bin/pythonpy rename to py From cc63cd7a8d20438530f5e7e7da1393073181c5ed Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 16 Jun 2014 20:57:49 -0700 Subject: [PATCH 072/254] Changed unittests to use "py". --- setup.py | 2 +- test/test_pythonpy.py | 56 +++++++++++++++++++++---------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/setup.py b/setup.py index 33475d1..e502dbf 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name='pythonpy', version='0.2.5', description='Take advantage of your python skills from the command line', - scripts=['bin/pythonpy', 'py', 'bin/wpy'], + scripts=['py', 'bin/wpy'], license='MIT', url='https://github.com/Russell91/pythonpy', long_description='', diff --git a/test/test_pythonpy.py b/test/test_pythonpy.py index a1768e7..ab4f349 100644 --- a/test/test_pythonpy.py +++ b/test/test_pythonpy.py @@ -3,48 +3,48 @@ class TestPythonPy(unittest.TestCase): def test_empty(self): - self.assertEqual(check_output(['pythonpy']),'') + self.assertEqual(check_output(['py']),'') def test_numbers(self): - self.assertEqual(check_output(['pythonpy', '3 * 4.5']),'13.5\n') + self.assertEqual(check_output(['py', '3 * 4.5']),'13.5\n') def test_range(self): - self.assertEqual(check_output(['pythonpy', 'range(3)']), '\n'.join(map(str, range(3))) + '\n') + self.assertEqual(check_output(['py', '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') + self.assertEqual(check_output(["""echo a,b | py -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') + self.assertEqual(check_output(["""echo abc | py -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), '') + self.assertEqual(check_output("""echo a | py -x --i 'None.None'""", shell=True), '') + self.assertEqual(check_output("""echo a | py -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') + self.assertEqual(check_output("""py -c 'a=5' -C 'print(a)'""", shell=True), '5\n') + self.assertEqual(check_output("""echo 3 | py -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) + check_output("py 'math'", shell=True) + check_output("py 'base64'", shell=True) + check_output("py 'calendar'", shell=True) + check_output("py 'csv'", shell=True) + check_output("py 'datetime'", shell=True) + check_output("py 'hashlib'", shell=True) + check_output("py 'glob'", shell=True) + check_output("py 'itertools'", shell=True) + check_output("py 'json'", shell=True) + check_output("py 'math'", shell=True) + check_output("py 'os'", shell=True) + check_output("py 'random'", shell=True) + check_output("py 're'", shell=True) + check_output("py 'shutil'", shell=True) + check_output("py 'tempfile'", shell=True) + check_output("py -c 'Counter'", shell=True) + check_output("py -c 'OrderedDict'", shell=True) + check_output("py -c 'groupby'", shell=True) + check_output("py 'uuid4'", shell=True) if __name__ == '__main__': From 792c3b36faa631b236020e57f29a207e38446d80 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 17 Jun 2014 23:57:48 -0700 Subject: [PATCH 073/254] Fixed bug where piping to head caused IOError. --- py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/py b/py index 6923642..ee1ec45 100755 --- a/py +++ b/py @@ -1,6 +1,9 @@ #!/usr/bin/env python from __future__ import (unicode_literals, absolute_import, print_function, division) +from signal import signal, SIGPIPE, SIG_DFL +signal(SIGPIPE,SIG_DFL) + import argparse import sys import json From 5d8d3df1d91c101f391f1274f25e502eea9b2eb0 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 00:20:23 -0700 Subject: [PATCH 074/254] Added command line completion!. --- extras/pycompleter | 83 ++++++++++++++++++++++++++++++++++++++++++ extras/pycompletion.sh | 7 ++++ setup.py | 5 ++- 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100755 extras/pycompleter create mode 100644 extras/pycompletion.sh diff --git a/extras/pycompleter b/extras/pycompleter new file mode 100755 index 0000000..8b4ba66 --- /dev/null +++ b/extras/pycompleter @@ -0,0 +1,83 @@ +#!/usr/bin/env python +from __future__ import (unicode_literals, absolute_import, + print_function, division) +import argparse +import sys +import json +import re +import base64 +import calendar +import csv +import datetime +import hashlib +import glob +import itertools +import json +import math +import os +import random +import re +import shutil +import tempfile +from collections import Counter +from collections import OrderedDict +from itertools import groupby +from uuid import uuid4 + +#if args.pre_cmd: + #exec(args.pre_cmd) + +#if args.post_cmd: + #exec(args.post_cmd) + +import rlcompleter + + +def complete_all(c, s, idx): + global x + global l + x = str() + l = list() + if c.complete(s, idx): + return [c.complete(s, idx)] + complete_all(c, s, idx + 1) + return [] + + +def parse_string(prefix, raw_input): + if prefix == '': + return ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'glob', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil', 'tempfile'] + elif prefix.startswith('--') and not any(q in raw_input for q in ["'", '"']): + return ['--si', '--so', '--ji', '--jo', '--i'] + elif prefix.startswith('-') and not any(q in raw_input for q in ["'", '"']): + return ['-h', '-x', '-fx', '-l', '-c', '-C'] + c = rlcompleter.Completer() + options = list(set(complete_all(c, prefix, 0))) + if prefix.endswith('.'): + options = [x for x in options if '_' not in x] + return options + + + +def parse_input(raw_input): + input = raw_input.rstrip().split(' ')[-1] + if len(input) > 0 and input[0] in ['"', "'"]: + input = input[1:] + return input + + +def main(): + raw_input = next(sys.stdin) + prefix = parse_input(raw_input) + options = parse_string(prefix, raw_input) + + with open('/tmp/a.txt', 'w') as f: + f.write(raw_input + '\n') + f.write(prefix + '\n') + for x in options: + f.write(x + '\n') + if len(options) <= 1: + options = options + [x + "'" for x in options] + print(' '.join(options)) + +if __name__ == '__main__': + main() diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh new file mode 100644 index 0000000..7692e60 --- /dev/null +++ b/extras/pycompletion.sh @@ -0,0 +1,7 @@ +_py() +{ + local cur=${COMP_WORDS[COMP_CWORD]} + COMPREPLY=($(echo ${COMP_WORDS[@]:1} | pycompleter)) +} + +complete -F _py -o nospace py diff --git a/setup.py b/setup.py index e502dbf..e2c4956 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,10 @@ name='pythonpy', version='0.2.5', description='Take advantage of your python skills from the command line', - scripts=['py', 'bin/wpy'], + scripts=['py', 'extras/pycompleter', 'bin/wpy'], + data_files=[ + ('/etc/bash_completion.d', ['extras/pycompletion.sh']), + ], license='MIT', url='https://github.com/Russell91/pythonpy', long_description='', From 099647bbd4e1bbacc53f489f0e4dd2c2bc02ff56 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 00:20:51 -0700 Subject: [PATCH 075/254] Moved wpy to new extras folder. --- {bin => extras}/wpy | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {bin => extras}/wpy (100%) diff --git a/bin/wpy b/extras/wpy similarity index 100% rename from bin/wpy rename to extras/wpy From 04070e6736af688b8a871edfbbef52099d734d4f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 00:22:08 -0700 Subject: [PATCH 076/254] Moved wpy to new extras folder. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e2c4956..d4e07ee 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name='pythonpy', version='0.2.5', description='Take advantage of your python skills from the command line', - scripts=['py', 'extras/pycompleter', 'bin/wpy'], + scripts=['py', 'extras/pycompleter', 'extras/wpy'], data_files=[ ('/etc/bash_completion.d', ['extras/pycompletion.sh']), ], From c4a39da92b1750ad6777a43c8af72fac52108c28 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 00:35:50 -0700 Subject: [PATCH 077/254] 0.2.6dev --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d4e07ee..0077b05 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='pythonpy', - version='0.2.5', + version='0.2.6dev', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/pycompleter', 'extras/wpy'], data_files=[ From 52076e92ad5fb76ca22732cde9a1947bb5adefd5 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 00:52:28 -0700 Subject: [PATCH 078/254] Removed some less common modules from completion list. --- extras/pycompleter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index 8b4ba66..4640b31 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -45,7 +45,7 @@ def complete_all(c, s, idx): def parse_string(prefix, raw_input): if prefix == '': - return ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'glob', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil', 'tempfile'] + return ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] elif prefix.startswith('--') and not any(q in raw_input for q in ["'", '"']): return ['--si', '--so', '--ji', '--jo', '--i'] elif prefix.startswith('-') and not any(q in raw_input for q in ["'", '"']): From 6a86f460aa57eed5c20b765ca14bbb88fa9826af Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 01:22:50 -0700 Subject: [PATCH 079/254] Ignoring tab completion if root is not used. --- setup.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 0077b05..abe3346 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,20 @@ from distutils.core import setup import os +try: + with open('/etc/bash_completion.d/pycompletion.sh', 'w') eo: + eo.write('test') + data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] +except error: + print 'User does not have write access to /etc completion will not work' + data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] + setup( name='pythonpy', version='0.2.6dev', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/pycompleter', 'extras/wpy'], - data_files=[ - ('/etc/bash_completion.d', ['extras/pycompletion.sh']), - ], + data_files=data_files, license='MIT', url='https://github.com/Russell91/pythonpy', long_description='', From 423d2354a8b4b84662593bfc6884bc4a6fb5bd51 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 01:25:10 -0700 Subject: [PATCH 080/254] 0.2.6dev2 --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index abe3346..612801f 100644 --- a/setup.py +++ b/setup.py @@ -2,16 +2,16 @@ import os try: - with open('/etc/bash_completion.d/pycompletion.sh', 'w') eo: + with open('/etc/bash_completion.d/pycompletion.sh', 'w') as eo: eo.write('test') data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] -except error: +except: print 'User does not have write access to /etc completion will not work' data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] setup( name='pythonpy', - version='0.2.6dev', + version='0.2.6dev2', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/pycompleter', 'extras/wpy'], data_files=data_files, From b275b09a62550e449f48161b9e180bf88cb14335 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 19:00:02 -0700 Subject: [PATCH 081/254] Removed dead $cur reference from pycompletion.sh. --- extras/pycompletion.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index 7692e60..7a32916 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -1,6 +1,5 @@ _py() { - local cur=${COMP_WORDS[COMP_CWORD]} COMPREPLY=($(echo ${COMP_WORDS[@]:1} | pycompleter)) } From e489210312cb7e6ddd2dfd6e553440a2806ff0f5 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 19:03:33 -0700 Subject: [PATCH 082/254] 0.2.6dev3 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 612801f..fdeb4a6 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ try: with open('/etc/bash_completion.d/pycompletion.sh', 'w') as eo: - eo.write('test') + eo.write('test2') data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] except: print 'User does not have write access to /etc completion will not work' @@ -11,7 +11,7 @@ setup( name='pythonpy', - version='0.2.6dev2', + version='0.2.6dev3', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/pycompleter', 'extras/wpy'], data_files=data_files, From da47b0f1c71b564f22af7c467676ee26a2cbd4e4 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 19:07:39 -0700 Subject: [PATCH 083/254] 0.2.6dev4 --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index fdeb4a6..3515996 100644 --- a/setup.py +++ b/setup.py @@ -2,8 +2,8 @@ import os try: - with open('/etc/bash_completion.d/pycompletion.sh', 'w') as eo: - eo.write('test2') + with open('/etc/bash_completion.d/pycompletion_test.sh', 'w') as eo: + eo.write('test3') data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] except: print 'User does not have write access to /etc completion will not work' @@ -11,7 +11,7 @@ setup( name='pythonpy', - version='0.2.6dev3', + version='0.2.6dev4', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/pycompleter', 'extras/wpy'], data_files=data_files, From 323974a08742c567feeae24cf47db5ea1de58a4e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 19:11:50 -0700 Subject: [PATCH 084/254] 0.2.6dev5 --- setup.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 3515996..973fd48 100644 --- a/setup.py +++ b/setup.py @@ -1,17 +1,15 @@ from distutils.core import setup import os -try: - with open('/etc/bash_completion.d/pycompletion_test.sh', 'w') as eo: - eo.write('test3') +if os.geteuid() == 0: data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] -except: +else: print 'User does not have write access to /etc completion will not work' data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] setup( name='pythonpy', - version='0.2.6dev4', + version='0.2.6dev5', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/pycompleter', 'extras/wpy'], data_files=data_files, From dbf9b0fd2a1f4074c3abcc44106dc2136360da96 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 19:23:54 -0700 Subject: [PATCH 085/254] 0.3.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 973fd48..eddaf48 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='pythonpy', - version='0.2.6dev5', + version='0.3.0', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/pycompleter', 'extras/wpy'], data_files=data_files, From 2821f897e6c255647f36d11c3d18317fbfa2fc07 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 18 Jun 2014 19:26:43 -0700 Subject: [PATCH 086/254] Added bash refresh to install command in README. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ee19fc4..cb42d99 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ Installation :: - sudo pip install pythonpy + sudo pip install pythonpy && bash :: From 1af9b33b578f554b1226fa5268f40de669d9b82f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 19 Jun 2014 10:26:14 -0700 Subject: [PATCH 087/254] Added requirement isinstance(result, Iterable) for multi-line output. --- py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/py b/py index ee1ec45..294273e 100755 --- a/py +++ b/py @@ -8,6 +8,7 @@ import argparse import sys import json import re +from collections import Iterable def lazy_imports(expression, pre_cmd, post_cmd): query = ((expression if expression else '') + @@ -118,7 +119,7 @@ def format(output): return output -if hasattr(result, '__iter__'): +if isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstance(result, str): for x in result: formatted = format(x) if formatted is not None: From 939be417fdbe6992e3e003de362e6b5c10ff3b3e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 19 Jun 2014 10:54:40 -0700 Subject: [PATCH 088/254] Added docstring to wpy. --- extras/wpy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extras/wpy b/extras/wpy index 6923642..3548252 100755 --- a/extras/wpy +++ b/extras/wpy @@ -1,4 +1,8 @@ #!/usr/bin/env python +''' +Windows version of py. Still in development. +''' + from __future__ import (unicode_literals, absolute_import, print_function, division) import argparse From 56840815c8f9ba03ac9a8321be3791609f0b8f1e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 19 Jun 2014 10:59:15 -0700 Subject: [PATCH 089/254] Added py3 executable for use with python3. --- extras/py3 | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 2 files changed, 134 insertions(+), 1 deletion(-) create mode 100755 extras/py3 diff --git a/extras/py3 b/extras/py3 new file mode 100755 index 0000000..eb512f9 --- /dev/null +++ b/extras/py3 @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +from __future__ import (unicode_literals, absolute_import, + print_function, division) +from signal import signal, SIGPIPE, SIG_DFL +signal(SIGPIPE,SIG_DFL) + +import argparse +import sys +import json +import re +from collections import Iterable + +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 isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstance(result, str): + 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 eddaf48..fbc6e44 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ name='pythonpy', version='0.3.0', description='Take advantage of your python skills from the command line', - scripts=['py', 'extras/pycompleter', 'extras/wpy'], + scripts=['py', 'extras/py3', 'extras/pycompleter', 'extras/wpy'], data_files=data_files, license='MIT', url='https://github.com/Russell91/pythonpy', From 377f04a5f2c8f35fb744d9065ba64fbdbe15042c Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 19 Jun 2014 11:01:05 -0700 Subject: [PATCH 090/254] Added docstring to py3. --- extras/py3 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extras/py3 b/extras/py3 index eb512f9..c73835f 100755 --- a/extras/py3 +++ b/extras/py3 @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +''' +Just like py but using python3! +Tab completion not yet supported :(. +''' from __future__ import (unicode_literals, absolute_import, print_function, division) from signal import signal, SIGPIPE, SIG_DFL From 3eecdbeec65ce1d5c702a03e3f5a189937aa3530 Mon Sep 17 00:00:00 2001 From: Bartosz Marcinkowski Date: Thu, 19 Jun 2014 19:52:11 +0200 Subject: [PATCH 091/254] py3k installation fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index eddaf48..37718de 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ if os.geteuid() == 0: data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] else: - print 'User does not have write access to /etc completion will not work' + print('User does not have write access to /etc completion will not work') data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] setup( From def6a8be95c70042d791c831639c84f84552cdb3 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 19 Jun 2014 15:33:44 -0700 Subject: [PATCH 092/254] Menitioned tab completion in README. --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index cb42d99..81df47d 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,8 @@ Installation :: - sudo pip install pythonpy && bash + sudo pip install pythonpy + bash # restart your shell for tab completion to take effect :: From 9938d29d9fb3c692afbfcb5874eaad5a22dcbb96 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 19 Jun 2014 15:35:18 -0700 Subject: [PATCH 093/254] Changed spacing on README. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 81df47d..add2abb 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Installation :: sudo pip install pythonpy - bash # restart your shell for tab completion to take effect + bash # restart your shell for tab completion to take effect :: @@ -13,7 +13,7 @@ Float Arithmetic :: - $ py '3 * 1.5' # For Windows use: wpy '3 * 1.5' + $ py '3 * 1.5' # For Windows use: wpy '3 * 1.5' 4.5 From 3406d5657d3ead8e0cd3dcbbfbb27ad6b29b6b21 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 20 Jun 2014 21:30:08 -0700 Subject: [PATCH 094/254] Changed the flow of the readme. --- README.rst | 154 ++++++++++++++++++++--------------------------------- 1 file changed, 59 insertions(+), 95 deletions(-) diff --git a/README.rst b/README.rst index add2abb..cd1053b 100644 --- a/README.rst +++ b/README.rst @@ -8,29 +8,38 @@ Installation :: +py 'expression' ≅ python -c 'print(expression)' +----------------------------------------------- + Float Arithmetic ~~~~~~~~~~~~~~~~ :: - $ py '3 * 1.5' # For Windows use: wpy '3 * 1.5' + $ py '3 * 1.5' 4.5 - :: -Exponentiation -~~~~~~~~~~~~~~ +Access common imports directly +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: - $ py '7**3' - 343 + $ py 'math.exp(1)' + 2.71828182846 + + $ py 'random.random()' + 0.103173957713 + + $ py 'datetime.datetime.now()' + 2014-06-20 20:22:03.699290 + :: -Number sequence -~~~~~~~~~~~~~~~ +Lists are printed row by row +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: @@ -39,40 +48,13 @@ Number sequence 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 '[range(3)]' + [0, 1, 2] :: - $ py 'random.random()' - 0.103173957713 - -:: +py -x 'foo(x)' will apply foo to each line of input +--------------------------------------------------- Multiply each line of input by 7. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -92,51 +74,12 @@ Append ".txt" to each line of input :: $ py 'range(3)' | py -x 'x + ".txt"' - 0.txt 1.txt 2.txt + 3.txt :: -Sometimes you want to treat the input as a python list ------------------------------------------------------- - -Reverse a list -~~~~~~~~~~~~~~ - -:: - - $ py 'range(4)' | py -l 'l[::-1]' - 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 ~~~~~~~~~~~~~~~~~~~~~ @@ -150,8 +93,8 @@ Get only even numbers :: -The shorthand -fx (filter on x) is also available -------------------------------------------------- +py -fx 'predicate(x)' filters rows satisfying a condition +--------------------------------------------------------- Get only odd numbers ~~~~~~~~~~~~~~~~~~~~ @@ -208,33 +151,54 @@ Get long palindromes :: -Count words beginning with each letter -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +py -l will set l = list(sys.stdin) +------------------------------------------- + +Reverse the input +~~~~~~~~~~~~~~~~~ :: - $ 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) + $ py 'range(3)' | py -l 'l[::-1]' + 2 + 1 + 0 + +:: + +Sum the input +~~~~~~~~~~~~~ :: -Keep going if some rows raise Errors with (--i). ------------------------------------------------- + $ py 'range(3)' | py -l 'sum(int(x) for x in l)' + 3 + +:: -Get the local network ip +Count the lines of input ~~~~~~~~~~~~~~~~~~~~~~~~ :: - $ ifconfig | py -x --i 're.search(r"192\.168[\d\.]+", x).group()' - 192.168.1.41 + $ py 'range(17)' | py -l 'len(l)' + 17 + +:: + +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) + +:: If you haven't had enough yet, check out the `wiki `__ -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From caeacf122bffb95d8c7d0651072fcd2eb59d62a6 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 19 Jun 2014 23:46:30 -0700 Subject: [PATCH 095/254] Fixed some space bugs in tab completion. --- extras/pycompleter | 7 +------ extras/pycompletion.sh | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 4640b31..9097cf2 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -59,7 +59,7 @@ def parse_string(prefix, raw_input): def parse_input(raw_input): - input = raw_input.rstrip().split(' ')[-1] + input = raw_input.split(' ')[-1].rstrip() if len(input) > 0 and input[0] in ['"', "'"]: input = input[1:] return input @@ -70,11 +70,6 @@ def main(): prefix = parse_input(raw_input) options = parse_string(prefix, raw_input) - with open('/tmp/a.txt', 'w') as f: - f.write(raw_input + '\n') - f.write(prefix + '\n') - for x in options: - f.write(x + '\n') if len(options) <= 1: options = options + [x + "'" for x in options] print(' '.join(options)) diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index 7a32916..b1aff5b 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -1,6 +1,6 @@ _py() { - COMPREPLY=($(echo ${COMP_WORDS[@]:1} | pycompleter)) + COMPREPLY=($(echo "${COMP_WORDS[@]:1}" | pycompleter)) } complete -F _py -o nospace py From 515260fdc82bd8c0033e1a8727b02ddce947938b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 20 Jun 2014 00:33:56 -0700 Subject: [PATCH 096/254] Moving towards import completion. --- extras/pycompleter | 75 +++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 9097cf2..323b94b 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -24,51 +24,58 @@ from collections import OrderedDict from itertools import groupby from uuid import uuid4 -#if args.pre_cmd: - #exec(args.pre_cmd) - -#if args.post_cmd: - #exec(args.post_cmd) - import rlcompleter -def complete_all(c, s, idx): - global x - global l - x = str() - l = list() - if c.complete(s, idx): - return [c.complete(s, idx)] + complete_all(c, s, idx + 1) - return [] +def complete_all(prefix, c_arg): + if c_arg: + exec(c_arg) + idx = 0 + options_set = set() + completer = rlcompleter.Completer() + while completer.complete(prefix, idx): + options_set.add(completer.complete(prefix, idx)) + idx += 1 + return list(options_set) -def parse_string(prefix, raw_input): - if prefix == '': - return ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] - elif prefix.startswith('--') and not any(q in raw_input for q in ["'", '"']): - return ['--si', '--so', '--ji', '--jo', '--i'] +def parse_string(input, raw_input): + options = [] + prefix = input[-1].lstrip('"\'') + prior = input[:-1] + if prefix.startswith('--') and not any(q in raw_input for q in ["'", '"']): + options = ['--si', '--so', '--ji', '--jo', '--i'] elif prefix.startswith('-') and not any(q in raw_input for q in ["'", '"']): - return ['-h', '-x', '-fx', '-l', '-c', '-C'] - c = rlcompleter.Completer() - options = list(set(complete_all(c, prefix, 0))) - if prefix.endswith('.'): - options = [x for x in options if '_' not in x] + options = ['-h', '-x', '-fx', '-l', '-c', '-C'] + elif prefix == '': + options = ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] # +x,l + if '-x' in input[:-1] or '-fx' in input[:-1]: + options += 'x' + if '-l' in input[:-1]: + options += 'l' + else: + c_arg = None + if '-x' in prior or '-fx' in prior: + global x + x = str() + if '-l' in prior: + global l + l = list() + if '-c' in prior: + c_index = prior.index('-c') + if (c_index + 1) < len(prior): + c_arg = prior[c_index + 1] + options = complete_all(prefix, c_arg) + if prefix.endswith('.'): + options = [x for x in options if '._' not in x] return options - -def parse_input(raw_input): - input = raw_input.split(' ')[-1].rstrip() - if len(input) > 0 and input[0] in ['"', "'"]: - input = input[1:] - return input - - def main(): raw_input = next(sys.stdin) - prefix = parse_input(raw_input) - options = parse_string(prefix, raw_input) + input = raw_input.split(' ') + input[-1] = input[-1].rstrip() + options = parse_string(input, raw_input) if len(options) <= 1: options = options + [x + "'" for x in options] From 01aa3e88933f03af78c450d8db51b1d0417b612a Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 00:22:45 -0700 Subject: [PATCH 097/254] import module, module.tab_complete works! --- extras/pycompleter | 17 ++++++++--------- extras/pycompletion.sh | 3 ++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 323b94b..8047e27 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -29,23 +29,23 @@ import rlcompleter def complete_all(prefix, c_arg): if c_arg: - exec(c_arg) + exec(c_arg.strip('"\'')) idx = 0 options_set = set() - completer = rlcompleter.Completer() + completer = rlcompleter.Completer(locals()) while completer.complete(prefix, idx): options_set.add(completer.complete(prefix, idx)) idx += 1 return list(options_set) -def parse_string(input, raw_input): +def parse_string(input): options = [] prefix = input[-1].lstrip('"\'') prior = input[:-1] - if prefix.startswith('--') and not any(q in raw_input for q in ["'", '"']): + if prefix.startswith('--') and not any(q in ''.join(input) for q in ["'", '"']): options = ['--si', '--so', '--ji', '--jo', '--i'] - elif prefix.startswith('-') and not any(q in raw_input for q in ["'", '"']): + elif prefix.startswith('-') and not any(q in ''.join(input) for q in ["'", '"']): options = ['-h', '-x', '-fx', '-l', '-c', '-C'] elif prefix == '': options = ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] # +x,l @@ -65,6 +65,7 @@ def parse_string(input, raw_input): c_index = prior.index('-c') if (c_index + 1) < len(prior): c_arg = prior[c_index + 1] + #sys.stderr.write(c_arg) options = complete_all(prefix, c_arg) if prefix.endswith('.'): options = [x for x in options if '._' not in x] @@ -72,10 +73,8 @@ def parse_string(input, raw_input): def main(): - raw_input = next(sys.stdin) - input = raw_input.split(' ') - input[-1] = input[-1].rstrip() - options = parse_string(input, raw_input) + input = sys.argv[1:] + options = parse_string(input) if len(options) <= 1: options = options + [x + "'" for x in options] diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index b1aff5b..8d9f299 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -1,6 +1,7 @@ _py() { - COMPREPLY=($(echo "${COMP_WORDS[@]:1}" | pycompleter)) + #COMPREPLY=($(echo "${COMP_WORDS[@]:1}" | pycompleter)) + COMPREPLY=($(pycompleter "${COMP_WORDS[@]:1}" )) } complete -F _py -o nospace py From 49e8b8aa5d5659998587c917fcb70baf0c22c735 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 01:07:09 -0700 Subject: [PATCH 098/254] Starting to build import completion. --- extras/pycompleter | 56 +++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 8047e27..4eb422a 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -25,14 +25,17 @@ from itertools import groupby from uuid import uuid4 import rlcompleter +import IPython.core.completerlib as completerlib def complete_all(prefix, c_arg): if c_arg: exec(c_arg.strip('"\'')) + context = locals() + context.update(globals()) + completer = rlcompleter.Completer(context) idx = 0 options_set = set() - completer = rlcompleter.Completer(locals()) while completer.complete(prefix, idx): options_set.add(completer.complete(prefix, idx)) idx += 1 @@ -41,34 +44,37 @@ def complete_all(prefix, c_arg): def parse_string(input): options = [] - prefix = input[-1].lstrip('"\'') + current_full = input[-1].lstrip('"\'') + current_prefix = current_full.split(' ')[-1] prior = input[:-1] - if prefix.startswith('--') and not any(q in ''.join(input) for q in ["'", '"']): + if input[-1].startswith('--'): options = ['--si', '--so', '--ji', '--jo', '--i'] - elif prefix.startswith('-') and not any(q in ''.join(input) for q in ["'", '"']): + elif input[-1].startswith('-'): options = ['-h', '-x', '-fx', '-l', '-c', '-C'] - elif prefix == '': - options = ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] # +x,l - if '-x' in input[:-1] or '-fx' in input[:-1]: - options += 'x' - if '-l' in input[:-1]: - options += 'l' + elif len(prior) > 0 and prior[-1] == '-c': + options = completerlib.module_completion(current_full) or [] else: - c_arg = None - if '-x' in prior or '-fx' in prior: - global x - x = str() - if '-l' in prior: - global l - l = list() - if '-c' in prior: - c_index = prior.index('-c') - if (c_index + 1) < len(prior): - c_arg = prior[c_index + 1] - #sys.stderr.write(c_arg) - options = complete_all(prefix, c_arg) - if prefix.endswith('.'): - options = [x for x in options if '._' not in x] + if current_full == '': + options = ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] + if '-x' in input[:-1] or '-fx' in input[:-1]: + options += 'x' + if '-l' in input[:-1]: + options += 'l' + else: + c_arg = None + if '-x' in prior or '-fx' in prior: + global x + x = str() + if '-l' in prior: + global l + l = list() + if '-c' in prior: + c_index = prior.index('-c') + if (c_index + 1) < len(prior): + c_arg = prior[c_index + 1] + options = complete_all(current_prefix, c_arg) + if current_prefix.endswith('.'): + options = [x for x in options if '._' not in x] return options From 69799289403ce1b4a94b6d380282549a18b80ad9 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 01:16:45 -0700 Subject: [PATCH 099/254] 0.3.1dev --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3a2d14f..099a939 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='pythonpy', - version='0.3.0', + version='0.3.1dev', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/py3', 'extras/pycompleter', 'extras/wpy'], data_files=data_files, From 2c8dde7f65314f0bb449eba88ffeac0a41b2db30 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 01:18:26 -0700 Subject: [PATCH 100/254] Removed wpy until it`s ready. --- extras/wpy | 133 ----------------------------------------------------- setup.py | 2 +- 2 files changed, 1 insertion(+), 134 deletions(-) delete mode 100755 extras/wpy diff --git a/extras/wpy b/extras/wpy deleted file mode 100755 index 3548252..0000000 --- a/extras/wpy +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python -''' -Windows version of py. Still in development. -''' - -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 099a939..e446b8b 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ name='pythonpy', version='0.3.1dev', description='Take advantage of your python skills from the command line', - scripts=['py', 'extras/py3', 'extras/pycompleter', 'extras/wpy'], + scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, license='MIT', url='https://github.com/Russell91/pythonpy', From ab335f29bc1c9326a34ebad9d0b02754dccd1c05 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 01:24:18 -0700 Subject: [PATCH 101/254] Used lazy import of completer lib for. --- extras/pycompleter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index 4eb422a..b3d6e86 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -25,7 +25,6 @@ from itertools import groupby from uuid import uuid4 import rlcompleter -import IPython.core.completerlib as completerlib def complete_all(prefix, c_arg): @@ -52,6 +51,7 @@ def parse_string(input): elif input[-1].startswith('-'): options = ['-h', '-x', '-fx', '-l', '-c', '-C'] elif len(prior) > 0 and prior[-1] == '-c': + import IPython.core.completerlib as completerlib options = completerlib.module_completion(current_full) or [] else: if current_full == '': From 21d2af81e3af49f072a14fcdfa92e5a96b1171c4 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 01:31:56 -0700 Subject: [PATCH 102/254] Return from completion immediately if no input. --- extras/pycompleter | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extras/pycompleter b/extras/pycompleter index b3d6e86..9f91de7 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -80,6 +80,8 @@ def parse_string(input): def main(): input = sys.argv[1:] + if len(input) == 0: + return options = parse_string(input) if len(options) <= 1: From 6b71ef0874eb12c34b1a50123f2b59d08666c7c6 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 01:32:16 -0700 Subject: [PATCH 103/254] 0.3.1dev2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e446b8b..4de7c9c 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='pythonpy', - version='0.3.1dev', + version='0.3.1dev2', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From ee5793d42e14bae3e5261f04b6cff8d7e970e126 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 11:03:48 -0700 Subject: [PATCH 104/254] Muted errors in pycompleter. --- extras/pycompletion.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index 8d9f299..1902210 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -1,7 +1,7 @@ _py() { #COMPREPLY=($(echo "${COMP_WORDS[@]:1}" | pycompleter)) - COMPREPLY=($(pycompleter "${COMP_WORDS[@]:1}" )) + COMPREPLY=($(pycompleter "${COMP_WORDS[@]:1}" 2>/dev/null )) } complete -F _py -o nospace py From 6d8b989d41bb97d74d732c4d350b41256c5d4496 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 12:30:30 -0700 Subject: [PATCH 105/254] Added -c import completion. --- extras/completerlib.py | 189 +++++++++++++++++++++++++++++++++++++++++ extras/pycompleter | 35 +++++--- 2 files changed, 211 insertions(+), 13 deletions(-) create mode 100644 extras/completerlib.py diff --git a/extras/completerlib.py b/extras/completerlib.py new file mode 100644 index 0000000..b96fdc8 --- /dev/null +++ b/extras/completerlib.py @@ -0,0 +1,189 @@ +"""Implementations for various useful completers. + +These are all loaded by default by IPython. +""" +#----------------------------------------------------------------------------- +# Copyright (C) 2010-2011 The IPython Development Team. +# +# Distributed under the terms of the BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +from __future__ import print_function + +import inspect +import os +import re +import sys + +try: + # Python >= 3.3 + from importlib.machinery import all_suffixes + _suffixes = all_suffixes() +except ImportError: + from imp import get_suffixes + _suffixes = [ s[0] for s in get_suffixes() ] + +# Third-party imports +from time import time +from zipimport import zipimporter + +TIMEOUT_STORAGE = 2 + +TIMEOUT_GIVEUP = 20 + +# Regular expression for the python import statement +import_re = re.compile(r'(?P[a-zA-Z_][a-zA-Z0-9_]*?)' + r'(?P[/\\]__init__)?' + r'(?P%s)$' % + r'|'.join(re.escape(s) for s in _suffixes)) + +# RE for the ipython %run command (python + ipython scripts) +magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$') + +def module_list(path): + """ + Return the list containing the names of the modules available in the given + folder. + """ + # sys.path has the cwd as an empty string, but isdir/listdir need it as '.' + if path == '': + path = '.' + + # A few local constants to be used in loops below + pjoin = os.path.join + + if os.path.isdir(path): + # Build a list of all files in the directory and all files + # in its subdirectories. For performance reasons, do not + # recurse more than one level into subdirectories. + files = [] + for root, dirs, nondirs in os.walk(path): + subdir = root[len(path)+1:] + if subdir: + files.extend(pjoin(subdir, f) for f in nondirs) + dirs[:] = [] # Do not recurse into additional subdirectories. + else: + files.extend(nondirs) + + else: + try: + files = list(zipimporter(path)._files.keys()) + except: + files = [] + + # Build a list of modules which match the import_re regex. + modules = [] + for f in files: + m = import_re.match(f) + if m: + modules.append(m.group('name')) + return list(set(modules)) + + +def get_root_modules(): + """ + Returns a list containing the names of all the modules available in the + folders of the pythonpath. + + ip.db['rootmodules_cache'] maps sys.path entries to list of modules. + """ + #ip = get_ipython() + #rootmodules_cache = ip.db.get('rootmodules_cache', {}) + rootmodules_cache = {} + rootmodules = list(sys.builtin_module_names) + start_time = time() + #store = False + for path in sys.path: + try: + modules = rootmodules_cache[path] + except KeyError: + modules = module_list(path) + try: + modules.remove('__init__') + except ValueError: + pass + if path not in ('', '.'): # cwd modules should not be cached + rootmodules_cache[path] = modules + if time() - start_time > TIMEOUT_STORAGE and not store: + #store = True + #print("\nCaching the list of root modules, please wait!") + #print("(This will only be done once - type '%rehashx' to " + #"reset cache!)\n") + sys.stdout.flush() + if time() - start_time > TIMEOUT_GIVEUP: + print("This is taking too long, we give up.\n") + return [] + rootmodules.extend(modules) + #if store: + #ip.db['rootmodules_cache'] = rootmodules_cache + rootmodules = list(set(rootmodules)) + return rootmodules + + +def is_importable(module, attr, only_modules): + if only_modules: + return inspect.ismodule(getattr(module, attr)) + else: + return not(attr[:2] == '__' and attr[-2:] == '__') + + +def try_import(mod, only_modules=False): + try: + m = __import__(mod) + except: + return [] + mods = mod.split('.') + for module in mods[1:]: + m = getattr(m, module) + + m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__ + + completions = [] + if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init: + completions.extend( [attr for attr in dir(m) if + is_importable(m, attr, only_modules)]) + + completions.extend(getattr(m, '__all__', [])) + if m_is_init: + completions.extend(module_list(os.path.dirname(m.__file__))) + completions = set(completions) + if '__init__' in completions: + completions.remove('__init__') + return list(completions) + + +def module_completion(line): + """ + Returns a list containing the completion possibilities for an import line. + + The line looks like this : + 'import xml.d' + 'from xml.dom import' + """ + + words = line.split(' ') + nwords = len(words) + + # from whatever -> 'import ' + if nwords == 3 and words[0] == 'from': + return ['import '] + + # 'from xy' or 'import xy' + if nwords < 3 and (words[0] in ['import','from']) : + if nwords == 1: + return get_root_modules() + mod = words[1].split('.') + if len(mod) < 2: + return get_root_modules() + completion_list = try_import('.'.join(mod[:-1]), True) + return ['.'.join(mod[:-1] + [el]) for el in completion_list] + + # 'from xyz import abc' + if nwords >= 3 and words[0] == 'from': + mod = words[1] + return try_import(mod) diff --git a/extras/pycompleter b/extras/pycompleter index 9f91de7..1c89966 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -26,10 +26,14 @@ from uuid import uuid4 import rlcompleter - -def complete_all(prefix, c_arg): - if c_arg: - exec(c_arg.strip('"\'')) +def complete_all(prefix, args): + if args: + if args['x_arg']: + x = str() + if args['l_arg']: + l = list() + if args['c_arg']: + exec(args['c_arg'].strip('"\'')) context = locals() context.update(globals()) completer = rlcompleter.Completer(context) @@ -51,8 +55,14 @@ def parse_string(input): elif input[-1].startswith('-'): options = ['-h', '-x', '-fx', '-l', '-c', '-C'] elif len(prior) > 0 and prior[-1] == '-c': - import IPython.core.completerlib as completerlib - options = completerlib.module_completion(current_full) or [] + if 'import'.startswith(current_full): + options = ["'import"] + else: + import completerlib + options = completerlib.module_completion(current_full) + options += completerlib.module_list('') + if options: + options = [x for x in options if x.startswith(current_prefix)] else: if current_full == '': options = ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] @@ -61,18 +71,17 @@ def parse_string(input): if '-l' in input[:-1]: options += 'l' else: - c_arg = None + from collections import defaultdict + args = defaultdict(lambda: None) if '-x' in prior or '-fx' in prior: - global x - x = str() + args['x_arg'] = True if '-l' in prior: - global l - l = list() + args['l_arg'] = True if '-c' in prior: c_index = prior.index('-c') if (c_index + 1) < len(prior): - c_arg = prior[c_index + 1] - options = complete_all(current_prefix, c_arg) + args['c_arg'] = prior[c_index + 1] + options = complete_all(current_prefix, args) if current_prefix.endswith('.'): options = [x for x in options if '._' not in x] return options From 8a2d15bf58e18647d770659c3d9d0a3f7d07afed Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 12:43:52 -0700 Subject: [PATCH 106/254] 0.3.1dev3 --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4de7c9c..539cf0c 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,9 @@ setup( name='pythonpy', - version='0.3.1dev2', + version='0.3.1dev3', description='Take advantage of your python skills from the command line', + py_modules=['extras/completerlib.py'], scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, license='MIT', From ee76dfba7c7bb0f3864e708c18bc5df9714ab7e1 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 12:48:33 -0700 Subject: [PATCH 107/254] 0.3.1dev3 --- completion/__init__.py | 0 {extras => completion}/completerlib.py | 0 setup.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 completion/__init__.py rename {extras => completion}/completerlib.py (100%) diff --git a/completion/__init__.py b/completion/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/extras/completerlib.py b/completion/completerlib.py similarity index 100% rename from extras/completerlib.py rename to completion/completerlib.py diff --git a/setup.py b/setup.py index 539cf0c..65e13af 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ name='pythonpy', version='0.3.1dev3', description='Take advantage of your python skills from the command line', - py_modules=['extras/completerlib.py'], + py_modules=['completion'], scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, license='MIT', From 0c2f6e47530ed2d3ffb9df03dd7606dd9eaac8cb Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:02:14 -0700 Subject: [PATCH 108/254] Moved competerlib into pycompleter to avoid using a source package. --- completion/__init__.py | 0 completion/completerlib.py | 189 ---------------------------------- extras/pycompleter | 204 ++++++++++++++++++++++++++++++++++++- setup.py | 1 - 4 files changed, 201 insertions(+), 193 deletions(-) delete mode 100644 completion/__init__.py delete mode 100644 completion/completerlib.py diff --git a/completion/__init__.py b/completion/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/completion/completerlib.py b/completion/completerlib.py deleted file mode 100644 index b96fdc8..0000000 --- a/completion/completerlib.py +++ /dev/null @@ -1,189 +0,0 @@ -"""Implementations for various useful completers. - -These are all loaded by default by IPython. -""" -#----------------------------------------------------------------------------- -# Copyright (C) 2010-2011 The IPython Development Team. -# -# Distributed under the terms of the BSD License. -# -# The full license is in the file COPYING.txt, distributed with this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- -from __future__ import print_function - -import inspect -import os -import re -import sys - -try: - # Python >= 3.3 - from importlib.machinery import all_suffixes - _suffixes = all_suffixes() -except ImportError: - from imp import get_suffixes - _suffixes = [ s[0] for s in get_suffixes() ] - -# Third-party imports -from time import time -from zipimport import zipimporter - -TIMEOUT_STORAGE = 2 - -TIMEOUT_GIVEUP = 20 - -# Regular expression for the python import statement -import_re = re.compile(r'(?P[a-zA-Z_][a-zA-Z0-9_]*?)' - r'(?P[/\\]__init__)?' - r'(?P%s)$' % - r'|'.join(re.escape(s) for s in _suffixes)) - -# RE for the ipython %run command (python + ipython scripts) -magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$') - -def module_list(path): - """ - Return the list containing the names of the modules available in the given - folder. - """ - # sys.path has the cwd as an empty string, but isdir/listdir need it as '.' - if path == '': - path = '.' - - # A few local constants to be used in loops below - pjoin = os.path.join - - if os.path.isdir(path): - # Build a list of all files in the directory and all files - # in its subdirectories. For performance reasons, do not - # recurse more than one level into subdirectories. - files = [] - for root, dirs, nondirs in os.walk(path): - subdir = root[len(path)+1:] - if subdir: - files.extend(pjoin(subdir, f) for f in nondirs) - dirs[:] = [] # Do not recurse into additional subdirectories. - else: - files.extend(nondirs) - - else: - try: - files = list(zipimporter(path)._files.keys()) - except: - files = [] - - # Build a list of modules which match the import_re regex. - modules = [] - for f in files: - m = import_re.match(f) - if m: - modules.append(m.group('name')) - return list(set(modules)) - - -def get_root_modules(): - """ - Returns a list containing the names of all the modules available in the - folders of the pythonpath. - - ip.db['rootmodules_cache'] maps sys.path entries to list of modules. - """ - #ip = get_ipython() - #rootmodules_cache = ip.db.get('rootmodules_cache', {}) - rootmodules_cache = {} - rootmodules = list(sys.builtin_module_names) - start_time = time() - #store = False - for path in sys.path: - try: - modules = rootmodules_cache[path] - except KeyError: - modules = module_list(path) - try: - modules.remove('__init__') - except ValueError: - pass - if path not in ('', '.'): # cwd modules should not be cached - rootmodules_cache[path] = modules - if time() - start_time > TIMEOUT_STORAGE and not store: - #store = True - #print("\nCaching the list of root modules, please wait!") - #print("(This will only be done once - type '%rehashx' to " - #"reset cache!)\n") - sys.stdout.flush() - if time() - start_time > TIMEOUT_GIVEUP: - print("This is taking too long, we give up.\n") - return [] - rootmodules.extend(modules) - #if store: - #ip.db['rootmodules_cache'] = rootmodules_cache - rootmodules = list(set(rootmodules)) - return rootmodules - - -def is_importable(module, attr, only_modules): - if only_modules: - return inspect.ismodule(getattr(module, attr)) - else: - return not(attr[:2] == '__' and attr[-2:] == '__') - - -def try_import(mod, only_modules=False): - try: - m = __import__(mod) - except: - return [] - mods = mod.split('.') - for module in mods[1:]: - m = getattr(m, module) - - m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__ - - completions = [] - if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init: - completions.extend( [attr for attr in dir(m) if - is_importable(m, attr, only_modules)]) - - completions.extend(getattr(m, '__all__', [])) - if m_is_init: - completions.extend(module_list(os.path.dirname(m.__file__))) - completions = set(completions) - if '__init__' in completions: - completions.remove('__init__') - return list(completions) - - -def module_completion(line): - """ - Returns a list containing the completion possibilities for an import line. - - The line looks like this : - 'import xml.d' - 'from xml.dom import' - """ - - words = line.split(' ') - nwords = len(words) - - # from whatever -> 'import ' - if nwords == 3 and words[0] == 'from': - return ['import '] - - # 'from xy' or 'import xy' - if nwords < 3 and (words[0] in ['import','from']) : - if nwords == 1: - return get_root_modules() - mod = words[1].split('.') - if len(mod) < 2: - return get_root_modules() - completion_list = try_import('.'.join(mod[:-1]), True) - return ['.'.join(mod[:-1] + [el]) for el in completion_list] - - # 'from xyz import abc' - if nwords >= 3 and words[0] == 'from': - mod = words[1] - return try_import(mod) diff --git a/extras/pycompleter b/extras/pycompleter index 1c89966..bd5c5b4 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -26,6 +26,7 @@ from uuid import uuid4 import rlcompleter + def complete_all(prefix, args): if args: if args['x_arg']: @@ -45,6 +46,7 @@ def complete_all(prefix, args): return list(options_set) + def parse_string(input): options = [] current_full = input[-1].lstrip('"\'') @@ -58,9 +60,9 @@ def parse_string(input): if 'import'.startswith(current_full): options = ["'import"] else: - import completerlib - options = completerlib.module_completion(current_full) - options += completerlib.module_list('') + module_completion, module_list = get_completerlib() + options = module_completion(current_full) + options += module_list('') if options: options = [x for x in options if x.startswith(current_prefix)] else: @@ -87,6 +89,200 @@ def parse_string(input): return options +def get_completerlib(): + """Implementations for various useful completers. + + These are all loaded by default by IPython. + """ + #----------------------------------------------------------------------------- + # Copyright (C) 2010-2011 The IPython Development Team. + # + # Distributed under the terms of the BSD License. + # + # The full license is in the file COPYING.txt, distributed with this software. + #----------------------------------------------------------------------------- + + #----------------------------------------------------------------------------- + # Imports + #----------------------------------------------------------------------------- + #from __future__ import print_function + + import inspect + #import os + #import re + #import sys + + try: + # Python >= 3.3 + from importlib.machinery import all_suffixes + _suffixes = all_suffixes() + except ImportError: + from imp import get_suffixes + _suffixes = [ s[0] for s in get_suffixes() ] + + # Third-party imports + from time import time + from zipimport import zipimporter + + TIMEOUT_STORAGE = 2 + + TIMEOUT_GIVEUP = 20 + + # Regular expression for the python import statement + import_re = re.compile(r'(?P[a-zA-Z_][a-zA-Z0-9_]*?)' + r'(?P[/\\]__init__)?' + r'(?P%s)$' % + r'|'.join(re.escape(s) for s in _suffixes)) + + # RE for the ipython %run command (python + ipython scripts) + magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$') + + def module_list(path): + """ + Return the list containing the names of the modules available in the given + folder. + """ + # sys.path has the cwd as an empty string, but isdir/listdir need it as '.' + if path == '': + path = '.' + + # A few local constants to be used in loops below + pjoin = os.path.join + + if os.path.isdir(path): + # Build a list of all files in the directory and all files + # in its subdirectories. For performance reasons, do not + # recurse more than one level into subdirectories. + files = [] + for root, dirs, nondirs in os.walk(path): + subdir = root[len(path)+1:] + if subdir: + files.extend(pjoin(subdir, f) for f in nondirs) + dirs[:] = [] # Do not recurse into additional subdirectories. + else: + files.extend(nondirs) + + else: + try: + files = list(zipimporter(path)._files.keys()) + except: + files = [] + + # Build a list of modules which match the import_re regex. + modules = [] + for f in files: + m = import_re.match(f) + if m: + modules.append(m.group('name')) + return list(set(modules)) + + + def get_root_modules(): + """ + Returns a list containing the names of all the modules available in the + folders of the pythonpath. + + ip.db['rootmodules_cache'] maps sys.path entries to list of modules. + """ + #ip = get_ipython() + #rootmodules_cache = ip.db.get('rootmodules_cache', {}) + rootmodules_cache = {} + rootmodules = list(sys.builtin_module_names) + start_time = time() + #store = False + for path in sys.path: + try: + modules = rootmodules_cache[path] + except KeyError: + modules = module_list(path) + try: + modules.remove('__init__') + except ValueError: + pass + if path not in ('', '.'): # cwd modules should not be cached + rootmodules_cache[path] = modules + if time() - start_time > TIMEOUT_STORAGE and not store: + #store = True + #print("\nCaching the list of root modules, please wait!") + #print("(This will only be done once - type '%rehashx' to " + #"reset cache!)\n") + sys.stdout.flush() + if time() - start_time > TIMEOUT_GIVEUP: + print("This is taking too long, we give up.\n") + return [] + rootmodules.extend(modules) + #if store: + #ip.db['rootmodules_cache'] = rootmodules_cache + rootmodules = list(set(rootmodules)) + return rootmodules + + + def is_importable(module, attr, only_modules): + if only_modules: + return inspect.ismodule(getattr(module, attr)) + else: + return not(attr[:2] == '__' and attr[-2:] == '__') + + + def try_import(mod, only_modules=False): + try: + m = __import__(mod) + except: + return [] + mods = mod.split('.') + for module in mods[1:]: + m = getattr(m, module) + + m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__ + + completions = [] + if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init: + completions.extend( [attr for attr in dir(m) if + is_importable(m, attr, only_modules)]) + + completions.extend(getattr(m, '__all__', [])) + if m_is_init: + completions.extend(module_list(os.path.dirname(m.__file__))) + completions = set(completions) + if '__init__' in completions: + completions.remove('__init__') + return list(completions) + + + def module_completion(line): + """ + Returns a list containing the completion possibilities for an import line. + + The line looks like this : + 'import xml.d' + 'from xml.dom import' + """ + + words = line.split(' ') + nwords = len(words) + + # from whatever -> 'import ' + if nwords == 3 and words[0] == 'from': + return ['import '] + + # 'from xy' or 'import xy' + if nwords < 3 and (words[0] in ['import','from']) : + if nwords == 1: + return get_root_modules() + mod = words[1].split('.') + if len(mod) < 2: + return get_root_modules() + completion_list = try_import('.'.join(mod[:-1]), True) + return ['.'.join(mod[:-1] + [el]) for el in completion_list] + + # 'from xyz import abc' + if nwords >= 3 and words[0] == 'from': + mod = words[1] + return try_import(mod) + + return module_completion, module_list + + def main(): input = sys.argv[1:] if len(input) == 0: @@ -97,5 +293,7 @@ def main(): options = options + [x + "'" for x in options] print(' '.join(options)) + + if __name__ == '__main__': main() diff --git a/setup.py b/setup.py index 65e13af..2d758ea 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,6 @@ name='pythonpy', version='0.3.1dev3', description='Take advantage of your python skills from the command line', - py_modules=['completion'], scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, license='MIT', From c59c40939d125c58fe49682768297c2cc5fe51ce Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:03:21 -0700 Subject: [PATCH 109/254] 0.3.1dev4 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2d758ea..9309960 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='pythonpy', - version='0.3.1dev3', + version='0.3.1dev4', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 90c6cd96cbb56189c0eccef133bef839267ee0f1 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:09:22 -0700 Subject: [PATCH 110/254] Updated sudo failure message. --- extras/pycompletion.sh | 1 - setup.py | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index 1902210..85b4ce9 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -1,6 +1,5 @@ _py() { - #COMPREPLY=($(echo "${COMP_WORDS[@]:1}" | pycompleter)) COMPREPLY=($(pycompleter "${COMP_WORDS[@]:1}" 2>/dev/null )) } diff --git a/setup.py b/setup.py index 9309960..03f8f53 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,11 @@ if os.geteuid() == 0: data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] else: - print('User does not have write access to /etc completion will not work') + print( +'''PERMISSION DENIED: User does not have write access to /etc. +To add completion manually, run + source bash_completion.d/pycompletion.sh +from the install directory.''') data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] setup( From 3eaf43ce99b63b976b2333ac93d76e189c4f052d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:09:44 -0700 Subject: [PATCH 111/254] Added debug_completion which prints to stderr. --- extras/debug_pycompletion.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 extras/debug_pycompletion.sh diff --git a/extras/debug_pycompletion.sh b/extras/debug_pycompletion.sh new file mode 100644 index 0000000..2c41c15 --- /dev/null +++ b/extras/debug_pycompletion.sh @@ -0,0 +1,6 @@ +_py() +{ + COMPREPLY=($(pycompleter "${COMP_WORDS[@]:1}" )) +} + +complete -F _py -o nospace py From b190f2df87cd86cec6c742b375141a808de9cda0 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:10:10 -0700 Subject: [PATCH 112/254] 0.3.1dev5 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 03f8f53..584c681 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='pythonpy', - version='0.3.1dev4', + version='0.3.1dev5', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 51a3b209b48126cb577043108e9dcd18b4fd72ac Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:13:22 -0700 Subject: [PATCH 113/254] Fixed None+= Error in pycompletion. --- extras/pycompleter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index bd5c5b4..cc1a8df 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -61,7 +61,7 @@ def parse_string(input): options = ["'import"] else: module_completion, module_list = get_completerlib() - options = module_completion(current_full) + options = module_completion(current_full) or [] options += module_list('') if options: options = [x for x in options if x.startswith(current_prefix)] From 7dfe7500a36be57048bf31e1d9e463145f0fc08c Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:14:36 -0700 Subject: [PATCH 114/254] 0.3.1dev6 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 584c681..6826a8d 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='pythonpy', - version='0.3.1dev5', + version='0.3.1dev6', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 6ffa9a07d8d41f04d751cc7ce4711660f97f6cde Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:20:50 -0700 Subject: [PATCH 115/254] Added defaultdict to py. --- py | 1 + 1 file changed, 1 insertion(+) diff --git a/py b/py index 294273e..2a6fd03 100755 --- a/py +++ b/py @@ -30,6 +30,7 @@ def lazy_imports(expression, pre_cmd, post_cmd): 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 'defaultdict' in query: global defaultdict; from collections import defaultdict if 'groupby' in query: global groupby; from itertools import groupby if 'uuid4' in query: global uuid4; from uuid import uuid4 From dce0515e92193c9cd84a280f0965359f9fb600a6 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:22:57 -0700 Subject: [PATCH 116/254] Updated -c completion to fall back to expression completion without imports. --- extras/pycompleter | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index cc1a8df..6498a83 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -21,20 +21,21 @@ import shutil import tempfile from collections import Counter from collections import OrderedDict +from collections import defaultdict from itertools import groupby from uuid import uuid4 import rlcompleter -def complete_all(prefix, args): - if args: - if args['x_arg']: +def complete_all(prefix, completion_args): + if completion_args: + if completion_args['x_arg']: x = str() - if args['l_arg']: + if completion_args['l_arg']: l = list() - if args['c_arg']: - exec(args['c_arg'].strip('"\'')) + if completion_args['c_arg']: + exec(completion_args['c_arg'].strip('"\'')) context = locals() context.update(globals()) completer = rlcompleter.Completer(context) @@ -52,6 +53,7 @@ def parse_string(input): current_full = input[-1].lstrip('"\'') current_prefix = current_full.split(' ')[-1] prior = input[:-1] + completion_args = defaultdict(lambda: None) if input[-1].startswith('--'): options = ['--si', '--so', '--ji', '--jo', '--i'] elif input[-1].startswith('-'): @@ -59,12 +61,16 @@ def parse_string(input): elif len(prior) > 0 and prior[-1] == '-c': if 'import'.startswith(current_full): options = ["'import"] - else: + elif current_full.startswith('import '): module_completion, module_list = get_completerlib() options = module_completion(current_full) or [] options += module_list('') if options: options = [x for x in options if x.startswith(current_prefix)] + else: + options = complete_all(current_prefix, completion_args) + if current_prefix.endswith('.'): + options = [x for x in options if '._' not in x] else: if current_full == '': options = ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] @@ -73,17 +79,15 @@ def parse_string(input): if '-l' in input[:-1]: options += 'l' else: - from collections import defaultdict - args = defaultdict(lambda: None) if '-x' in prior or '-fx' in prior: - args['x_arg'] = True + completion_args['x_arg'] = True if '-l' in prior: - args['l_arg'] = True + completion_args['l_arg'] = True if '-c' in prior: c_index = prior.index('-c') if (c_index + 1) < len(prior): - args['c_arg'] = prior[c_index + 1] - options = complete_all(current_prefix, args) + completion_args['c_arg'] = prior[c_index + 1] + options = complete_all(current_prefix, completion_args) if current_prefix.endswith('.'): options = [x for x in options if '._' not in x] return options From 8072a8b42a075c773a39cd790b0400764d884755 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:23:14 -0700 Subject: [PATCH 117/254] 0.3.1dev7 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6826a8d..3627cdc 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='pythonpy', - version='0.3.1dev6', + version='0.3.1dev7', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 715dda6c7ac0e6571103f3587f88c6e648b78b9d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:32:28 -0700 Subject: [PATCH 118/254] Added support for importing local modules. --- py | 1 + 1 file changed, 1 insertion(+) diff --git a/py b/py index 2a6fd03..4ca8789 100755 --- a/py +++ b/py @@ -10,6 +10,7 @@ import json import re from collections import Iterable +sys.path.append('.') def lazy_imports(expression, pre_cmd, post_cmd): query = ((expression if expression else '') + (pre_cmd if pre_cmd else '') + From 4a7cffb3c1ba395fb55a553e450bfa3c6be8c53f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:33:19 -0700 Subject: [PATCH 119/254] 0.3.1dev8 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3627cdc..9d187bc 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='pythonpy', - version='0.3.1dev7', + version='0.3.1dev8', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 076c479f4c0d9dd94b80889258c4aea93cdff8fd Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:40:25 -0700 Subject: [PATCH 120/254] Supported tab completion of local modules and from A import B. --- extras/pycompleter | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 6498a83..467dd87 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -26,6 +26,7 @@ from itertools import groupby from uuid import uuid4 import rlcompleter +sys.path.append('.') def complete_all(prefix, completion_args): @@ -61,10 +62,9 @@ def parse_string(input): elif len(prior) > 0 and prior[-1] == '-c': if 'import'.startswith(current_full): options = ["'import"] - elif current_full.startswith('import '): + elif current_full.startswith('import ') or current_full.startswith('from '): module_completion, module_list = get_completerlib() options = module_completion(current_full) or [] - options += module_list('') if options: options = [x for x in options if x.startswith(current_prefix)] else: From 9c79eeafe131a15efe0ad1c7a0a0f4ceb9b1f70b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:45:15 -0700 Subject: [PATCH 121/254] Fixed print behavior with complete(from * imp...). --- extras/pycompleter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index 467dd87..27cc5fe 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -66,7 +66,7 @@ def parse_string(input): module_completion, module_list = get_completerlib() options = module_completion(current_full) or [] if options: - options = [x for x in options if x.startswith(current_prefix)] + options = [x.rstrip(' ') for x in options if x.startswith(current_prefix)] else: options = complete_all(current_prefix, completion_args) if current_prefix.endswith('.'): From 4e32161beac5729a033196cf8ace7d87533afa95 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:51:16 -0700 Subject: [PATCH 122/254] 0.3.1dev9 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9d187bc..9eae362 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='pythonpy', - version='0.3.1dev8', + version='0.3.1dev9', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 6ebe2d72327b2be867b585354494b84848d07333 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 13:54:08 -0700 Subject: [PATCH 123/254] 0.3.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9eae362..ff7abb2 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='pythonpy', - version='0.3.1dev9', + version='0.3.1', description='Take advantage of your python skills from the command line', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 1b30b300aaaa4334036747395a8afa2c70c4a776 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 14:20:12 -0700 Subject: [PATCH 124/254] Changed the pypi description. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ff7abb2..56b361b 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ setup( name='pythonpy', version='0.3.1', - description='Take advantage of your python skills from the command line', + description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, license='MIT', From 56e232932f7eb5decd133ada519a42f2b8d3ce06 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Jun 2014 14:21:59 -0700 Subject: [PATCH 125/254] Changed warning message for installation without sudo. --- setup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 56b361b..6295465 100644 --- a/setup.py +++ b/setup.py @@ -5,10 +5,12 @@ data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] else: print( -'''PERMISSION DENIED: User does not have write access to /etc. +'''****************************************************************************** +PERMISSION DENIED: User does not have write access to /etc. To add completion manually, run source bash_completion.d/pycompletion.sh -from the install directory.''') +from the install directory. +*****************************************************************************''') data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] setup( From 4398b6bee3e3a4e8955099e9ee6498ae756dde58 Mon Sep 17 00:00:00 2001 From: Finn Grimwood Date: Wed, 25 Jun 2014 19:16:09 +0100 Subject: [PATCH 126/254] Added ability to import any python module --- extras/py3 | 25 ++++++++++--------------- py | 25 ++++++++++--------------- test/test_pythonpy.py | 37 +++++++++++++++++-------------------- 3 files changed, 37 insertions(+), 50 deletions(-) diff --git a/extras/py3 b/extras/py3 index c73835f..3c81c45 100755 --- a/extras/py3 +++ b/extras/py3 @@ -18,24 +18,19 @@ 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 + + regex = re.compile("([a-zA-z0-9]+)\.") + matches = regex.findall(query) + for module_name in matches: + try: + module = __import__(module_name) + globals()[module_name] = module + except ImportError as e: + pass + 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) diff --git a/py b/py index 4ca8789..685d123 100755 --- a/py +++ b/py @@ -15,25 +15,20 @@ 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 + + regex = re.compile("([a-zA-z0-9]+)\.") + matches = regex.findall(query) + for module_name in matches: + try: + module = __import__(module_name) + globals()[module_name] = module + except ImportError as e: + pass + if 'Counter' in query: global Counter; from collections import Counter if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict if 'defaultdict' in query: global defaultdict; from collections import defaultdict 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) diff --git a/test/test_pythonpy.py b/test/test_pythonpy.py index ab4f349..b0b0304 100644 --- a/test/test_pythonpy.py +++ b/test/test_pythonpy.py @@ -26,26 +26,23 @@ def test_statements(self): self.assertEqual(check_output("""echo 3 | py -c 'a=5' -x x -C 'print(a)'""", shell=True), '3\n5\n') def test_imports(self): - check_output("py 'math'", shell=True) - check_output("py 'base64'", shell=True) - check_output("py 'calendar'", shell=True) - check_output("py 'csv'", shell=True) - check_output("py 'datetime'", shell=True) - check_output("py 'hashlib'", shell=True) - check_output("py 'glob'", shell=True) - check_output("py 'itertools'", shell=True) - check_output("py 'json'", shell=True) - check_output("py 'math'", shell=True) - check_output("py 'os'", shell=True) - check_output("py 'random'", shell=True) - check_output("py 're'", shell=True) - check_output("py 'shutil'", shell=True) - check_output("py 'tempfile'", shell=True) - check_output("py -c 'Counter'", shell=True) - check_output("py -c 'OrderedDict'", shell=True) - check_output("py -c 'groupby'", shell=True) - check_output("py 'uuid4'", shell=True) - + module_commands = ["math.ceil(2.5)", + "base64.b64encode('data to be encoded')", + "calendar.weekday(1955, 11, 5)", + "csv.list_dialects()", + "datetime.timedelta(hours=-5)", + "hashlib.sha224(\"Nobody inspects the spammish repetition\").hexdigest()", + "glob.glob('*')", + "itertools.product(['a','b'], [1,2])", + "json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))", + "os.name", + "random.randint(0, 1000)", + "re.compile('[a-z]').findall('abcd')", + "shutil.get_archive_formats()", + "tempfile.gettempdir()", + "uuid.uuid1()"] + for command in module_commands: + check_output("py %r" % command, shell=True) if __name__ == '__main__': unittest.main() From 374b6cb533977fb7ec77b9367868fe001e1116e2 Mon Sep 17 00:00:00 2001 From: Finn Grimwood Date: Wed, 25 Jun 2014 19:35:50 +0100 Subject: [PATCH 127/254] Allow underscores in dynamically imported module names --- extras/py3 | 2 +- py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/py3 b/extras/py3 index 3c81c45..beb00b5 100755 --- a/extras/py3 +++ b/extras/py3 @@ -19,7 +19,7 @@ def lazy_imports(expression, pre_cmd, post_cmd): (pre_cmd if pre_cmd else '') + (post_cmd if post_cmd else '')) - regex = re.compile("([a-zA-z0-9]+)\.") + regex = re.compile("([a-zA-z0-9_]+)\.") matches = regex.findall(query) for module_name in matches: try: diff --git a/py b/py index 685d123..3862391 100755 --- a/py +++ b/py @@ -16,7 +16,7 @@ def lazy_imports(expression, pre_cmd, post_cmd): (pre_cmd if pre_cmd else '') + (post_cmd if post_cmd else '')) - regex = re.compile("([a-zA-z0-9]+)\.") + regex = re.compile("([a-zA-z0-9_]+)\.") matches = regex.findall(query) for module_name in matches: try: From 935d07d2604ec569b1bfa8d47c143f687c7a59df Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 14:19:26 -0700 Subject: [PATCH 128/254] Removed clause disregarding "py" element of sys.argv --- extras/pycompletion.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index 85b4ce9..1486919 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -1,6 +1,6 @@ _py() { - COMPREPLY=($(pycompleter "${COMP_WORDS[@]:1}" 2>/dev/null )) + COMPREPLY=($(pycompleter "${COMP_WORDS[@]}" 2>/dev/null )) } complete -F _py -o nospace py From d5b712738c30b3a335d237e4faa1c2bebce4db73 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 14:23:57 -0700 Subject: [PATCH 129/254] Added ' to default completion. --- extras/pycompleter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index 27cc5fe..229000e 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -73,7 +73,7 @@ def parse_string(input): options = [x for x in options if '._' not in x] else: if current_full == '': - options = ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] + options = ["'sys", "'json", "'re", "'base64", "'calendar", "'csv", "'datetime", "'hashlib", "'itertools", "'json", "'math", "'os", "'random", "'re", "'shutil"] if '-x' in input[:-1] or '-fx' in input[:-1]: options += 'x' if '-l' in input[:-1]: From 398fc6bfa52148c87f6a4ff029a005c5db4e33cf Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 16:17:48 -0700 Subject: [PATCH 130/254] Added support for < filename_competion.txt. --- extras/pycompleter | 3 +++ extras/pycompletion.sh | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/extras/pycompleter b/extras/pycompleter index 229000e..951e675 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -291,6 +291,9 @@ def main(): input = sys.argv[1:] if len(input) == 0: return + elif '<' in input or '>' in input: + print('_pycd') + return options = parse_string(input) if len(options) <= 1: diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index 1486919..e37de22 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -1,6 +1,44 @@ +_pycd () +{ + local cur prev words cword; + _init_completion || return; + local IFS=' +' i j k; + compopt -o filenames; + if [[ -z "${CDPATH:-}" || "$cur" == ?(.)?(.)/* ]]; then + _filedir -d; + return 0; + fi; + local -r mark_dirs=$(_rl_enabled mark-directories && echo y); + local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y); + for i in ${CDPATH//:/' +'}; + do + k="${#COMPREPLY[@]}"; + for j in $( compgen -d $i/$cur ); + do + if [[ ( -n $mark_symdirs && -h $j || -n $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then + j+="/"; + fi; + COMPREPLY[k++]=${j#$i/}; + done; + done; + _filedir -d; + if [[ ${#COMPREPLY[@]} -eq 1 ]]; then + i=${COMPREPLY[0]}; + if [[ "$i" == "$cur" && $i != "*/" ]]; then + COMPREPLY[0]="${i}/"; + fi; + fi; + return 0 +} + _py() { COMPREPLY=($(pycompleter "${COMP_WORDS[@]}" 2>/dev/null )) + if [[ ${COMPREPLY[0]} == '_pycd' ]]; then + _pycd + fi } complete -F _py -o nospace py From 1aff746144cfc56083032f7f6dcf460a25485aaa Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 16:18:29 -0700 Subject: [PATCH 131/254] 0.3.2dev --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6295465..bb41705 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name='pythonpy', - version='0.3.1', + version='0.3.2dev', description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 475eb00601bd1f54334213a30627f5cb2b25a629 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 16:21:04 -0700 Subject: [PATCH 132/254] Removed ' from 'stdlibmodule completion because it felt weird. --- extras/pycompleter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index 951e675..650af35 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -73,7 +73,7 @@ def parse_string(input): options = [x for x in options if '._' not in x] else: if current_full == '': - options = ["'sys", "'json", "'re", "'base64", "'calendar", "'csv", "'datetime", "'hashlib", "'itertools", "'json", "'math", "'os", "'random", "'re", "'shutil"] + options = ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] if '-x' in input[:-1] or '-fx' in input[:-1]: options += 'x' if '-l' in input[:-1]: From 122dd4947b3e71a59c40c7fd7429ba7ee79d4851 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 16:31:51 -0700 Subject: [PATCH 133/254] Changed bash completion to rely on ubiquitous _cd. --- extras/pycompletion.sh | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index e37de22..8c2fe1d 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -1,43 +1,9 @@ -_pycd () -{ - local cur prev words cword; - _init_completion || return; - local IFS=' -' i j k; - compopt -o filenames; - if [[ -z "${CDPATH:-}" || "$cur" == ?(.)?(.)/* ]]; then - _filedir -d; - return 0; - fi; - local -r mark_dirs=$(_rl_enabled mark-directories && echo y); - local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y); - for i in ${CDPATH//:/' -'}; - do - k="${#COMPREPLY[@]}"; - for j in $( compgen -d $i/$cur ); - do - if [[ ( -n $mark_symdirs && -h $j || -n $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then - j+="/"; - fi; - COMPREPLY[k++]=${j#$i/}; - done; - done; - _filedir -d; - if [[ ${#COMPREPLY[@]} -eq 1 ]]; then - i=${COMPREPLY[0]}; - if [[ "$i" == "$cur" && $i != "*/" ]]; then - COMPREPLY[0]="${i}/"; - fi; - fi; - return 0 -} - _py() { COMPREPLY=($(pycompleter "${COMP_WORDS[@]}" 2>/dev/null )) if [[ ${COMPREPLY[0]} == '_pycd' ]]; then - _pycd + COMPREPLY=() + _cd 2>/dev/null fi } From 648fa28f7b11c775e43a3e1b76e644bd23b058c7 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 16:32:44 -0700 Subject: [PATCH 134/254] 0.3.2dev2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index bb41705..2585cd1 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name='pythonpy', - version='0.3.2dev', + version='0.3.2dev2', description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 249e5c1bfec22210600ab9a7dc9f97deb88d727a Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 16:39:19 -0700 Subject: [PATCH 135/254] Changed _cd to _longopt. --- extras/pycompleter | 2 +- extras/pycompletion.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 650af35..5e1faa1 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -292,7 +292,7 @@ def main(): if len(input) == 0: return elif '<' in input or '>' in input: - print('_pycd') + print('_longopt') return options = parse_string(input) diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index 8c2fe1d..c37e48b 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -1,9 +1,9 @@ _py() { COMPREPLY=($(pycompleter "${COMP_WORDS[@]}" 2>/dev/null )) - if [[ ${COMPREPLY[0]} == '_pycd' ]]; then + if [[ ${COMPREPLY[0]} == '_longopt' ]]; then COMPREPLY=() - _cd 2>/dev/null + _longopt 2>/dev/null fi } From ec100b2d72516046fdeaaa969770e49c849bc092 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 16:39:36 -0700 Subject: [PATCH 136/254] 0.3.2dev3 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2585cd1..48f35ef 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name='pythonpy', - version='0.3.2dev2', + version='0.3.2dev3', description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From bc7802ea3883cbaecc2b7280b2552a3f578988d6 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 26 Jun 2014 19:10:50 -0700 Subject: [PATCH 137/254] Show help() when expression ends with "?" --- py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py b/py index 4ca8789..8bbcf34 100755 --- a/py +++ b/py @@ -78,6 +78,8 @@ else: if args.expression: args.expression = args.expression.replace("`", "'") + if args.expression.endswith('?') or args.expression.startswith('?'): + args.expression = 'help(%s)' % args.expression.strip('?') if args.pre_cmd: args.pre_cmd = args.pre_cmd.replace("`", "'") if args.post_cmd: From 3d547f99ca557b54250ae53a63e1d412bd4a4312 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sun, 29 Jun 2014 13:18:00 -0700 Subject: [PATCH 138/254] Added help? support. --- py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/py b/py index 8bbcf34..c8d0629 100755 --- a/py +++ b/py @@ -78,8 +78,11 @@ else: if args.expression: args.expression = args.expression.replace("`", "'") - if args.expression.endswith('?') or args.expression.startswith('?'): + if args.expression.startswith('?') or args.expression.endswith('?'): args.expression = 'help(%s)' % args.expression.strip('?') + if args.lines_of_stdin: + from itertools import islice + stdin = islice(stdin,1) if args.pre_cmd: args.pre_cmd = args.pre_cmd.replace("`", "'") if args.post_cmd: From f5c8b61a0e6f211ddf3c175fdac8b1f8d79a2d61 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 4 Jul 2014 18:20:17 -0700 Subject: [PATCH 139/254] Added support for automatic imports to tab completion. --- extras/pycompleter | 22 ++++++++++++++++++++++ py | 9 +++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 5e1faa1..2dbc166 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -29,6 +29,27 @@ import rlcompleter sys.path.append('.') +def lazy_imports(*args): + old_path = sys.path + sys.path = [x for x in sys.path if x != '.'] + + query = ' '.join([x for x in args if x]) + regex = re.compile("([a-zA-z0-9_]+)\.") + matches = regex.findall(query) + for module_name in matches: + try: + module = __import__(module_name) + globals()[module_name] = module + except ImportError as e: + pass + + if 'Counter' in query: global Counter; from collections import Counter + if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict + if 'defaultdict' in query: global defaultdict; from collections import defaultdict + if 'groupby' in query: global groupby; from itertools import groupby + sys.path = old_path + + def complete_all(prefix, completion_args): if completion_args: if completion_args['x_arg']: @@ -37,6 +58,7 @@ def complete_all(prefix, completion_args): l = list() if completion_args['c_arg']: exec(completion_args['c_arg'].strip('"\'')) + lazy_imports(prefix, completion_args['c_arg']) context = locals() context.update(globals()) completer = rlcompleter.Completer(context) diff --git a/py b/py index 514720d..a0990e8 100755 --- a/py +++ b/py @@ -10,12 +10,8 @@ import json import re from collections import Iterable -sys.path.append('.') -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 '')) - +def lazy_imports(*args): + query = ' '.join([x for x in args if x]) regex = re.compile("([a-zA-z0-9_]+)\.") matches = regex.findall(query) for module_name in matches: @@ -84,6 +80,7 @@ if args.post_cmd: args.post_cmd = args.post_cmd.replace("`", "'") lazy_imports(args.expression, args.pre_cmd, args.post_cmd) +sys.path.append('.') if args.pre_cmd: exec(args.pre_cmd) From ff334c51e1aa046624eee1ac0111ec14df6b8155 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 4 Jul 2014 18:49:56 -0700 Subject: [PATCH 140/254] Added support for automatic imports to tab completion without -c. --- extras/pycompleter | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 2dbc166..547f36f 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -30,9 +30,6 @@ sys.path.append('.') def lazy_imports(*args): - old_path = sys.path - sys.path = [x for x in sys.path if x != '.'] - query = ' '.join([x for x in args if x]) regex = re.compile("([a-zA-z0-9_]+)\.") matches = regex.findall(query) @@ -47,7 +44,6 @@ def lazy_imports(*args): if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict if 'defaultdict' in query: global defaultdict; from collections import defaultdict if 'groupby' in query: global groupby; from itertools import groupby - sys.path = old_path def complete_all(prefix, completion_args): @@ -68,7 +64,12 @@ def complete_all(prefix, completion_args): options_set.add(completer.complete(prefix, idx)) idx += 1 - return list(options_set) + module_completion, module_list = get_completerlib() + options = module_completion("import " + prefix) or [] + if options: + options = [x.rstrip(' ') for x in options if x.startswith(prefix)] + + return options + list(options_set) def parse_string(input): From 7883630f707adac16b50ebc81e5b6265e81f9386 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 4 Jul 2014 18:59:21 -0700 Subject: [PATCH 141/254] Fixed bug with duplicate completion options. --- extras/pycompleter | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 547f36f..f548c75 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -317,13 +317,12 @@ def main(): elif '<' in input or '>' in input: print('_longopt') return - options = parse_string(input) + options = list(set(parse_string(input))) if len(options) <= 1: options = options + [x + "'" for x in options] print(' '.join(options)) - if __name__ == '__main__': main() From fa980f32b8d1208d446f9ab7644efaeb28190af6 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 4 Jul 2014 19:14:40 -0700 Subject: [PATCH 142/254] Fixed bug with tab completion when quoted input contained space. --- extras/pycompleter | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extras/pycompleter b/extras/pycompleter index f548c75..7d13c12 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -319,6 +319,9 @@ def main(): return options = list(set(parse_string(input))) + if ' ' in input[-1] and max(map(len, options)) + 1 >= len(input[-1]): + options.append(input[-1].split(' ')[-1]) + if len(options) <= 1: options = options + [x + "'" for x in options] print(' '.join(options)) From 224ab244f976e5fa63450303f12de02342c39a5f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 4 Jul 2014 19:31:40 -0700 Subject: [PATCH 143/254] Removed support for sys.path = "." The system failed in lots of places and was more likely to cause problems for tab completion. --- extras/pycompleter | 1 - py | 1 - 2 files changed, 2 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 7d13c12..40d9b9c 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -26,7 +26,6 @@ from itertools import groupby from uuid import uuid4 import rlcompleter -sys.path.append('.') def lazy_imports(*args): diff --git a/py b/py index a0990e8..73f4826 100755 --- a/py +++ b/py @@ -80,7 +80,6 @@ if args.post_cmd: args.post_cmd = args.post_cmd.replace("`", "'") lazy_imports(args.expression, args.pre_cmd, args.post_cmd) -sys.path.append('.') if args.pre_cmd: exec(args.pre_cmd) From 8bbe6f2120f3b294390c2bf37fd85662a3ed06cb Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 4 Jul 2014 19:52:32 -0700 Subject: [PATCH 144/254] 0.3.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 48f35ef..0c2082c 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name='pythonpy', - version='0.3.2dev3', + version='0.3.2', description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From f0e28a2c477a79762e6f1717a2b2f766ac417d4f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 4 Jul 2014 19:57:58 -0700 Subject: [PATCH 145/254] Updated README. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index cd1053b..8f0e456 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ Float Arithmetic :: -Access common imports directly +Access imports directly ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: From 05193f8ac7258351b74ac062210982f0d046f3ca Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sun, 6 Jul 2014 00:07:07 -0700 Subject: [PATCH 146/254] Fixed bug not replacing ` in -c tab completion. --- extras/pycompleter | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 40d9b9c..3f866df 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -46,14 +46,14 @@ def lazy_imports(*args): def complete_all(prefix, completion_args): + lazy_imports(prefix, completion_args['c_arg']) if completion_args: if completion_args['x_arg']: x = str() if completion_args['l_arg']: l = list() if completion_args['c_arg']: - exec(completion_args['c_arg'].strip('"\'')) - lazy_imports(prefix, completion_args['c_arg']) + exec(completion_args['c_arg'].strip('"\'').replace("`", "'")) context = locals() context.update(globals()) completer = rlcompleter.Completer(context) From a04cdf73d17759ba0723b7596e891891c3b79e1b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 9 Jul 2014 15:05:54 -0700 Subject: [PATCH 147/254] Prevent error on py 'module' --- extras/pycompleter | 2 +- py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 3f866df..adf084b 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -30,7 +30,7 @@ import rlcompleter def lazy_imports(*args): query = ' '.join([x for x in args if x]) - regex = re.compile("([a-zA-z0-9_]+)\.") + regex = re.compile("([a-zA-z_][a-zA-Z0-9_]*)\.") matches = regex.findall(query) for module_name in matches: try: diff --git a/py b/py index 73f4826..26dc093 100755 --- a/py +++ b/py @@ -12,8 +12,10 @@ from collections import Iterable def lazy_imports(*args): query = ' '.join([x for x in args if x]) - regex = re.compile("([a-zA-z0-9_]+)\.") + regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.") matches = regex.findall(query) + if not any(x in ['.', ' '] for x in args[0]): + matches.append(args[0]) for module_name in matches: try: module = __import__(module_name) From 506fd66cd82408c212ce4f67a3965c414e24ba1d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 9 Jul 2014 15:27:33 -0700 Subject: [PATCH 148/254] Added -V, --version command. --- py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/py b/py index 26dc093..0876752 100755 --- a/py +++ b/py @@ -10,6 +10,7 @@ import json import re from collections import Iterable + def lazy_imports(*args): query = ' '.join([x for x in args if x]) regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.") @@ -43,6 +44,10 @@ parser.add_argument('-l', dest='list_of_stdin', action='store_const', 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') +__version__ = '0.3.2' +import pkg_resources +__version__ = pkg_resources.require("Pythonpy")[0].version +parser.add_argument('-V', '--version', action='version', version='py version %s' % __version__, help='version info') parser.add_argument('--i', '--ignore_exceptions', dest='ignore_exceptions', action='store_const', const=True, default=False, From 5910ed066a79dc097999de2c4bf4c26c2736a42e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 9 Jul 2014 15:37:52 -0700 Subject: [PATCH 149/254] Fixed -V, --version command. --- VERSION | 1 + py | 11 +++++++---- setup.py | 5 ++++- 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 VERSION diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..d15723f --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.3.2 diff --git a/py b/py index 0876752..3be8923 100755 --- a/py +++ b/py @@ -29,6 +29,12 @@ def lazy_imports(*args): if 'defaultdict' in query: global defaultdict; from collections import defaultdict if 'groupby' in query: global groupby; from itertools import groupby + +def get_version(): + with open('VERSION', 'r') as f: + __version__ = f.read() + return __version__ + parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter) @@ -44,10 +50,7 @@ parser.add_argument('-l', dest='list_of_stdin', action='store_const', 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') -__version__ = '0.3.2' -import pkg_resources -__version__ = pkg_resources.require("Pythonpy")[0].version -parser.add_argument('-V', '--version', action='version', version='py version %s' % __version__, help='version info') +parser.add_argument('-V', '--version', action='version', version='py version %s' % get_version(), help='version info') parser.add_argument('--i', '--ignore_exceptions', dest='ignore_exceptions', action='store_const', const=True, default=False, diff --git a/setup.py b/setup.py index 0c2082c..d62f558 100644 --- a/setup.py +++ b/setup.py @@ -13,9 +13,12 @@ *****************************************************************************''') data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] +with open('VERSION', 'r') as f: + __version__ = f.read() + setup( name='pythonpy', - version='0.3.2', + version=__version__, description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 74677be00cbd54d1126df8eff9478f67547755ba Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 9 Jul 2014 15:50:33 -0700 Subject: [PATCH 150/254] Just hard coded version in 2 places. --- VERSION | 1 - py | 2 +- setup.py | 5 +---- 3 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 VERSION diff --git a/VERSION b/VERSION deleted file mode 100644 index d15723f..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.3.2 diff --git a/py b/py index 3be8923..fea1fb2 100755 --- a/py +++ b/py @@ -50,7 +50,7 @@ parser.add_argument('-l', dest='list_of_stdin', action='store_const', 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('-V', '--version', action='version', version='py version %s' % get_version(), help='version info') +parser.add_argument('-V', '--version', action='version', version='py version 0.3.2', help='version info') parser.add_argument('--i', '--ignore_exceptions', dest='ignore_exceptions', action='store_const', const=True, default=False, diff --git a/setup.py b/setup.py index d62f558..b8d0cf3 100644 --- a/setup.py +++ b/setup.py @@ -13,12 +13,9 @@ *****************************************************************************''') data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] -with open('VERSION', 'r') as f: - __version__ = f.read() - setup( name='pythonpy', - version=__version__, + version='0.3.2, description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 5ceb17cd3d829c782b6734d537a8e06b367bbb4e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 9 Jul 2014 15:51:28 -0700 Subject: [PATCH 151/254] Fixed typo in setup.py. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b8d0cf3..0c2082c 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name='pythonpy', - version='0.3.2, + version='0.3.2', description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 7f08754daf660224b3a3951f4921ec1a2e8f23d3 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 9 Jul 2014 16:09:30 -0700 Subject: [PATCH 152/254] Reduced number of default module recommendations. --- extras/pycompleter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index adf084b..c6bd1e1 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -95,7 +95,7 @@ def parse_string(input): options = [x for x in options if '._' not in x] else: if current_full == '': - options = ['sys', 'json', 're', 'base64', 'calendar', 'csv', 'datetime', 'hashlib', 'itertools', 'json', 'math', 'os', 'random', 're', 'shutil'] + options = ['sys', 'json', 're', 'csv', 'datetime', 'hashlib', 'itertools', 'math', 'os', 'random', 'shutil'] if '-x' in input[:-1] or '-fx' in input[:-1]: options += 'x' if '-l' in input[:-1]: From 60f580768c54f303ef2abccaecf5f0f8f9239dbf Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 9 Jul 2014 16:22:18 -0700 Subject: [PATCH 153/254] Changed print to output utf-8 by default. --- py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py b/py index fea1fb2..5bd4d01 100755 --- a/py +++ b/py @@ -131,11 +131,11 @@ if isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstan for x in result: formatted = format(x) if formatted is not None: - print(formatted) + print(formatted.encode('utf-8')) else: formatted = format(result) if formatted is not None: - print(formatted) + print(formatted.encode('utf-8')) if args.post_cmd: exec(args.post_cmd) From ba069b206626dd2a86da870c997d11653675f3f0 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 9 Jul 2014 16:25:05 -0700 Subject: [PATCH 154/254] Changed print to output utf-8 only when default fails. --- py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/py b/py index 5bd4d01..198168e 100755 --- a/py +++ b/py @@ -131,11 +131,17 @@ if isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstan for x in result: formatted = format(x) if formatted is not None: - print(formatted.encode('utf-8')) + try: + print(formatted) + except: + print(formatted.encode('utf-8')) else: formatted = format(result) if formatted is not None: - print(formatted.encode('utf-8')) + try: + print(formatted) + except: + print(formatted.encode('utf-8')) if args.post_cmd: exec(args.post_cmd) From b104350637c806298a10d7f37f0b3944ff1f1690 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 9 Jul 2014 16:28:28 -0700 Subject: [PATCH 155/254] Changed print to output utf-8 only when default fails with UnicodeEncodeError. --- py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py b/py index 198168e..999ea78 100755 --- a/py +++ b/py @@ -133,14 +133,14 @@ if isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstan if formatted is not None: try: print(formatted) - except: + except UnicodeEncodeError: print(formatted.encode('utf-8')) else: formatted = format(result) if formatted is not None: try: - print(formatted) - except: + print(formatted) + except UnicodeEncodeError: print(formatted.encode('utf-8')) if args.post_cmd: From b3c4f3fc9093d45bda3b9d92377172e7ad7fe396 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 24 Jul 2014 12:16:26 -0700 Subject: [PATCH 156/254] Fixed A-z typo in pycompleter. --- extras/pycompleter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index c6bd1e1..0ccb22e 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -30,7 +30,7 @@ import rlcompleter def lazy_imports(*args): query = ' '.join([x for x in args if x]) - regex = re.compile("([a-zA-z_][a-zA-Z0-9_]*)\.") + regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.") matches = regex.findall(query) for module_name in matches: try: From e6c067700b0438f7012fba9f9c2cbcf52e36709a Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 24 Jul 2014 12:22:09 -0700 Subject: [PATCH 157/254] Removed dead get_version function. --- py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/py b/py index 999ea78..7be4511 100755 --- a/py +++ b/py @@ -29,12 +29,6 @@ def lazy_imports(*args): if 'defaultdict' in query: global defaultdict; from collections import defaultdict if 'groupby' in query: global groupby; from itertools import groupby - -def get_version(): - with open('VERSION', 'r') as f: - __version__ = f.read() - return __version__ - parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter) From 8dc340ce035c8cfc81daf01d54388c0c04b4304b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 24 Jul 2014 12:24:35 -0700 Subject: [PATCH 158/254] 0.3.3 --- py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/py b/py index 7be4511..975be09 100755 --- a/py +++ b/py @@ -44,7 +44,7 @@ parser.add_argument('-l', dest='list_of_stdin', action='store_const', 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('-V', '--version', action='version', version='py version 0.3.2', help='version info') +parser.add_argument('-V', '--version', action='version', version='py version 0.3.3', help='version info') parser.add_argument('--i', '--ignore_exceptions', dest='ignore_exceptions', action='store_const', const=True, default=False, diff --git a/setup.py b/setup.py index 0c2082c..5c9eaf2 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name='pythonpy', - version='0.3.2', + version='0.3.3', description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From a4cffdf8d7341e650570c70a9c725a184c184fa5 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 24 Jul 2014 12:43:08 -0700 Subject: [PATCH 159/254] Changed method of supporting statements containing only module names. --- extras/pycompleter | 2 +- py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index 0ccb22e..e56c60a 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -30,7 +30,7 @@ import rlcompleter def lazy_imports(*args): query = ' '.join([x for x in args if x]) - regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.") + regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.?") matches = regex.findall(query) for module_name in matches: try: diff --git a/py b/py index 975be09..261577c 100755 --- a/py +++ b/py @@ -13,10 +13,8 @@ from collections import Iterable def lazy_imports(*args): query = ' '.join([x for x in args if x]) - regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.") + regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.?") matches = regex.findall(query) - if not any(x in ['.', ' '] for x in args[0]): - matches.append(args[0]) for module_name in matches: try: module = __import__(module_name) From bf2b6a40630fd9449ac0e88642298272ff03e175 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 24 Jul 2014 12:47:07 -0700 Subject: [PATCH 160/254] Added "math" and "[math]" to nosetests. --- test/test_pythonpy.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/test_pythonpy.py b/test/test_pythonpy.py index b0b0304..bf03c8e 100644 --- a/test/test_pythonpy.py +++ b/test/test_pythonpy.py @@ -40,7 +40,10 @@ def test_imports(self): "re.compile('[a-z]').findall('abcd')", "shutil.get_archive_formats()", "tempfile.gettempdir()", - "uuid.uuid1()"] + "uuid.uuid1()", + "math", + "[math]", + ] for command in module_commands: check_output("py %r" % command, shell=True) From 2384fc5c420bac15e0222b39ac8300f4b111df23 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Mon, 28 Jul 2014 14:51:31 -0700 Subject: [PATCH 161/254] Fixed bug not importing submodules. --- py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/py b/py index 261577c..8d820bb 100755 --- a/py +++ b/py @@ -11,17 +11,20 @@ import re from collections import Iterable -def lazy_imports(*args): - query = ' '.join([x for x in args if x]) - regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.?") - matches = regex.findall(query) +def import_matches(query, prefix=''): + matches = set(re.findall(r"(%s[a-zA-Z_][a-zA-Z0-9_]*)\.?" % prefix, query)) for module_name in matches: try: module = __import__(module_name) globals()[module_name] = module + import_matches(query, prefix='%s.' % module_name) except ImportError as e: pass +def lazy_imports(*args): + query = ' '.join([x for x in args if x]) + import_matches(query) + if 'Counter' in query: global Counter; from collections import Counter if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict if 'defaultdict' in query: global defaultdict; from collections import defaultdict From 335f8039f636eaed0f6d7c9e7c25c2e39e0b6ca5 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 29 Jul 2014 19:40:03 -0700 Subject: [PATCH 162/254] Refactored pycompleter --- extras/pycompleter | 123 ++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/extras/pycompleter b/extras/pycompleter index e56c60a..e393e6f 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -1,33 +1,31 @@ #!/usr/bin/env python from __future__ import (unicode_literals, absolute_import, print_function, division) -import argparse import sys -import json import re -import base64 -import calendar -import csv -import datetime -import hashlib -import glob -import itertools -import json -import math -import os -import random -import re -import shutil -import tempfile -from collections import Counter -from collections import OrderedDict from collections import defaultdict -from itertools import groupby -from uuid import uuid4 import rlcompleter +def current_raw(input): + if len(input[-1]) > 0 and input[-1][0] in '"\'': + return input[-1][1:] + return input[-1] + + +def current_list(input): + return re.split(r'[^a-zA-Z0-9\.]', current_raw(input)) + + +def current_prefix(input): + return current_list(input)[-1] + + +def prior(input): + return input[:-1] + + def lazy_imports(*args): query = ' '.join([x for x in args if x]) regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.?") @@ -72,47 +70,44 @@ def complete_all(prefix, completion_args): def parse_string(input): - options = [] - current_full = input[-1].lstrip('"\'') - current_prefix = current_full.split(' ')[-1] - prior = input[:-1] - completion_args = defaultdict(lambda: None) - if input[-1].startswith('--'): - options = ['--si', '--so', '--ji', '--jo', '--i'] - elif input[-1].startswith('-'): - options = ['-h', '-x', '-fx', '-l', '-c', '-C'] - elif len(prior) > 0 and prior[-1] == '-c': - if 'import'.startswith(current_full): + if current_raw(input).startswith('--'): + return ['--si', '--so', '--ji', '--jo', '--i'] + elif current_raw(input).startswith('-'): + return ['-h', '-x', '-fx', '-l', '-c', '-C'] + elif len(prior(input)) > 0 and prior(input)[-1] == '-c': + if 'import'.startswith(current_raw(input)): options = ["'import"] - elif current_full.startswith('import ') or current_full.startswith('from '): + elif current_raw(input).startswith('import ') or current_raw(input).startswith('from '): module_completion, module_list = get_completerlib() - options = module_completion(current_full) or [] + options = module_completion(current_raw(input)) or [] if options: - options = [x.rstrip(' ') for x in options if x.startswith(current_prefix)] + options = [x.rstrip(' ') for x in options if x.startswith(current_prefix(input))] else: - options = complete_all(current_prefix, completion_args) - if current_prefix.endswith('.'): + options = complete_all(current_prefix(input), defaultdict(lambda: None)) + if current_prefix(input).endswith('.'): options = [x for x in options if '._' not in x] + return options + elif current_raw(input) == '': + options = ['sys', 'json', 're', 'csv', 'datetime', 'hashlib', 'itertools', 'math', 'os', 'random', 'shutil'] + if '-x' in input[:-1] or '-fx' in input[:-1]: + options += 'x' + if '-l' in input[:-1]: + options += 'l' + return options else: - if current_full == '': - options = ['sys', 'json', 're', 'csv', 'datetime', 'hashlib', 'itertools', 'math', 'os', 'random', 'shutil'] - if '-x' in input[:-1] or '-fx' in input[:-1]: - options += 'x' - if '-l' in input[:-1]: - options += 'l' - else: - if '-x' in prior or '-fx' in prior: - completion_args['x_arg'] = True - if '-l' in prior: - completion_args['l_arg'] = True - if '-c' in prior: - c_index = prior.index('-c') - if (c_index + 1) < len(prior): - completion_args['c_arg'] = prior[c_index + 1] - options = complete_all(current_prefix, completion_args) - if current_prefix.endswith('.'): - options = [x for x in options if '._' not in x] - return options + completion_args = defaultdict(lambda: None) + if '-x' in prior(input) or '-fx' in prior(input): + completion_args['x_arg'] = True + if '-l' in prior(input): + completion_args['l_arg'] = True + if '-c' in prior(input): + c_index = prior(input).index('-c') + if (c_index + 1) < len(prior(input)): + completion_args['c_arg'] = prior(input)[c_index + 1] + options = complete_all(current_prefix(input), completion_args) + if current_prefix(input).endswith('.'): + options = [x for x in options if '._' not in x] + return options def get_completerlib(): @@ -134,7 +129,7 @@ def get_completerlib(): #from __future__ import print_function import inspect - #import os + import os #import re #import sys @@ -316,14 +311,18 @@ def main(): elif '<' in input or '>' in input: print('_longopt') return - options = list(set(parse_string(input))) + else: + options = list(set(parse_string(input))) + + if len(options) == 0: + return - if ' ' in input[-1] and max(map(len, options)) + 1 >= len(input[-1]): - options.append(input[-1].split(' ')[-1]) + if len(current_list(input)) > 1 and max(map(len, options)) + 1 >= len(current_raw(input)): + options.append(current_prefix(input)) - if len(options) <= 1: - options = options + [x + "'" for x in options] - print(' '.join(options)) + if len(options) <= 1: + options = options + [x + "'" for x in options] + print(' '.join(options)) if __name__ == '__main__': From e1b390118afe466f100b454ea2cb93cc53cb0a7f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 30 Jul 2014 16:29:16 -0700 Subject: [PATCH 163/254] Removed trailing parenthesis from tab completion options. --- extras/pycompleter | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index e393e6f..46db439 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -304,6 +304,12 @@ def get_completerlib(): return module_completion, module_list +def remove_trailing_paren(str_): + if str_.endswith('('): + return str_[:-1] + return str_ + + def main(): input = sys.argv[1:] if len(input) == 0: @@ -312,7 +318,7 @@ def main(): print('_longopt') return else: - options = list(set(parse_string(input))) + options = list(set(map(remove_trailing_paren, parse_string(input)))) if len(options) == 0: return From 3741965cd34156cea5b552c8ed5d54669d3294d3 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 30 Jul 2014 16:33:44 -0700 Subject: [PATCH 164/254] Added underscore to variable parsing for tab completion. --- extras/pycompleter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index 46db439..bd19e7e 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -15,7 +15,7 @@ def current_raw(input): def current_list(input): - return re.split(r'[^a-zA-Z0-9\.]', current_raw(input)) + return re.split(r'[^a-zA-Z0-9_\.]', current_raw(input)) def current_prefix(input): From 2eecb482df6340c88056ffb15680d8bf1fa80bfd Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 30 Jul 2014 16:47:09 -0700 Subject: [PATCH 165/254] Added ascii art to permission denied message. --- setup.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 5c9eaf2..d4cf54d 100644 --- a/setup.py +++ b/setup.py @@ -5,12 +5,14 @@ data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] else: print( -'''****************************************************************************** -PERMISSION DENIED: User does not have write access to /etc. -To add completion manually, run - source bash_completion.d/pycompletion.sh -from the install directory. -*****************************************************************************''') +'''************************************************************************** +___ ____ ____ _ _ _ ____ ____ _ ____ _ _ ___ ____ _ _ _ ____ ___ +|__] |___ |__/ |\/| | [__ [__ | | | |\ | | \ |___ |\ | | |___ | \ +| |___ | \ | | | ___] ___] | |__| | \| |__/ |___ | \| | |___ |__/ +PERMISSION DENIED: Cannot copy pycompletion.sh to /etc/bash_completion.d +To configure tab completion without root, run + source /path/to/install_directory/bash_completion.d/pycompletion.sh +**************************************************************************''') data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] setup( From 26d9bf1bf942b82e2ca89b6df28b8cc4853289a5 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 5 Aug 2014 15:10:48 -0700 Subject: [PATCH 166/254] Added source lookup to py. --- py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/py b/py index 8d820bb..e537ac7 100755 --- a/py +++ b/py @@ -21,6 +21,7 @@ def import_matches(query, prefix=''): except ImportError as e: pass + def lazy_imports(*args): query = ' '.join([x for x in args if x]) import_matches(query) @@ -30,6 +31,14 @@ def lazy_imports(*args): if 'defaultdict' in query: global defaultdict; from collections import defaultdict if 'groupby' in query: global groupby; from itertools import groupby + +def inspect_source(obj): + import inspect + try: + return ''.join(inspect.getsourcelines(obj)[0]) + except: + return help(obj) + parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter) @@ -75,7 +84,12 @@ else: if args.expression: args.expression = args.expression.replace("`", "'") if args.expression.startswith('?') or args.expression.endswith('?'): - args.expression = 'help(%s)' % args.expression.strip('?') + if args.expression.startswith('??') or args.expression.endswith('??'): + import inspect + #args.expression = "''.join(inspect.getsourcelines(%s)[0])" % args.expression.strip('?') + args.expression = "inspect_source(%s)" % args.expression.strip('?') + else: + args.expression = 'help(%s)' % args.expression.strip('?') if args.lines_of_stdin: from itertools import islice stdin = islice(stdin,1) From 9a48ba38aba15da8e382a7d9616fd5e9562ed40d Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 5 Aug 2014 15:22:39 -0700 Subject: [PATCH 167/254] Added support for help on multi-variable expressions. --- py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/py b/py index e537ac7..c05ee6e 100755 --- a/py +++ b/py @@ -32,6 +32,10 @@ def lazy_imports(*args): if 'groupby' in query: global groupby; from itertools import groupby +def current_list(input): + return re.split(r'[^a-zA-Z0-9_\.]', input) + + def inspect_source(obj): import inspect try: @@ -84,12 +88,18 @@ else: if args.expression: args.expression = args.expression.replace("`", "'") if args.expression.startswith('?') or args.expression.endswith('?'): - if args.expression.startswith('??') or args.expression.endswith('??'): + final_atom = current_list(args.expression.rstrip('?'))[-1] + first_atom = current_list(args.expression.lstrip('?'))[0] + if args.expression.startswith('??'): + import inspect + args.expression = "inspect_source(%s)" % first_atom + elif args.expression.endswith('??'): import inspect - #args.expression = "''.join(inspect.getsourcelines(%s)[0])" % args.expression.strip('?') - args.expression = "inspect_source(%s)" % args.expression.strip('?') + args.expression = "inspect_source(%s)" % final_atom + elif args.expression.startswith('?'): + args.expression = 'help(%s)' % first_atom else: - args.expression = 'help(%s)' % args.expression.strip('?') + args.expression = 'help(%s)' % final_atom if args.lines_of_stdin: from itertools import islice stdin = islice(stdin,1) From 051da19c3aa5b8513d952b5e03e71bf7bbd65139 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 5 Aug 2014 15:30:59 -0700 Subject: [PATCH 168/254] Made output of ?? use pydoc.pager instead of print. --- py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/py b/py index c05ee6e..2a73343 100755 --- a/py +++ b/py @@ -38,8 +38,10 @@ def current_list(input): def inspect_source(obj): import inspect + import pydoc try: - return ''.join(inspect.getsourcelines(obj)[0]) + pydoc.pager(''.join(inspect.getsourcelines(obj)[0])) + return None except: return help(obj) From af75159083e9210d1c276215a9c306adc81a47ab Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 6 Aug 2014 15:36:47 -0700 Subject: [PATCH 169/254] Added ? to README. --- README.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 8f0e456..dc20081 100644 --- a/README.rst +++ b/README.rst @@ -32,8 +32,11 @@ Access imports directly $ py 'random.random()' 0.103173957713 - $ py 'datetime.datetime.now()' - 2014-06-20 20:22:03.699290 + $ py 'datetime.datetime.now?' + Help on built-in function now: + + now(...) + [tz] -> new datetime with tz's local day and time. :: From 3cc2957a62ff195853383c329f565cb064bcca38 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 7 Aug 2014 13:11:04 -0700 Subject: [PATCH 170/254] Changed bash restart recommendation in installation section. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index dc20081..4846888 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Installation :: sudo pip install pythonpy - bash # restart your shell for tab completion to take effect + # restart your shell for tab completion to take effect :: From 55710a6323bda36285eee40099baa2ac9daa8ef7 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 8 Aug 2014 12:43:40 -0700 Subject: [PATCH 171/254] 0.3.4 --- py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/py b/py index 2a73343..a834014 100755 --- a/py +++ b/py @@ -60,7 +60,7 @@ parser.add_argument('-l', dest='list_of_stdin', action='store_const', 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('-V', '--version', action='version', version='py version 0.3.3', help='version info') +parser.add_argument('-V', '--version', action='version', version='py version 0.3.4', help='version info') parser.add_argument('--i', '--ignore_exceptions', dest='ignore_exceptions', action='store_const', const=True, default=False, diff --git a/setup.py b/setup.py index d4cf54d..b9ed0eb 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ setup( name='pythonpy', - version='0.3.3', + version='0.3.4', description='python -c, with tab completion and shorthand', scripts=['py', 'extras/py3', 'extras/pycompleter'], data_files=data_files, From 1db9705cce16269e686120d20b338266b03ab599 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 8 Aug 2014 13:05:44 -0700 Subject: [PATCH 172/254] Fixed bug from exception thrown in ipython code. --- extras/pycompleter | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extras/pycompleter b/extras/pycompleter index bd19e7e..436842b 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -62,7 +62,10 @@ def complete_all(prefix, completion_args): idx += 1 module_completion, module_list = get_completerlib() - options = module_completion("import " + prefix) or [] + try: + options = module_completion("import " + prefix) or [] + except: #module_completion may throw exception (e.g. on 'import sqlalchemy_utils.') + options = [] if options: options = [x.rstrip(' ') for x in options if x.startswith(prefix)] From 89f41d7ff1ce32b9e41d51a857e43ed52e131710 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 8 Aug 2014 16:47:12 -0700 Subject: [PATCH 173/254] Added ... | sh example to readme. --- README.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.rst b/README.rst index 4846888..3a75324 100644 --- a/README.rst +++ b/README.rst @@ -83,6 +83,15 @@ Append ".txt" to each line of input :: +Append ".txt" to every file in the directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + $ ls | py -x '"mv %s %s.txt" % (x,x)' | sh + +:: + Get only even numbers ~~~~~~~~~~~~~~~~~~~~~ From eab5b36bf9ceb45ccba2af7b908d5ef5b0082bfd Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Aug 2014 12:09:12 -0700 Subject: [PATCH 174/254] Changed python hashbang to python2 for arch linux support. --- py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py b/py index a834014..74b7d2b 100755 --- a/py +++ b/py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 from __future__ import (unicode_literals, absolute_import, print_function, division) from signal import signal, SIGPIPE, SIG_DFL From c47b04c80a11b04ee579ff5e19e8404e789e8d7c Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Aug 2014 14:24:11 -0700 Subject: [PATCH 175/254] Removed angry PERMISSION DENIED message. --- setup.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index b9ed0eb..4e44415 100644 --- a/setup.py +++ b/setup.py @@ -5,14 +5,17 @@ data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] else: print( -'''************************************************************************** -___ ____ ____ _ _ _ ____ ____ _ ____ _ _ ___ ____ _ _ _ ____ ___ -|__] |___ |__/ |\/| | [__ [__ | | | |\ | | \ |___ |\ | | |___ | \ -| |___ | \ | | | ___] ___] | |__| | \| |__/ |___ | \| | |___ |__/ -PERMISSION DENIED: Cannot copy pycompletion.sh to /etc/bash_completion.d -To configure tab completion without root, run - source /path/to/install_directory/bash_completion.d/pycompletion.sh -**************************************************************************''') +'''****************************************************************************** +Looks like you didn't run this command using sudo. +Pythonpy needs root privileges to copy pycompletion.sh to /etc/bash_completion.d +1) If you are in a virtualenv, you can configure tab completion without root using: + source /path/to/virtualenv/bash_completion.d/pycompletion.sh +2) If you aren't using virtualenv, remember that pip requires sudo by default + on most systems. py is a simple python script does not require any + root access or special privileges. If you don't like using root, + learn virtualenv and refer to 1). +Installation proceeding without root access... +******************************************************************************''') data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] setup( From 0283233659fb3faa4eb8a31d6d97f6cf520d5cc5 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Aug 2014 14:32:05 -0700 Subject: [PATCH 176/254] Changed README example for filenames. --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3a75324..7ccdc96 100644 --- a/README.rst +++ b/README.rst @@ -88,7 +88,9 @@ Append ".txt" to every file in the directory :: - $ ls | py -x '"mv %s %s.txt" % (x,x)' | sh + $ ls | py -x '"mv `%s` `%s.txt`" % (x,x)' | sh + # sharp quotes are swapped out for single quotes + # single quotes handle spaces in filenames :: From a650bb248f280dbc003d3508a3948dc13fbc7ce2 Mon Sep 17 00:00:00 2001 From: Abhishek Mukherjee Date: Tue, 12 Aug 2014 19:37:36 -0400 Subject: [PATCH 177/254] Fix missing comma -> wrong option names --- py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py b/py index 74b7d2b..4034ef8 100755 --- a/py +++ b/py @@ -69,11 +69,11 @@ 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', +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', +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)') From 75fb9b358f7ff1129e3f00600596b5328fb9dc74 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Aug 2014 18:12:35 -0700 Subject: [PATCH 178/254] Changed name of split_input and split_output argument to delimiter for more useful help. --- py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/py b/py index 4034ef8..07f0ff7 100755 --- a/py +++ b/py @@ -65,9 +65,9 @@ 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', +parser.add_argument('--si', '--split_input', dest='input_delimiter', help='pre-process each row with re.split(delimiter, row)') -parser.add_argument('--so', '--split_output', dest='split_output', +parser.add_argument('--so', '--split_output', dest='output_delimiter', help='post-process each row with delimiter.join(row)') parser.add_argument('--ji', '--json_input', dest='json_input', action='store_const', @@ -82,8 +82,8 @@ 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) +elif args.input_delimiter: + stdin = (re.split(args.input_delimiter, x.rstrip()) for x in sys.stdin) else: stdin = (x.rstrip() for x in sys.stdin) @@ -142,8 +142,8 @@ def format(output): return None elif args.json_output: return json.dumps(output) - elif args.split_output: - return args.split_output.join(output) + elif args.output_delimiter: + return args.output_delimiter.join(output) else: return output From 774b9101fe653c0e8a7db1e9ea5ae22c379c0330 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Aug 2014 18:51:55 -0700 Subject: [PATCH 179/254] Reordered command arguments for cleaner help display. --- py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/py b/py index 07f0ff7..1a8059d 100755 --- a/py +++ b/py @@ -61,14 +61,6 @@ parser.add_argument('-l', dest='list_of_stdin', action='store_const', 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('-V', '--version', action='version', version='py version 0.3.4', help='version info') -parser.add_argument('--i', '--ignore_exceptions', - dest='ignore_exceptions', action='store_const', - const=True, default=False, - help='') -parser.add_argument('--si', '--split_input', dest='input_delimiter', - help='pre-process each row with re.split(delimiter, row)') -parser.add_argument('--so', '--split_output', dest='output_delimiter', - 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, @@ -77,6 +69,14 @@ 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)') +parser.add_argument('--si', '--split_input', dest='input_delimiter', + help='pre-process each row with re.split(delimiter, row)') +parser.add_argument('--so', '--split_output', dest='output_delimiter', + help='post-process each row with delimiter.join(row)') +parser.add_argument('--i', '--ignore_exceptions', + dest='ignore_exceptions', action='store_const', + const=True, default=False, + help='Wrap try-except-pass around each row of -x or -fx') args = parser.parse_args() From 7b091f14f850ed80722e61a86e712dc2d7dcde22 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Aug 2014 19:09:12 -0700 Subject: [PATCH 180/254] Updated py3 to match py and added tab completion support. --- extras/py3 | 88 +++++++---- extras/pycompleter | 2 +- extras/pycompleter3 | 338 +++++++++++++++++++++++++++++++++++++++++ extras/pycompletion.sh | 10 ++ setup.py | 2 +- 5 files changed, 412 insertions(+), 28 deletions(-) create mode 100755 extras/pycompleter3 diff --git a/extras/py3 b/extras/py3 index beb00b5..4466f30 100755 --- a/extras/py3 +++ b/extras/py3 @@ -1,8 +1,4 @@ #!/usr/bin/env python3 -''' -Just like py but using python3! -Tab completion not yet supported :(. -''' from __future__ import (unicode_literals, absolute_import, print_function, division) from signal import signal, SIGPIPE, SIG_DFL @@ -14,24 +10,41 @@ import json import re from collections import Iterable -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 '')) - regex = re.compile("([a-zA-z0-9_]+)\.") - matches = regex.findall(query) +def import_matches(query, prefix=''): + matches = set(re.findall(r"(%s[a-zA-Z_][a-zA-Z0-9_]*)\.?" % prefix, query)) for module_name in matches: try: module = __import__(module_name) globals()[module_name] = module + import_matches(query, prefix='%s.' % module_name) except ImportError as e: pass + +def lazy_imports(*args): + query = ' '.join([x for x in args if x]) + import_matches(query) + if 'Counter' in query: global Counter; from collections import Counter if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict + if 'defaultdict' in query: global defaultdict; from collections import defaultdict if 'groupby' in query: global groupby; from itertools import groupby + +def current_list(input): + return re.split(r'[^a-zA-Z0-9_\.]', input) + + +def inspect_source(obj): + import inspect + import pydoc + try: + pydoc.pager(''.join(inspect.getsourcelines(obj)[0])) + return None + except: + return help(obj) + parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter) @@ -47,34 +60,51 @@ parser.add_argument('-l', dest='list_of_stdin', action='store_const', 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', +parser.add_argument('-V', '--version', action='version', version='py version 0.3.4', help='version info') +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', +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)') +parser.add_argument('--si', '--split_input', dest='input_delimiter', + help='pre-process each row with re.split(delimiter, row)') +parser.add_argument('--so', '--split_output', dest='output_delimiter', + help='post-process each row with delimiter.join(row)') +parser.add_argument('--i', '--ignore_exceptions', + dest='ignore_exceptions', action='store_const', + const=True, default=False, + help='Wrap try-except-pass around each row of -x or -fx') 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) +elif args.input_delimiter: + stdin = (re.split(args.input_delimiter, 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.expression.startswith('?') or args.expression.endswith('?'): + final_atom = current_list(args.expression.rstrip('?'))[-1] + first_atom = current_list(args.expression.lstrip('?'))[0] + if args.expression.startswith('??'): + import inspect + args.expression = "inspect_source(%s)" % first_atom + elif args.expression.endswith('??'): + import inspect + args.expression = "inspect_source(%s)" % final_atom + elif args.expression.startswith('?'): + args.expression = 'help(%s)' % first_atom + else: + args.expression = 'help(%s)' % final_atom + if args.lines_of_stdin: + from itertools import islice + stdin = islice(stdin,1) if args.pre_cmd: args.pre_cmd = args.pre_cmd.replace("`", "'") if args.post_cmd: @@ -112,8 +142,8 @@ def format(output): return None elif args.json_output: return json.dumps(output) - elif args.split_output: - return args.split_output.join(output) + elif args.output_delimiter: + return args.output_delimiter.join(output) else: return output @@ -122,11 +152,17 @@ if isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstan for x in result: formatted = format(x) if formatted is not None: - print(formatted) + try: + print(formatted) + except UnicodeEncodeError: + print(formatted.encode('utf-8')) else: formatted = format(result) if formatted is not None: - print(formatted) + try: + print(formatted) + except UnicodeEncodeError: + print(formatted.encode('utf-8')) if args.post_cmd: exec(args.post_cmd) diff --git a/extras/pycompleter b/extras/pycompleter index 436842b..3bdb35f 100755 --- a/extras/pycompleter +++ b/extras/pycompleter @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 from __future__ import (unicode_literals, absolute_import, print_function, division) import sys diff --git a/extras/pycompleter3 b/extras/pycompleter3 new file mode 100755 index 0000000..fec2192 --- /dev/null +++ b/extras/pycompleter3 @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +from __future__ import (unicode_literals, absolute_import, + print_function, division) +import sys +import re +from collections import defaultdict + +import rlcompleter + + +def current_raw(input): + if len(input[-1]) > 0 and input[-1][0] in '"\'': + return input[-1][1:] + return input[-1] + + +def current_list(input): + return re.split(r'[^a-zA-Z0-9_\.]', current_raw(input)) + + +def current_prefix(input): + return current_list(input)[-1] + + +def prior(input): + return input[:-1] + + +def lazy_imports(*args): + query = ' '.join([x for x in args if x]) + regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.?") + matches = regex.findall(query) + for module_name in matches: + try: + module = __import__(module_name) + globals()[module_name] = module + except ImportError as e: + pass + + if 'Counter' in query: global Counter; from collections import Counter + if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict + if 'defaultdict' in query: global defaultdict; from collections import defaultdict + if 'groupby' in query: global groupby; from itertools import groupby + + +def complete_all(prefix, completion_args): + lazy_imports(prefix, completion_args['c_arg']) + if completion_args: + if completion_args['x_arg']: + x = str() + if completion_args['l_arg']: + l = list() + if completion_args['c_arg']: + exec(completion_args['c_arg'].strip('"\'').replace("`", "'")) + context = locals() + context.update(globals()) + completer = rlcompleter.Completer(context) + idx = 0 + options_set = set() + while completer.complete(prefix, idx): + options_set.add(completer.complete(prefix, idx)) + idx += 1 + + module_completion, module_list = get_completerlib() + try: + options = module_completion("import " + prefix) or [] + except: #module_completion may throw exception (e.g. on 'import sqlalchemy_utils.') + options = [] + if options: + options = [x.rstrip(' ') for x in options if x.startswith(prefix)] + + return options + list(options_set) + + +def parse_string(input): + if current_raw(input).startswith('--'): + return ['--si', '--so', '--ji', '--jo', '--i'] + elif current_raw(input).startswith('-'): + return ['-h', '-x', '-fx', '-l', '-c', '-C'] + elif len(prior(input)) > 0 and prior(input)[-1] == '-c': + if 'import'.startswith(current_raw(input)): + options = ["'import"] + elif current_raw(input).startswith('import ') or current_raw(input).startswith('from '): + module_completion, module_list = get_completerlib() + options = module_completion(current_raw(input)) or [] + if options: + options = [x.rstrip(' ') for x in options if x.startswith(current_prefix(input))] + else: + options = complete_all(current_prefix(input), defaultdict(lambda: None)) + if current_prefix(input).endswith('.'): + options = [x for x in options if '._' not in x] + return options + elif current_raw(input) == '': + options = ['sys', 'json', 're', 'csv', 'datetime', 'hashlib', 'itertools', 'math', 'os', 'random', 'shutil'] + if '-x' in input[:-1] or '-fx' in input[:-1]: + options += 'x' + if '-l' in input[:-1]: + options += 'l' + return options + else: + completion_args = defaultdict(lambda: None) + if '-x' in prior(input) or '-fx' in prior(input): + completion_args['x_arg'] = True + if '-l' in prior(input): + completion_args['l_arg'] = True + if '-c' in prior(input): + c_index = prior(input).index('-c') + if (c_index + 1) < len(prior(input)): + completion_args['c_arg'] = prior(input)[c_index + 1] + options = complete_all(current_prefix(input), completion_args) + if current_prefix(input).endswith('.'): + options = [x for x in options if '._' not in x] + return options + + +def get_completerlib(): + """Implementations for various useful completers. + + These are all loaded by default by IPython. + """ + #----------------------------------------------------------------------------- + # Copyright (C) 2010-2011 The IPython Development Team. + # + # Distributed under the terms of the BSD License. + # + # The full license is in the file COPYING.txt, distributed with this software. + #----------------------------------------------------------------------------- + + #----------------------------------------------------------------------------- + # Imports + #----------------------------------------------------------------------------- + #from __future__ import print_function + + import inspect + import os + #import re + #import sys + + try: + # Python >= 3.3 + from importlib.machinery import all_suffixes + _suffixes = all_suffixes() + except ImportError: + from imp import get_suffixes + _suffixes = [ s[0] for s in get_suffixes() ] + + # Third-party imports + from time import time + from zipimport import zipimporter + + TIMEOUT_STORAGE = 2 + + TIMEOUT_GIVEUP = 20 + + # Regular expression for the python import statement + import_re = re.compile(r'(?P[a-zA-Z_][a-zA-Z0-9_]*?)' + r'(?P[/\\]__init__)?' + r'(?P%s)$' % + r'|'.join(re.escape(s) for s in _suffixes)) + + # RE for the ipython %run command (python + ipython scripts) + magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$') + + def module_list(path): + """ + Return the list containing the names of the modules available in the given + folder. + """ + # sys.path has the cwd as an empty string, but isdir/listdir need it as '.' + if path == '': + path = '.' + + # A few local constants to be used in loops below + pjoin = os.path.join + + if os.path.isdir(path): + # Build a list of all files in the directory and all files + # in its subdirectories. For performance reasons, do not + # recurse more than one level into subdirectories. + files = [] + for root, dirs, nondirs in os.walk(path): + subdir = root[len(path)+1:] + if subdir: + files.extend(pjoin(subdir, f) for f in nondirs) + dirs[:] = [] # Do not recurse into additional subdirectories. + else: + files.extend(nondirs) + + else: + try: + files = list(zipimporter(path)._files.keys()) + except: + files = [] + + # Build a list of modules which match the import_re regex. + modules = [] + for f in files: + m = import_re.match(f) + if m: + modules.append(m.group('name')) + return list(set(modules)) + + + def get_root_modules(): + """ + Returns a list containing the names of all the modules available in the + folders of the pythonpath. + + ip.db['rootmodules_cache'] maps sys.path entries to list of modules. + """ + #ip = get_ipython() + #rootmodules_cache = ip.db.get('rootmodules_cache', {}) + rootmodules_cache = {} + rootmodules = list(sys.builtin_module_names) + start_time = time() + #store = False + for path in sys.path: + try: + modules = rootmodules_cache[path] + except KeyError: + modules = module_list(path) + try: + modules.remove('__init__') + except ValueError: + pass + if path not in ('', '.'): # cwd modules should not be cached + rootmodules_cache[path] = modules + if time() - start_time > TIMEOUT_STORAGE and not store: + #store = True + #print("\nCaching the list of root modules, please wait!") + #print("(This will only be done once - type '%rehashx' to " + #"reset cache!)\n") + sys.stdout.flush() + if time() - start_time > TIMEOUT_GIVEUP: + print("This is taking too long, we give up.\n") + return [] + rootmodules.extend(modules) + #if store: + #ip.db['rootmodules_cache'] = rootmodules_cache + rootmodules = list(set(rootmodules)) + return rootmodules + + + def is_importable(module, attr, only_modules): + if only_modules: + return inspect.ismodule(getattr(module, attr)) + else: + return not(attr[:2] == '__' and attr[-2:] == '__') + + + def try_import(mod, only_modules=False): + try: + m = __import__(mod) + except: + return [] + mods = mod.split('.') + for module in mods[1:]: + m = getattr(m, module) + + m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__ + + completions = [] + if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init: + completions.extend( [attr for attr in dir(m) if + is_importable(m, attr, only_modules)]) + + completions.extend(getattr(m, '__all__', [])) + if m_is_init: + completions.extend(module_list(os.path.dirname(m.__file__))) + completions = set(completions) + if '__init__' in completions: + completions.remove('__init__') + return list(completions) + + + def module_completion(line): + """ + Returns a list containing the completion possibilities for an import line. + + The line looks like this : + 'import xml.d' + 'from xml.dom import' + """ + + words = line.split(' ') + nwords = len(words) + + # from whatever -> 'import ' + if nwords == 3 and words[0] == 'from': + return ['import '] + + # 'from xy' or 'import xy' + if nwords < 3 and (words[0] in ['import','from']) : + if nwords == 1: + return get_root_modules() + mod = words[1].split('.') + if len(mod) < 2: + return get_root_modules() + completion_list = try_import('.'.join(mod[:-1]), True) + return ['.'.join(mod[:-1] + [el]) for el in completion_list] + + # 'from xyz import abc' + if nwords >= 3 and words[0] == 'from': + mod = words[1] + return try_import(mod) + + return module_completion, module_list + + +def remove_trailing_paren(str_): + if str_.endswith('('): + return str_[:-1] + return str_ + + +def main(): + input = sys.argv[1:] + if len(input) == 0: + return + elif '<' in input or '>' in input: + print('_longopt') + return + else: + options = list(set(map(remove_trailing_paren, parse_string(input)))) + + if len(options) == 0: + return + + if len(current_list(input)) > 1 and max(map(len, options)) + 1 >= len(current_raw(input)): + options.append(current_prefix(input)) + + if len(options) <= 1: + options = options + [x + "'" for x in options] + print(' '.join(options)) + + +if __name__ == '__main__': + main() diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index c37e48b..a7cc966 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -7,4 +7,14 @@ _py() fi } +_py3() +{ + COMPREPLY=($(pycompleter3 "${COMP_WORDS[@]}" 2>/dev/null )) + if [[ ${COMPREPLY[0]} == '_longopt' ]]; then + COMPREPLY=() + _longopt 2>/dev/null + fi +} + complete -F _py -o nospace py +complete -F _py3 -o nospace py3 diff --git a/setup.py b/setup.py index 4e44415..f1361a8 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ name='pythonpy', version='0.3.4', description='python -c, with tab completion and shorthand', - scripts=['py', 'extras/py3', 'extras/pycompleter'], + scripts=['py', 'extras/py3', 'extras/pycompleter', 'extras/pycompleter3'], data_files=data_files, license='MIT', url='https://github.com/Russell91/pythonpy', From c8e325fff99727b1981e523139e9effd2c0668fb Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Aug 2014 19:12:37 -0700 Subject: [PATCH 181/254] Changed version message on py3. --- extras/py3 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/py3 b/extras/py3 index 4466f30..1e909a4 100755 --- a/extras/py3 +++ b/extras/py3 @@ -60,7 +60,7 @@ parser.add_argument('-l', dest='list_of_stdin', action='store_const', 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('-V', '--version', action='version', version='py version 0.3.4', help='version info') +parser.add_argument('-V', '--version', action='version', version='py3 version 0.3.4', help='version info') parser.add_argument('--ji', '--json_input', dest='json_input', action='store_const', const=True, default=False, From 954d4c56a6d38ba47307339fe576a77faf417f53 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Aug 2014 22:34:30 -0700 Subject: [PATCH 182/254] Added fancy error for multiline json files. --- py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/py b/py index 1a8059d..2778130 100755 --- a/py +++ b/py @@ -81,7 +81,19 @@ parser.add_argument('--i', '--ignore_exceptions', args = parser.parse_args() if args.json_input: - stdin = (json.loads(x.rstrip()) for x in sys.stdin) + def loads(str_): + try: + return json.loads(str_.rstrip()) + except Exception as ex: + if args.ignore_exceptions: + pass + else: + if sum(1 for x in sys.stdin) > 0: + sys.stderr.write( +"Json expressions are assumed to be oneline. Try piping input into py -l '\"\".join(l)'\n" +"first if you have a multi-line json file and not a file with multiple lines of json\n") + raise ex + stdin = (loads(x) for x in sys.stdin) elif args.input_delimiter: stdin = (re.split(args.input_delimiter, x.rstrip()) for x in sys.stdin) else: From 386c8e64ccebda4ad74b1995a17ef123957e02c0 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Aug 2014 22:38:27 -0700 Subject: [PATCH 183/254] Reformatted previous error. --- py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/py b/py index 2778130..fa51aaf 100755 --- a/py +++ b/py @@ -90,8 +90,9 @@ if args.json_input: else: if sum(1 for x in sys.stdin) > 0: sys.stderr.write( -"Json expressions are assumed to be oneline. Try piping input into py -l '\"\".join(l)'\n" -"first if you have a multi-line json file and not a file with multiple lines of json\n") +"""Json expressions are assumed to be oneline. Try piping input into py -l '"".join(l)' +first if you have a multi-line json file and not a file with multiple lines of json. +""") raise ex stdin = (loads(x) for x in sys.stdin) elif args.input_delimiter: From 5590bcf41cc26c1a67fc81bf068669524995fca8 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 15 Aug 2014 12:58:20 -0700 Subject: [PATCH 184/254] Changed json file hint. --- py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py b/py index fa51aaf..5b53250 100755 --- a/py +++ b/py @@ -90,8 +90,8 @@ if args.json_input: else: if sum(1 for x in sys.stdin) > 0: sys.stderr.write( -"""Json expressions are assumed to be oneline. Try piping input into py -l '"".join(l)' -first if you have a multi-line json file and not a file with multiple lines of json. +"""Hint: --ji requies oneline json strings. Use py 'json.load(sys.stdin)' +if you have a multi-line json file and not a file with multiple lines of json. """) raise ex stdin = (loads(x) for x in sys.stdin) From e92c926119aaf2d049934002130570bf31eef5a1 Mon Sep 17 00:00:00 2001 From: Razzi Abuissa Date: Sat, 16 Aug 2014 20:55:42 -0400 Subject: [PATCH 185/254] Correctly use 0-based indexing --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 7ccdc96..d0066fd 100644 --- a/README.rst +++ b/README.rst @@ -77,9 +77,9 @@ Append ".txt" to each line of input :: $ py 'range(3)' | py -x 'x + ".txt"' + 0.txt 1.txt 2.txt - 3.txt :: From d68ed2c8567c8f289e9981cdae4a495571e85b9a Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 19 Aug 2014 10:53:24 -0700 Subject: [PATCH 186/254] Simplified --i help message. --- py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py b/py index 5b53250..20ad9f2 100755 --- a/py +++ b/py @@ -76,7 +76,7 @@ parser.add_argument('--so', '--split_output', dest='output_delimiter', parser.add_argument('--i', '--ignore_exceptions', dest='ignore_exceptions', action='store_const', const=True, default=False, - help='Wrap try-except-pass around each row of -x or -fx') + help='Wrap try-except-pass around each row') args = parser.parse_args() From 6158969bb32ff23abe8f55315a4dd252783eb058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20=E2=80=9CKwpolska=E2=80=9D=20Warrick?= Date: Wed, 20 Aug 2014 21:01:34 +0200 Subject: [PATCH 187/254] fix #34 -- use entry_points to get proper scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a fix to the shebang issue. The following scripts are created: * py * py2 | py3 * py2.6 | py2.7 | py3.3 | py3.4 The shebang is of the interpreter that was used to install. py2.5 and earlier, py3.2 and earlier, and py3.5 and later are (currently) unsupported because (a) I’m lazy and (b) there is no way to automate creation of completion functions. Bonus: if someone manages to install it under Windows (which is currently impossible), they will also get .exe versions of those (that will possibly collide with py.exe shipped with Python 3.x depending on %PATH% — see #31). Signed-off-by: Chris “Kwpolska” Warrick --- extras/py3 | 168 --------- extras/pycompleter3 | 338 ------------------ extras/pycompletion.sh | 50 +++ pythonpy/__init__.py | 0 py => pythonpy/__main__.py | 10 +- extras/pycompleter => pythonpy/pycompleter.py | 0 setup.py | 28 +- 7 files changed, 80 insertions(+), 514 deletions(-) delete mode 100755 extras/py3 delete mode 100755 extras/pycompleter3 create mode 100755 pythonpy/__init__.py rename py => pythonpy/__main__.py (97%) rename extras/pycompleter => pythonpy/pycompleter.py (100%) mode change 100644 => 100755 setup.py diff --git a/extras/py3 b/extras/py3 deleted file mode 100755 index 1e909a4..0000000 --- a/extras/py3 +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python3 -from __future__ import (unicode_literals, absolute_import, - print_function, division) -from signal import signal, SIGPIPE, SIG_DFL -signal(SIGPIPE,SIG_DFL) - -import argparse -import sys -import json -import re -from collections import Iterable - - -def import_matches(query, prefix=''): - matches = set(re.findall(r"(%s[a-zA-Z_][a-zA-Z0-9_]*)\.?" % prefix, query)) - for module_name in matches: - try: - module = __import__(module_name) - globals()[module_name] = module - import_matches(query, prefix='%s.' % module_name) - except ImportError as e: - pass - - -def lazy_imports(*args): - query = ' '.join([x for x in args if x]) - import_matches(query) - - if 'Counter' in query: global Counter; from collections import Counter - if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict - if 'defaultdict' in query: global defaultdict; from collections import defaultdict - if 'groupby' in query: global groupby; from itertools import groupby - - -def current_list(input): - return re.split(r'[^a-zA-Z0-9_\.]', input) - - -def inspect_source(obj): - import inspect - import pydoc - try: - pydoc.pager(''.join(inspect.getsourcelines(obj)[0])) - return None - except: - return help(obj) - -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('-V', '--version', action='version', version='py3 version 0.3.4', help='version info') -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)') -parser.add_argument('--si', '--split_input', dest='input_delimiter', - help='pre-process each row with re.split(delimiter, row)') -parser.add_argument('--so', '--split_output', dest='output_delimiter', - help='post-process each row with delimiter.join(row)') -parser.add_argument('--i', '--ignore_exceptions', - dest='ignore_exceptions', action='store_const', - const=True, default=False, - help='Wrap try-except-pass around each row of -x or -fx') - -args = parser.parse_args() - -if args.json_input: - stdin = (json.loads(x.rstrip()) for x in sys.stdin) -elif args.input_delimiter: - stdin = (re.split(args.input_delimiter, 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.expression.startswith('?') or args.expression.endswith('?'): - final_atom = current_list(args.expression.rstrip('?'))[-1] - first_atom = current_list(args.expression.lstrip('?'))[0] - if args.expression.startswith('??'): - import inspect - args.expression = "inspect_source(%s)" % first_atom - elif args.expression.endswith('??'): - import inspect - args.expression = "inspect_source(%s)" % final_atom - elif args.expression.startswith('?'): - args.expression = 'help(%s)' % first_atom - else: - args.expression = 'help(%s)' % final_atom - if args.lines_of_stdin: - from itertools import islice - stdin = islice(stdin,1) -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.output_delimiter: - return args.output_delimiter.join(output) - else: - return output - - -if isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstance(result, str): - for x in result: - formatted = format(x) - if formatted is not None: - try: - print(formatted) - except UnicodeEncodeError: - print(formatted.encode('utf-8')) -else: - formatted = format(result) - if formatted is not None: - try: - print(formatted) - except UnicodeEncodeError: - print(formatted.encode('utf-8')) - -if args.post_cmd: - exec(args.post_cmd) diff --git a/extras/pycompleter3 b/extras/pycompleter3 deleted file mode 100755 index fec2192..0000000 --- a/extras/pycompleter3 +++ /dev/null @@ -1,338 +0,0 @@ -#!/usr/bin/env python3 -from __future__ import (unicode_literals, absolute_import, - print_function, division) -import sys -import re -from collections import defaultdict - -import rlcompleter - - -def current_raw(input): - if len(input[-1]) > 0 and input[-1][0] in '"\'': - return input[-1][1:] - return input[-1] - - -def current_list(input): - return re.split(r'[^a-zA-Z0-9_\.]', current_raw(input)) - - -def current_prefix(input): - return current_list(input)[-1] - - -def prior(input): - return input[:-1] - - -def lazy_imports(*args): - query = ' '.join([x for x in args if x]) - regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.?") - matches = regex.findall(query) - for module_name in matches: - try: - module = __import__(module_name) - globals()[module_name] = module - except ImportError as e: - pass - - if 'Counter' in query: global Counter; from collections import Counter - if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict - if 'defaultdict' in query: global defaultdict; from collections import defaultdict - if 'groupby' in query: global groupby; from itertools import groupby - - -def complete_all(prefix, completion_args): - lazy_imports(prefix, completion_args['c_arg']) - if completion_args: - if completion_args['x_arg']: - x = str() - if completion_args['l_arg']: - l = list() - if completion_args['c_arg']: - exec(completion_args['c_arg'].strip('"\'').replace("`", "'")) - context = locals() - context.update(globals()) - completer = rlcompleter.Completer(context) - idx = 0 - options_set = set() - while completer.complete(prefix, idx): - options_set.add(completer.complete(prefix, idx)) - idx += 1 - - module_completion, module_list = get_completerlib() - try: - options = module_completion("import " + prefix) or [] - except: #module_completion may throw exception (e.g. on 'import sqlalchemy_utils.') - options = [] - if options: - options = [x.rstrip(' ') for x in options if x.startswith(prefix)] - - return options + list(options_set) - - -def parse_string(input): - if current_raw(input).startswith('--'): - return ['--si', '--so', '--ji', '--jo', '--i'] - elif current_raw(input).startswith('-'): - return ['-h', '-x', '-fx', '-l', '-c', '-C'] - elif len(prior(input)) > 0 and prior(input)[-1] == '-c': - if 'import'.startswith(current_raw(input)): - options = ["'import"] - elif current_raw(input).startswith('import ') or current_raw(input).startswith('from '): - module_completion, module_list = get_completerlib() - options = module_completion(current_raw(input)) or [] - if options: - options = [x.rstrip(' ') for x in options if x.startswith(current_prefix(input))] - else: - options = complete_all(current_prefix(input), defaultdict(lambda: None)) - if current_prefix(input).endswith('.'): - options = [x for x in options if '._' not in x] - return options - elif current_raw(input) == '': - options = ['sys', 'json', 're', 'csv', 'datetime', 'hashlib', 'itertools', 'math', 'os', 'random', 'shutil'] - if '-x' in input[:-1] or '-fx' in input[:-1]: - options += 'x' - if '-l' in input[:-1]: - options += 'l' - return options - else: - completion_args = defaultdict(lambda: None) - if '-x' in prior(input) or '-fx' in prior(input): - completion_args['x_arg'] = True - if '-l' in prior(input): - completion_args['l_arg'] = True - if '-c' in prior(input): - c_index = prior(input).index('-c') - if (c_index + 1) < len(prior(input)): - completion_args['c_arg'] = prior(input)[c_index + 1] - options = complete_all(current_prefix(input), completion_args) - if current_prefix(input).endswith('.'): - options = [x for x in options if '._' not in x] - return options - - -def get_completerlib(): - """Implementations for various useful completers. - - These are all loaded by default by IPython. - """ - #----------------------------------------------------------------------------- - # Copyright (C) 2010-2011 The IPython Development Team. - # - # Distributed under the terms of the BSD License. - # - # The full license is in the file COPYING.txt, distributed with this software. - #----------------------------------------------------------------------------- - - #----------------------------------------------------------------------------- - # Imports - #----------------------------------------------------------------------------- - #from __future__ import print_function - - import inspect - import os - #import re - #import sys - - try: - # Python >= 3.3 - from importlib.machinery import all_suffixes - _suffixes = all_suffixes() - except ImportError: - from imp import get_suffixes - _suffixes = [ s[0] for s in get_suffixes() ] - - # Third-party imports - from time import time - from zipimport import zipimporter - - TIMEOUT_STORAGE = 2 - - TIMEOUT_GIVEUP = 20 - - # Regular expression for the python import statement - import_re = re.compile(r'(?P[a-zA-Z_][a-zA-Z0-9_]*?)' - r'(?P[/\\]__init__)?' - r'(?P%s)$' % - r'|'.join(re.escape(s) for s in _suffixes)) - - # RE for the ipython %run command (python + ipython scripts) - magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$') - - def module_list(path): - """ - Return the list containing the names of the modules available in the given - folder. - """ - # sys.path has the cwd as an empty string, but isdir/listdir need it as '.' - if path == '': - path = '.' - - # A few local constants to be used in loops below - pjoin = os.path.join - - if os.path.isdir(path): - # Build a list of all files in the directory and all files - # in its subdirectories. For performance reasons, do not - # recurse more than one level into subdirectories. - files = [] - for root, dirs, nondirs in os.walk(path): - subdir = root[len(path)+1:] - if subdir: - files.extend(pjoin(subdir, f) for f in nondirs) - dirs[:] = [] # Do not recurse into additional subdirectories. - else: - files.extend(nondirs) - - else: - try: - files = list(zipimporter(path)._files.keys()) - except: - files = [] - - # Build a list of modules which match the import_re regex. - modules = [] - for f in files: - m = import_re.match(f) - if m: - modules.append(m.group('name')) - return list(set(modules)) - - - def get_root_modules(): - """ - Returns a list containing the names of all the modules available in the - folders of the pythonpath. - - ip.db['rootmodules_cache'] maps sys.path entries to list of modules. - """ - #ip = get_ipython() - #rootmodules_cache = ip.db.get('rootmodules_cache', {}) - rootmodules_cache = {} - rootmodules = list(sys.builtin_module_names) - start_time = time() - #store = False - for path in sys.path: - try: - modules = rootmodules_cache[path] - except KeyError: - modules = module_list(path) - try: - modules.remove('__init__') - except ValueError: - pass - if path not in ('', '.'): # cwd modules should not be cached - rootmodules_cache[path] = modules - if time() - start_time > TIMEOUT_STORAGE and not store: - #store = True - #print("\nCaching the list of root modules, please wait!") - #print("(This will only be done once - type '%rehashx' to " - #"reset cache!)\n") - sys.stdout.flush() - if time() - start_time > TIMEOUT_GIVEUP: - print("This is taking too long, we give up.\n") - return [] - rootmodules.extend(modules) - #if store: - #ip.db['rootmodules_cache'] = rootmodules_cache - rootmodules = list(set(rootmodules)) - return rootmodules - - - def is_importable(module, attr, only_modules): - if only_modules: - return inspect.ismodule(getattr(module, attr)) - else: - return not(attr[:2] == '__' and attr[-2:] == '__') - - - def try_import(mod, only_modules=False): - try: - m = __import__(mod) - except: - return [] - mods = mod.split('.') - for module in mods[1:]: - m = getattr(m, module) - - m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__ - - completions = [] - if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init: - completions.extend( [attr for attr in dir(m) if - is_importable(m, attr, only_modules)]) - - completions.extend(getattr(m, '__all__', [])) - if m_is_init: - completions.extend(module_list(os.path.dirname(m.__file__))) - completions = set(completions) - if '__init__' in completions: - completions.remove('__init__') - return list(completions) - - - def module_completion(line): - """ - Returns a list containing the completion possibilities for an import line. - - The line looks like this : - 'import xml.d' - 'from xml.dom import' - """ - - words = line.split(' ') - nwords = len(words) - - # from whatever -> 'import ' - if nwords == 3 and words[0] == 'from': - return ['import '] - - # 'from xy' or 'import xy' - if nwords < 3 and (words[0] in ['import','from']) : - if nwords == 1: - return get_root_modules() - mod = words[1].split('.') - if len(mod) < 2: - return get_root_modules() - completion_list = try_import('.'.join(mod[:-1]), True) - return ['.'.join(mod[:-1] + [el]) for el in completion_list] - - # 'from xyz import abc' - if nwords >= 3 and words[0] == 'from': - mod = words[1] - return try_import(mod) - - return module_completion, module_list - - -def remove_trailing_paren(str_): - if str_.endswith('('): - return str_[:-1] - return str_ - - -def main(): - input = sys.argv[1:] - if len(input) == 0: - return - elif '<' in input or '>' in input: - print('_longopt') - return - else: - options = list(set(map(remove_trailing_paren, parse_string(input)))) - - if len(options) == 0: - return - - if len(current_list(input)) > 1 and max(map(len, options)) + 1 >= len(current_raw(input)): - options.append(current_prefix(input)) - - if len(options) <= 1: - options = options + [x + "'" for x in options] - print(' '.join(options)) - - -if __name__ == '__main__': - main() diff --git a/extras/pycompletion.sh b/extras/pycompletion.sh index a7cc966..e218f7c 100644 --- a/extras/pycompletion.sh +++ b/extras/pycompletion.sh @@ -7,6 +7,33 @@ _py() fi } +_py2() +{ + COMPREPLY=($(pycompleter2 "${COMP_WORDS[@]}" 2>/dev/null )) + if [[ ${COMPREPLY[0]} == '_longopt' ]]; then + COMPREPLY=() + _longopt 2>/dev/null + fi +} + +_py2.6() +{ + COMPREPLY=($(pycompleter2.6 "${COMP_WORDS[@]}" 2>/dev/null )) + if [[ ${COMPREPLY[0]} == '_longopt' ]]; then + COMPREPLY=() + _longopt 2>/dev/null + fi +} + +_py2.7() +{ + COMPREPLY=($(pycompleter2.7 "${COMP_WORDS[@]}" 2>/dev/null )) + if [[ ${COMPREPLY[0]} == '_longopt' ]]; then + COMPREPLY=() + _longopt 2>/dev/null + fi +} + _py3() { COMPREPLY=($(pycompleter3 "${COMP_WORDS[@]}" 2>/dev/null )) @@ -16,5 +43,28 @@ _py3() fi } +_py3.3() +{ + COMPREPLY=($(pycompleter3.3 "${COMP_WORDS[@]}" 2>/dev/null )) + if [[ ${COMPREPLY[0]} == '_longopt' ]]; then + COMPREPLY=() + _longopt 2>/dev/null + fi +} +_py3.4() +{ + COMPREPLY=($(pycompleter3.4 "${COMP_WORDS[@]}" 2>/dev/null )) + if [[ ${COMPREPLY[0]} == '_longopt' ]]; then + COMPREPLY=() + _longopt 2>/dev/null + fi +} + + complete -F _py -o nospace py +complete -F _py2 -o nospace py2 +complete -F _py2.6 -o nospace py2.6 +complete -F _py2.7 -o nospace py2.7 complete -F _py3 -o nospace py3 +complete -F _py3.3 -o nospace py3.3 +complete -F _py3.4 -o nospace py3.4 diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/py b/pythonpy/__main__.py similarity index 97% rename from py rename to pythonpy/__main__.py index 20ad9f2..2d2f66b 100755 --- a/py +++ b/pythonpy/__main__.py @@ -2,7 +2,7 @@ from __future__ import (unicode_literals, absolute_import, print_function, division) from signal import signal, SIGPIPE, SIG_DFL -signal(SIGPIPE,SIG_DFL) +signal(SIGPIPE,SIG_DFL) import argparse import sys @@ -90,7 +90,7 @@ def loads(str_): else: if sum(1 for x in sys.stdin) > 0: sys.stderr.write( -"""Hint: --ji requies oneline json strings. Use py 'json.load(sys.stdin)' +"""Hint: --ji requies oneline json strings. Use py 'json.load(sys.stdin)' if you have a multi-line json file and not a file with multiple lines of json. """) raise ex @@ -159,7 +159,7 @@ def format(output): return args.output_delimiter.join(output) else: return output - + if isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstance(result, str): for x in result: @@ -179,3 +179,7 @@ def format(output): if args.post_cmd: exec(args.post_cmd) + +def main(): + # This does nothing. exec() does not easily work in a function. Sorry. + pass diff --git a/extras/pycompleter b/pythonpy/pycompleter.py similarity index 100% rename from extras/pycompleter rename to pythonpy/pycompleter.py diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index f1361a8..250f34a --- a/setup.py +++ b/setup.py @@ -1,5 +1,7 @@ -from distutils.core import setup +#!/usr/bin/env python +from setuptools import setup import os +import sys if os.geteuid() == 0: data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] @@ -11,20 +13,36 @@ 1) If you are in a virtualenv, you can configure tab completion without root using: source /path/to/virtualenv/bash_completion.d/pycompletion.sh 2) If you aren't using virtualenv, remember that pip requires sudo by default - on most systems. py is a simple python script does not require any - root access or special privileges. If you don't like using root, - learn virtualenv and refer to 1). + on most systems. py is a simple python script does not require any + root access or special privileges. If you don't like using root, + learn virtualenv and refer to 1). Installation proceeding without root access... ******************************************************************************''') data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] +py_entry = 'py%s = pythonpy.__main__:main' +pycompleter_entry = 'pycompleter%s = pythonpy.pycompleter:main' +endings = ( + '', + '%d' % sys.version_info[0], + '%d.%d' % sys.version_info[0:2] +) + +entry_points_scripts = [] +for e in endings: + entry_points_scripts.append(py_entry % e) + entry_points_scripts.append(pycompleter_entry % e) + setup( name='pythonpy', version='0.3.4', description='python -c, with tab completion and shorthand', - scripts=['py', 'extras/py3', 'extras/pycompleter', 'extras/pycompleter3'], data_files=data_files, license='MIT', url='https://github.com/Russell91/pythonpy', long_description='', + packages = ['pythonpy'], + entry_points = { + 'console_scripts': entry_points_scripts + }, ) From cc8c13a1fe991d3fdecde86f2ad2abead46453ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20=E2=80=9CKwpolska=E2=80=9D=20Warrick?= Date: Wed, 20 Aug 2014 21:10:18 +0200 Subject: [PATCH 188/254] intelligent version info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Chris “Kwpolska” Warrick --- pythonpy/__main__.py | 5 ++++- setup.py | 7 +------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 2d2f66b..3c57141 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -10,6 +10,9 @@ import re from collections import Iterable +__version__ = '0.3.4' +__version_info__ = '%s version %s' % (sys.argv[0], __version__) + def import_matches(query, prefix=''): matches = set(re.findall(r"(%s[a-zA-Z_][a-zA-Z0-9_]*)\.?" % prefix, query)) @@ -60,7 +63,7 @@ def inspect_source(obj): 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('-V', '--version', action='version', version='py version 0.3.4', help='version info') +parser.add_argument('-V', '--version', action='version', version=__version_info__, help='version info') parser.add_argument('--ji', '--json_input', dest='json_input', action='store_const', const=True, default=False, diff --git a/setup.py b/setup.py index 250f34a..0237bde 100755 --- a/setup.py +++ b/setup.py @@ -22,12 +22,7 @@ py_entry = 'py%s = pythonpy.__main__:main' pycompleter_entry = 'pycompleter%s = pythonpy.pycompleter:main' -endings = ( - '', - '%d' % sys.version_info[0], - '%d.%d' % sys.version_info[0:2] -) - +endings = ('', sys.version[:1], sys.version[:3]) entry_points_scripts = [] for e in endings: entry_points_scripts.append(py_entry % e) From 6f7f66e337eb48df80d93f0fb20e30000b7cbbe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20=E2=80=9CKwpolska=E2=80=9D=20Warrick?= Date: Wed, 20 Aug 2014 21:11:41 +0200 Subject: [PATCH 189/254] basename for version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Chris “Kwpolska” Warrick --- pythonpy/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 3c57141..919db60 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -5,13 +5,14 @@ signal(SIGPIPE,SIG_DFL) import argparse +import os.path import sys import json import re from collections import Iterable __version__ = '0.3.4' -__version_info__ = '%s version %s' % (sys.argv[0], __version__) +__version_info__ = '%s version %s' % (os.path.basename(sys.argv[0]), __version__) def import_matches(query, prefix=''): From 170d9fabb965f98e3a5787cc795197afa8ad7106 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 20 Aug 2014 12:30:09 -0700 Subject: [PATCH 190/254] Removed default imports for Counter, groupby, defaultdict, and OrderedDict. --- py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/py b/py index 20ad9f2..8628f7d 100755 --- a/py +++ b/py @@ -26,11 +26,6 @@ def lazy_imports(*args): query = ' '.join([x for x in args if x]) import_matches(query) - if 'Counter' in query: global Counter; from collections import Counter - if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict - if 'defaultdict' in query: global defaultdict; from collections import defaultdict - if 'groupby' in query: global groupby; from itertools import groupby - def current_list(input): return re.split(r'[^a-zA-Z0-9_\.]', input) From f427443c876567d096931fd10b20b5cdcaa8987c Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 20 Aug 2014 12:30:41 -0700 Subject: [PATCH 191/254] Updated README. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d0066fd..133a2bc 100644 --- a/README.rst +++ b/README.rst @@ -205,7 +205,7 @@ Count words beginning with each letter :: - $ cat /usr/share/dict/words | py -x 'x[0].lower()' | py -l 'Counter(l).most_common(5)' + $ cat /usr/share/dict/words | py -x 'x[0].lower()' | py -l 'collections.Counter(l).most_common(5)' ('s', 11327) ('c', 9521) ('p', 7659) From 4c01ed70bf9a73e239e266dbba27620e4add49f0 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 20 Aug 2014 13:42:35 -0700 Subject: [PATCH 192/254] Moved pycompletion.sh from extras/ to pythonpy/. --- MANIFEST.in | 1 + {extras => pythonpy}/debug_pycompletion.sh | 0 {extras => pythonpy}/pycompletion.sh | 0 setup.py | 4 ++-- 4 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 MANIFEST.in rename {extras => pythonpy}/debug_pycompletion.sh (100%) rename {extras => pythonpy}/pycompletion.sh (100%) diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..a307177 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include pythonpy/pycompletion.sh diff --git a/extras/debug_pycompletion.sh b/pythonpy/debug_pycompletion.sh similarity index 100% rename from extras/debug_pycompletion.sh rename to pythonpy/debug_pycompletion.sh diff --git a/extras/pycompletion.sh b/pythonpy/pycompletion.sh similarity index 100% rename from extras/pycompletion.sh rename to pythonpy/pycompletion.sh diff --git a/setup.py b/setup.py index 0237bde..e9deb48 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import sys if os.geteuid() == 0: - data_files = [('/etc/bash_completion.d', ['extras/pycompletion.sh']),] + data_files = [('/etc/bash_completion.d', ['pythonpy/pycompletion.sh']),] else: print( '''****************************************************************************** @@ -18,7 +18,7 @@ learn virtualenv and refer to 1). Installation proceeding without root access... ******************************************************************************''') - data_files = [('bash_completion.d', ['extras/pycompletion.sh']),] + data_files = [('bash_completion.d', ['pythonpy/pycompletion.sh']),] py_entry = 'py%s = pythonpy.__main__:main' pycompleter_entry = 'pycompleter%s = pythonpy.pycompleter:main' From ae48973fd27b39fbc12e9fbd969570fbd200e14f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 20 Aug 2014 14:00:00 -0700 Subject: [PATCH 193/254] Removed verbose exception formatting from entry_points. --- pythonpy/__main__.py | 177 ++++++++++++++++++++-------------------- pythonpy/pycompleter.py | 5 -- 2 files changed, 90 insertions(+), 92 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 2a1fbb2..86ded19 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -77,108 +77,111 @@ def inspect_source(obj): const=True, default=False, help='Wrap try-except-pass around each row') -args = parser.parse_args() +try: + args = parser.parse_args() -if args.json_input: - def loads(str_): - try: - return json.loads(str_.rstrip()) - except Exception as ex: - if args.ignore_exceptions: - pass + if args.json_input: + def loads(str_): + try: + return json.loads(str_.rstrip()) + except Exception as ex: + if args.ignore_exceptions: + pass + else: + if sum(1 for x in sys.stdin) > 0: + sys.stderr.write( + """Hint: --ji requies oneline json strings. Use py 'json.load(sys.stdin)' + if you have a multi-line json file and not a file with multiple lines of json. + """) + raise ex + stdin = (loads(x) for x in sys.stdin) + elif args.input_delimiter: + stdin = (re.split(args.input_delimiter, 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.expression.startswith('?') or args.expression.endswith('?'): + final_atom = current_list(args.expression.rstrip('?'))[-1] + first_atom = current_list(args.expression.lstrip('?'))[0] + if args.expression.startswith('??'): + import inspect + args.expression = "inspect_source(%s)" % first_atom + elif args.expression.endswith('??'): + import inspect + args.expression = "inspect_source(%s)" % final_atom + elif args.expression.startswith('?'): + args.expression = 'help(%s)' % first_atom else: - if sum(1 for x in sys.stdin) > 0: - sys.stderr.write( -"""Hint: --ji requies oneline json strings. Use py 'json.load(sys.stdin)' -if you have a multi-line json file and not a file with multiple lines of json. -""") - raise ex - stdin = (loads(x) for x in sys.stdin) -elif args.input_delimiter: - stdin = (re.split(args.input_delimiter, 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.expression.startswith('?') or args.expression.endswith('?'): - final_atom = current_list(args.expression.rstrip('?'))[-1] - first_atom = current_list(args.expression.lstrip('?'))[0] - if args.expression.startswith('??'): - import inspect - args.expression = "inspect_source(%s)" % first_atom - elif args.expression.endswith('??'): - import inspect - args.expression = "inspect_source(%s)" % final_atom - elif args.expression.startswith('?'): - args.expression = 'help(%s)' % first_atom - else: - args.expression = 'help(%s)' % final_atom - if args.lines_of_stdin: - from itertools import islice - stdin = islice(stdin,1) -if args.pre_cmd: - args.pre_cmd = args.pre_cmd.replace("`", "'") -if args.post_cmd: - args.post_cmd = args.post_cmd.replace("`", "'") + args.expression = 'help(%s)' % final_atom + if args.lines_of_stdin: + from itertools import islice + stdin = islice(stdin,1) + 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) + lazy_imports(args.expression, args.pre_cmd, args.post_cmd) -if args.pre_cmd: - exec(args.pre_cmd) + if args.pre_cmd: + exec(args.pre_cmd) -def safe_eval(text, x): - try: - return eval(text) - except: - return None + 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.output_delimiter: - return args.output_delimiter.join(output) + 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: - return output + result = eval(args.expression) + + def format(output): + if output == None: + return None + elif args.json_output: + return json.dumps(output) + elif args.output_delimiter: + return args.output_delimiter.join(output) + else: + return output -if isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstance(result, str): - for x in result: - formatted = format(x) + if isinstance(result, Iterable) and hasattr(result, '__iter__') and not isinstance(result, str): + for x in result: + formatted = format(x) + if formatted is not None: + try: + print(formatted) + except UnicodeEncodeError: + print(formatted.encode('utf-8')) + else: + formatted = format(result) if formatted is not None: try: print(formatted) except UnicodeEncodeError: print(formatted.encode('utf-8')) -else: - formatted = format(result) - if formatted is not None: - try: - print(formatted) - except UnicodeEncodeError: - print(formatted.encode('utf-8')) -if args.post_cmd: - exec(args.post_cmd) + if args.post_cmd: + exec(args.post_cmd) +except Exception as ex: + import traceback + print(traceback.format_exc()) def main(): - # This does nothing. exec() does not easily work in a function. Sorry. pass diff --git a/pythonpy/pycompleter.py b/pythonpy/pycompleter.py index 3bdb35f..76e3125 100755 --- a/pythonpy/pycompleter.py +++ b/pythonpy/pycompleter.py @@ -37,11 +37,6 @@ def lazy_imports(*args): except ImportError as e: pass - if 'Counter' in query: global Counter; from collections import Counter - if 'OrderedDict' in query: global OrderedDict; from collections import OrderedDict - if 'defaultdict' in query: global defaultdict; from collections import defaultdict - if 'groupby' in query: global groupby; from itertools import groupby - def complete_all(prefix, completion_args): lazy_imports(prefix, completion_args['c_arg']) From 772d9a20ad6ef25b690f5d1f0a4527556606f6e5 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 20 Aug 2014 14:23:23 -0700 Subject: [PATCH 194/254] Prevented pypi from hijacking long_description when empty. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e9deb48..d9e4320 100755 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ data_files=data_files, license='MIT', url='https://github.com/Russell91/pythonpy', - long_description='', + long_description='https://github.com/Russell91/pythonpy', packages = ['pythonpy'], entry_points = { 'console_scripts': entry_points_scripts From 1165d5973f74f518c7c5aec860dcecbdca2f9732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20=E2=80=9CKwpolska=E2=80=9D=20Warrick?= Date: Thu, 21 Aug 2014 10:04:31 +0200 Subject: [PATCH 195/254] Move version to __init__.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adhere to best practices and move __version__ to __init__.py file. Signed-off-by: Chris “Kwpolska” Warrick --- pythonpy/__init__.py | 1 + pythonpy/__main__.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index e69de29..bfeb9e7 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -0,0 +1 @@ +__version__ = '0.3.4' diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 86ded19..2b585ca 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -11,7 +11,10 @@ import re from collections import Iterable -__version__ = '0.3.4' +try: + from . import __version__ +except (ImportError, ValueError, SystemError): + __version__ = '???' # NOQA __version_info__ = '%s version %s' % (os.path.basename(sys.argv[0]), __version__) From 2115e8725130e184bedc51f51a646b429ab7e08e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 23 Aug 2014 15:05:57 -0700 Subject: [PATCH 196/254] 0.3.5 --- pythonpy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index bfeb9e7..40ed83d 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.3.4' +__version__ = '0.3.5' diff --git a/setup.py b/setup.py index d9e4320..a60a766 100755 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ setup( name='pythonpy', - version='0.3.4', + version='0.3.5', description='python -c, with tab completion and shorthand', data_files=data_files, license='MIT', From 94c8a10dbd58c0b9a0c3f3a772c21e157a613cbf Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 23 Aug 2014 15:27:48 -0700 Subject: [PATCH 197/254] Prevented sudo warning from being printed twice. --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index a60a766..1437b5a 100755 --- a/setup.py +++ b/setup.py @@ -4,9 +4,10 @@ import sys if os.geteuid() == 0: - data_files = [('/etc/bash_completion.d', ['pythonpy/pycompletion.sh']),] + data_files = [('/etc/bash_completion.d', ['pythonpy/pycompletion.sh']),] else: - print( + if sys.argv[1] == 'install': + print( '''****************************************************************************** Looks like you didn't run this command using sudo. Pythonpy needs root privileges to copy pycompletion.sh to /etc/bash_completion.d @@ -18,7 +19,7 @@ learn virtualenv and refer to 1). Installation proceeding without root access... ******************************************************************************''') - data_files = [('bash_completion.d', ['pythonpy/pycompletion.sh']),] + data_files = [('bash_completion.d', ['pythonpy/pycompletion.sh']),] py_entry = 'py%s = pythonpy.__main__:main' pycompleter_entry = 'pycompleter%s = pythonpy.pycompleter:main' From ae42174128e6a435a2d3cc76c34d0cc5988ddc1e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 23 Aug 2014 15:44:43 -0700 Subject: [PATCH 198/254] Added python sys.version information to py -V output. --- pythonpy/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 2b585ca..a7033ed 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -15,7 +15,8 @@ from . import __version__ except (ImportError, ValueError, SystemError): __version__ = '???' # NOQA -__version_info__ = '%s version %s' % (os.path.basename(sys.argv[0]), __version__) +__version_info__ = '''%s version %s +python version %s''' % (os.path.basename(sys.argv[0]), __version__, sys.version) def import_matches(query, prefix=''): From 318dbf2327ad7260fed38518deabb4b45999f5f6 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 23 Aug 2014 15:47:49 -0700 Subject: [PATCH 199/254] Removed argv dependence of py -V. --- pythonpy/__main__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index a7033ed..280fb5a 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -5,7 +5,6 @@ signal(SIGPIPE,SIG_DFL) import argparse -import os.path import sys import json import re @@ -15,8 +14,8 @@ from . import __version__ except (ImportError, ValueError, SystemError): __version__ = '???' # NOQA -__version_info__ = '''%s version %s -python version %s''' % (os.path.basename(sys.argv[0]), __version__, sys.version) +__version_info__ = '''pythonpy version %s +python version %s''' % (__version__, sys.version) def import_matches(query, prefix=''): From f143d97222cb24545afd3937b3c47a494d323d85 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 23 Aug 2014 15:49:43 -0700 Subject: [PATCH 200/254] Removed grammar error from setup.py message. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1437b5a..23f6e6e 100755 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ 1) If you are in a virtualenv, you can configure tab completion without root using: source /path/to/virtualenv/bash_completion.d/pycompletion.sh 2) If you aren't using virtualenv, remember that pip requires sudo by default - on most systems. py is a simple python script does not require any + on most systems. py is a simple python script that does not require any root access or special privileges. If you don't like using root, learn virtualenv and refer to 1). Installation proceeding without root access... From 2e74dfbb06df36251f681a63f9097d2e4d12bb0b Mon Sep 17 00:00:00 2001 From: Simeon Visser Date: Sun, 24 Aug 2014 00:27:44 +0100 Subject: [PATCH 201/254] Added tox configuration for testing in various Python versions --- .gitignore | 2 ++ tox.ini | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 35379c1..22e6a38 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ dist *.pyc *swp MANIFEST +.tox/ +*.egg-info/ diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..d002ca5 --- /dev/null +++ b/tox.ini @@ -0,0 +1,7 @@ +[tox] +envlist = py27, py32, py33, py34 + +[testenv] +commands = nosetests +deps = + nose From e722ec67a7c2e9668f838a81bffeb0caece840f2 Mon Sep 17 00:00:00 2001 From: Simeon Visser Date: Sun, 24 Aug 2014 00:28:57 +0100 Subject: [PATCH 202/254] Make tests pass in Python 2.7 and Python 3.x --- test/test_pythonpy.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/test_pythonpy.py b/test/test_pythonpy.py index bf03c8e..99fefa0 100644 --- a/test/test_pythonpy.py +++ b/test/test_pythonpy.py @@ -3,27 +3,27 @@ class TestPythonPy(unittest.TestCase): def test_empty(self): - self.assertEqual(check_output(['py']),'') - + self.assertEqual(check_output(['py']), b'') + def test_numbers(self): - self.assertEqual(check_output(['py', '3 * 4.5']),'13.5\n') + self.assertEqual(check_output(['py', '3 * 4.5']), b'13.5\n') def test_range(self): - self.assertEqual(check_output(['py', 'range(3)']), '\n'.join(map(str, range(3))) + '\n') + self.assertEqual(check_output(['py', 'range(3)']), b'0\n1\n2\n') def test_split_input(self): - self.assertEqual(check_output(["""echo a,b | py -x 'x[1]' --si ,"""], shell=True), 'b\n') + self.assertEqual(check_output(["""echo a,b | py -x 'x[1]' --si ,"""], shell=True), b'b\n') def test_split_output(self): - self.assertEqual(check_output(["""echo abc | py -x x --si '' --so ','"""], shell=True), 'a,b,c\n') + self.assertEqual(check_output(["""echo abc | py -x x --si '' --so ','"""], shell=True), b'a,b,c\n') def test_ignore_errors(self): - self.assertEqual(check_output("""echo a | py -x --i 'None.None'""", shell=True), '') - self.assertEqual(check_output("""echo a | py -fx --i 'None.None'""", shell=True), '') + self.assertEqual(check_output("""echo a | py -x --i 'None.None'""", shell=True), b'') + self.assertEqual(check_output("""echo a | py -fx --i 'None.None'""", shell=True), b'') def test_statements(self): - self.assertEqual(check_output("""py -c 'a=5' -C 'print(a)'""", shell=True), '5\n') - self.assertEqual(check_output("""echo 3 | py -c 'a=5' -x x -C 'print(a)'""", shell=True), '3\n5\n') + self.assertEqual(check_output("""py -c 'a=5' -C 'print(a)'""", shell=True), b'5\n') + self.assertEqual(check_output("""echo 3 | py -c 'a=5' -x x -C 'print(a)'""", shell=True), b'3\n5\n') def test_imports(self): module_commands = ["math.ceil(2.5)", From c17491409dcfdab4f0c170a8f306b6f6baa1a7d2 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 5 Sep 2014 13:25:54 -0700 Subject: [PATCH 203/254] Fixed bug using ==None rather than is None. --- pythonpy/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 280fb5a..3f06325 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -154,7 +154,7 @@ def safe_eval(text, x): result = eval(args.expression) def format(output): - if output == None: + if output is None: return None elif args.json_output: return json.dumps(output) From e22bbc5b12869071deb0ed792b3138d358fd5152 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 5 Sep 2014 13:30:46 -0700 Subject: [PATCH 204/254] Simplified py -V output. --- pythonpy/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 3f06325..164101c 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -14,8 +14,8 @@ from . import __version__ except (ImportError, ValueError, SystemError): __version__ = '???' # NOQA -__version_info__ = '''pythonpy version %s -python version %s''' % (__version__, sys.version) +__version_info__ = '''Pythonpy %s +Python %s''' % (__version__, sys.version.split(' ')[0]) def import_matches(query, prefix=''): From 625138fefea0514ae5525f66bcbb687811905c47 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 11 Oct 2014 11:10:01 -0700 Subject: [PATCH 205/254] Update README.rst --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 133a2bc..5879ff5 100644 --- a/README.rst +++ b/README.rst @@ -21,8 +21,8 @@ Float Arithmetic :: -Access imports directly -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Import modules automatically +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: From 7bd18d1a96c824084fccd6395409c1f4218f87dd Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sun, 19 Oct 2014 11:31:17 -0700 Subject: [PATCH 206/254] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 5879ff5..2a421e9 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ Float Arithmetic :: -Import modules automatically +Import any module automatically ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: From 0d9741eec077120684b199ecd355de061d38be96 Mon Sep 17 00:00:00 2001 From: yeondudad Date: Wed, 7 Jan 2015 13:26:13 +0900 Subject: [PATCH 207/254] set default encoding for unicode --- pythonpy/__main__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 164101c..98e94f0 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -1,11 +1,13 @@ #!/usr/bin/env python2 from __future__ import (unicode_literals, absolute_import, print_function, division) +import sys +reload(sys) +sys.setdefaultencoding('utf-8') from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE,SIG_DFL) import argparse -import sys import json import re from collections import Iterable From 62ab90be00495587b6707bea01b98db2e2f260d5 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 5 Sep 2014 13:56:25 -0700 Subject: [PATCH 208/254] 0.3.6 --- pythonpy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index 40ed83d..4596d03 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.3.5' +__version__ = '0.3.6' diff --git a/setup.py b/setup.py index 23f6e6e..41d19f9 100755 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ setup( name='pythonpy', - version='0.3.5', + version='0.3.6', description='python -c, with tab completion and shorthand', data_files=data_files, license='MIT', From e1abb068494160d533670dd3cdb0d2ad1d64382c Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 6 Jan 2015 22:37:51 -0800 Subject: [PATCH 209/254] Changed exceptions to write to stderr. --- pythonpy/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 164101c..e2c9847 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -184,7 +184,7 @@ def format(output): exec(args.post_cmd) except Exception as ex: import traceback - print(traceback.format_exc()) + sys.stderr.write(traceback.format_exc()) def main(): pass From 0122fc69d0f012331176bbbbba2b65b3897f6c27 Mon Sep 17 00:00:00 2001 From: Hans Roman Date: Wed, 7 Jan 2015 05:20:07 -0500 Subject: [PATCH 210/254] Check python version to restrict setdefaultencoding setdefaultencoding only works in python 2 --- pythonpy/__main__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index ec6f2e6..52fe609 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -2,8 +2,11 @@ from __future__ import (unicode_literals, absolute_import, print_function, division) import sys -reload(sys) -sys.setdefaultencoding('utf-8') + +if sys.version_info.major == 2: + reload(sys) + sys.setdefaultencoding('utf-8') + from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE,SIG_DFL) From 5116b94d3ae9265e19595c71ebd0682444600138 Mon Sep 17 00:00:00 2001 From: Andrew Barnert Date: Fri, 9 Jan 2015 18:08:13 -0800 Subject: [PATCH 211/254] Modify setup to handle completions more portably. - Bash completions may be stored in `/usr/local/etc`, `/opt/local/etc`, or other places besides `/etc`. In particular: - OS X `bash` doesn't come with completions by default; installing them manually or with Homebrew puts them in `/usr/local`. - OS X users who replace Apple's `bash` (because it's 3.2, and uses `libedit` instead of `libreadline`) usually do so with Homebrew (`/usr/local`), a manual build (`/usr/local`), or MacPorts (`/opt/local`), although other options (Fink, Gentoo Prefix, etc.) exist. - Most *BSD systems have completions in ports rather than base (or the relevant equivalent), so again they go into `/usr/local`. - I have no idea how well this will work for Windows users using MSYS or other native `bash`, but my guess is that if this doesn't work, the only thing that will is custom Windows code. At any rate, the worst case is that we fall back to installing the completion locally. - The new version searches every path in `$PATH`, looking for a `../etc/bash_completion.d`, and uses the first one it finds. - `pip` doesn't always require `sudo` outside a virtualenv. - On OS X, the Python.org installer and Homebrew both, by default, give you a site-packages that's group- or world-writable. Fortunately, people who use such a Python are usually using a group- or world- writable bash-completions as well. - `pip --user` of course doesn't need it either. - Windows users have a world-writable site-packages (and don't have `sudo`). - The simplest thing to do is check whether the discovered directory is writable, instead of checking whether we're running as root. - These changes required some minor rewrites to the warning message. - This might need updates for Windows `bash` users... or maybe anyone using native `bash` on Windows should be expected to know what they're doing... --- setup.py | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/setup.py b/setup.py index 41d19f9..a48d75e 100755 --- a/setup.py +++ b/setup.py @@ -2,24 +2,40 @@ from setuptools import setup import os import sys +import tempfile -if os.geteuid() == 0: - data_files = [('/etc/bash_completion.d', ['pythonpy/pycompletion.sh']),] +for path in os.environ['PATH'].split(':'): + target = os.path.join(os.path.dirname(path), 'etc', 'bash_completion.d') + if os.path.isdir(target): + break else: - if sys.argv[1] == 'install': - print( + # Fall back to the default used by many Linux distros + target = '/etc/bash_completion.d' + +try: + with tempfile.TemporaryFile(dir=target) as t: + pass +except OSError as e: + print( '''****************************************************************************** -Looks like you didn't run this command using sudo. -Pythonpy needs root privileges to copy pycompletion.sh to /etc/bash_completion.d -1) If you are in a virtualenv, you can configure tab completion without root using: - source /path/to/virtualenv/bash_completion.d/pycompletion.sh -2) If you aren't using virtualenv, remember that pip requires sudo by default - on most systems. py is a simple python script that does not require any - root access or special privileges. If you don't like using root, - learn virtualenv and refer to 1). +Pythonpy can't create a file in: + {} +The error was: + {} +It looks like you either didn't run this command using sudo, or don't have +bash completions set up. +1) If this is intentional (e.g., because you're in a virtualenv), you can + configure tab completion without root using: + source /path/to/virtualenv/bash_completion.d/pycompletion.sh +2) Otherwise, remember that pip requires sudo by default + on most systems. py is a simple python script that does not require any + root access or special privileges. If you don't like using root, + learn virtualenv and refer to 1). Installation proceeding without root access... ******************************************************************************''') - data_files = [('bash_completion.d', ['pythonpy/pycompletion.sh']),] + target='bash_completion.d' + +data_files = [(target, ['pythonpy/pycompletion.sh']),] py_entry = 'py%s = pythonpy.__main__:main' pycompleter_entry = 'pycompleter%s = pythonpy.pycompleter:main' From 45d570c6629305796016a446a7b9849eb28807d2 Mon Sep 17 00:00:00 2001 From: Russell Date: Mon, 12 Jan 2015 22:27:38 -0800 Subject: [PATCH 212/254] Added warning to users using multiple -x, -fx, -l flags. --- pythonpy/__main__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 52fe609..5d1f3e6 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -87,6 +87,9 @@ def inspect_source(obj): try: args = parser.parse_args() + if sum([args.list_of_stdin, args.lines_of_stdin, args.filter_result]) > 1: + sys.stderr.write('Pythonpy accepts at most one of [-x, -fx, -l] flags\n') + sys.exit() if args.json_input: def loads(str_): From c8bfa186fcfacdb4ceba02364b91567b89336def Mon Sep 17 00:00:00 2001 From: Andrew Barnert Date: Fri, 9 Jan 2015 17:13:18 -0800 Subject: [PATCH 213/254] Skip code from `pythonpy` itself in tracebacks. Signed-off-by: Russell --- pythonpy/__main__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 5d1f3e6..e5a79cc 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -190,9 +190,12 @@ def format(output): if args.post_cmd: exec(args.post_cmd) -except Exception as ex: +except Exception: import traceback - sys.stderr.write(traceback.format_exc()) + header = 'File "{}"'.format(__file__) + lines = traceback.format_exception(*sys.exc_info()) + lines = (line for line in lines if not line.lstrip().startswith(header)) + sys.stderr.write(''.join(lines)) def main(): pass From 818dad8f68382a873b3a67dd62f75979a86b2947 Mon Sep 17 00:00:00 2001 From: Andrew Barnert Date: Fri, 9 Jan 2015 18:55:40 -0800 Subject: [PATCH 214/254] Add expression text to tracebacks. This is a bit hacky. (Inspired by a similar feature in `spy`, but simpler.) The actual traceback has an empty `text` for any code that comes from a ``, and there's really no way to distinguish the user's input from some other string that gets evaluated farther down the chain. Also, if the expression does something like define a lambda and call it (or pass it to another function that calls it later), it will also be from file `` with empty text. We could distinguish this one, because the location will be, e.g., `` instead of ``, but since we can't distinguish the previous case, there doesn't seem to be much point. So, what we do is just fill in the text for the first traceback line we see from file ``, and then not fill it in for later lines from the same file. Signed-off-by: Russell --- pythonpy/__main__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index e5a79cc..14ed7e3 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -192,10 +192,17 @@ def format(output): exec(args.post_cmd) except Exception: import traceback - header = 'File "{}"'.format(__file__) + pyheader = 'File "{}"'.format(__file__) + exprheader = 'File ""' + foundexpr = False lines = traceback.format_exception(*sys.exc_info()) - lines = (line for line in lines if not line.lstrip().startswith(header)) - sys.stderr.write(''.join(lines)) + for line in lines: + if line.lstrip().startswith(pyheader): + continue + sys.stderr.write(line) + if not foundexpr and line.lstrip().startswith(exprheader): + sys.stderr.write(' {}\n'.format(args.expression)) + foundexpr = True def main(): pass From f5a81c295d7642f860ddb3af10af64dcf3609050 Mon Sep 17 00:00:00 2001 From: Russell Date: Mon, 12 Jan 2015 22:47:40 -0800 Subject: [PATCH 215/254] Added special case to avoid double printing SyntaxError expression. --- pythonpy/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 14ed7e3..9da0852 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -190,7 +190,7 @@ def format(output): if args.post_cmd: exec(args.post_cmd) -except Exception: +except Exception as ex: import traceback pyheader = 'File "{}"'.format(__file__) exprheader = 'File ""' @@ -200,7 +200,7 @@ def format(output): if line.lstrip().startswith(pyheader): continue sys.stderr.write(line) - if not foundexpr and line.lstrip().startswith(exprheader): + if not foundexpr and line.lstrip().startswith(exprheader) and not isinstance(ex, SyntaxError): sys.stderr.write(' {}\n'.format(args.expression)) foundexpr = True From 5ba6f1ea925ee4a7edd26236812fac2cd57864db Mon Sep 17 00:00:00 2001 From: Russell Date: Mon, 12 Jan 2015 22:55:19 -0800 Subject: [PATCH 216/254] Removed python2 test failing for python3 unicode. --- test/test_pythonpy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_pythonpy.py b/test/test_pythonpy.py index 99fefa0..441eddd 100644 --- a/test/test_pythonpy.py +++ b/test/test_pythonpy.py @@ -27,7 +27,6 @@ def test_statements(self): def test_imports(self): module_commands = ["math.ceil(2.5)", - "base64.b64encode('data to be encoded')", "calendar.weekday(1955, 11, 5)", "csv.list_dialects()", "datetime.timedelta(hours=-5)", From ee045880b8b4d0f70c41c9f6cca3cb173c205901 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 28 Jan 2015 20:57:08 -0800 Subject: [PATCH 217/254] 0.3.7 --- pythonpy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index 4596d03..d93912e 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.3.6' +__version__ = '0.3.7' diff --git a/setup.py b/setup.py index a48d75e..66edde0 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ setup( name='pythonpy', - version='0.3.6', + version='0.3.7', description='python -c, with tab completion and shorthand', data_files=data_files, license='MIT', From a881da74de515c196165485d855628112e7da9fe Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 28 Jan 2015 21:19:00 -0800 Subject: [PATCH 218/254] Fixed error cleanup trick to work for root installs. --- pythonpy/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 9da0852..83bbfe7 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -192,12 +192,12 @@ def format(output): exec(args.post_cmd) except Exception as ex: import traceback - pyheader = 'File "{}"'.format(__file__) + pyheader = 'pythonpy/__main__.py' exprheader = 'File ""' foundexpr = False lines = traceback.format_exception(*sys.exc_info()) for line in lines: - if line.lstrip().startswith(pyheader): + if pyheader in line: continue sys.stderr.write(line) if not foundexpr and line.lstrip().startswith(exprheader) and not isinstance(ex, SyntaxError): From 8302e435427511785cff4c594cb89640f02214a9 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 6 Mar 2015 12:46:08 -0800 Subject: [PATCH 219/254] Changed install error message. --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 66edde0..8101f3b 100755 --- a/setup.py +++ b/setup.py @@ -19,9 +19,9 @@ print( '''****************************************************************************** Pythonpy can't create a file in: - {} + /etc/bash_completion.d The error was: - {} + %s It looks like you either didn't run this command using sudo, or don't have bash completions set up. 1) If this is intentional (e.g., because you're in a virtualenv), you can @@ -32,7 +32,7 @@ root access or special privileges. If you don't like using root, learn virtualenv and refer to 1). Installation proceeding without root access... -******************************************************************************''') +******************************************************************************''') % e target='bash_completion.d' data_files = [(target, ['pythonpy/pycompletion.sh']),] From c25a678bf2a00cf4a095cdab992ffd5c7ce7ecd4 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 6 Mar 2015 12:47:12 -0800 Subject: [PATCH 220/254] 0.3.8 --- pythonpy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index d93912e..17a3314 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.3.7' +__version__ = '0.3.8' diff --git a/setup.py b/setup.py index 8101f3b..9af6617 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ setup( name='pythonpy', - version='0.3.7', + version='0.3.8', description='python -c, with tab completion and shorthand', data_files=data_files, license='MIT', From 1da0e17cd0ab8b3b9d11b9bd3753b558e8466778 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 20 Mar 2015 15:39:21 -0700 Subject: [PATCH 221/254] Removed tox.ini file. Tests can be run with python test/test_pythonpy. --- tox.ini | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 tox.ini diff --git a/tox.ini b/tox.ini deleted file mode 100644 index d002ca5..0000000 --- a/tox.ini +++ /dev/null @@ -1,7 +0,0 @@ -[tox] -envlist = py27, py32, py33, py34 - -[testenv] -commands = nosetests -deps = - nose From ef9d2a3d4f64356d7fc59fbe996f64ef2bf9dde3 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 20 Mar 2015 15:39:56 -0700 Subject: [PATCH 222/254] Changed help documentation to remove seldom used flags. --- README.rst | 58 -------------------------------------------- pythonpy/__main__.py | 51 +++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 84 deletions(-) diff --git a/README.rst b/README.rst index 2a421e9..55c2c7c 100644 --- a/README.rst +++ b/README.rst @@ -107,64 +107,6 @@ Get only even numbers :: -py -fx 'predicate(x)' filters rows satisfying a condition ---------------------------------------------------------- - -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 - -:: - py -l will set l = list(sys.stdin) ------------------------------------------- diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index 83bbfe7..e9a5519 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -53,42 +53,46 @@ def inspect_source(obj): return help(obj) parser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter) + formatter_class=argparse.RawDescriptionHelpFormatter, + add_help=False) -parser.add_argument('expression', nargs='?', default='None') -parser.add_argument('-x', dest='lines_of_stdin', action='store_const', +group = parser.add_argument_group("Options") + +parser.add_argument('expression', nargs='?', default='None', help="e.g. py '2 ** 32'") +group.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', + help='treat each row of stdin as x') +group.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', + help=argparse.SUPPRESS) +group.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('-V', '--version', action='version', version=__version_info__, help='version info') -parser.add_argument('--ji', '--json_input', +group.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', + help=argparse.SUPPRESS) +group.add_argument('--jo', '--json_output', dest='json_output', action='store_const', const=True, default=False, - help='post-process each row with json.dumps(row)') -parser.add_argument('--si', '--split_input', dest='input_delimiter', - help='pre-process each row with re.split(delimiter, row)') -parser.add_argument('--so', '--split_output', dest='output_delimiter', - help='post-process each row with delimiter.join(row)') -parser.add_argument('--i', '--ignore_exceptions', + help=argparse.SUPPRESS) +group.add_argument('--si', '--split_input', dest='input_delimiter', + help=argparse.SUPPRESS) +group.add_argument('--so', '--split_output', dest='output_delimiter', + help=argparse.SUPPRESS) +group.add_argument('-c', dest='pre_cmd', help='run code before expression') +group.add_argument('-C', dest='post_cmd', help='run code after expression') +group.add_argument('--i', '--ignore_exceptions', dest='ignore_exceptions', action='store_const', const=True, default=False, - help='Wrap try-except-pass around each row') + help=argparse.SUPPRESS) +group.add_argument('-V', '--version', action='version', version=__version_info__, help='version info') +group.add_argument('-h', '--help', action='help', help="show this help message and exit") try: args = parser.parse_args() if sum([args.list_of_stdin, args.lines_of_stdin, args.filter_result]) > 1: - sys.stderr.write('Pythonpy accepts at most one of [-x, -fx, -l] flags\n') + sys.stderr.write('Pythonpy accepts at most one of [-x, -l] flags\n') sys.exit() if args.json_input: @@ -99,11 +103,6 @@ def loads(str_): if args.ignore_exceptions: pass else: - if sum(1 for x in sys.stdin) > 0: - sys.stderr.write( - """Hint: --ji requies oneline json strings. Use py 'json.load(sys.stdin)' - if you have a multi-line json file and not a file with multiple lines of json. - """) raise ex stdin = (loads(x) for x in sys.stdin) elif args.input_delimiter: From 22fca19952c391f1665a2f1448e75dc2202b994a Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 20 Mar 2015 15:41:08 -0700 Subject: [PATCH 223/254] 0.4.0 --- CHANGELOG.md | 7 +++++++ pythonpy/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..457dc4f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# Pythonpy 0.4 +Removed documentation for flags --ji, --jo, --so, --si, --i, and -fx. +These flags are not deprecated. Users should feel comfortable continuing to use them, +but from some simple polling I have found that they are infrequently used due to +their complexity and unorthodox double-dash form. If some of them turn out to be sorely +missed, they may be reincarnated with more conventional single dash, single letter +forms. diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index 17a3314..abeeedb 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.3.8' +__version__ = '0.4.0' diff --git a/setup.py b/setup.py index 9af6617..24ad234 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ setup( name='pythonpy', - version='0.3.8', + version='0.4.0', description='python -c, with tab completion and shorthand', data_files=data_files, license='MIT', From c1bac5943d4463fb80f04f3772e40022439b1863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Hermann?= Date: Wed, 8 Apr 2015 21:47:57 +0200 Subject: [PATCH 224/254] Fix error under Python 3.4 on Travis See https://travis-ci.org/jhermann/gh-commander/jobs/57697416 "print" is not a statement ;) --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 24ad234..c92c2bf 100755 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ with tempfile.TemporaryFile(dir=target) as t: pass except OSError as e: - print( + print(( '''****************************************************************************** Pythonpy can't create a file in: /etc/bash_completion.d @@ -32,7 +32,7 @@ root access or special privileges. If you don't like using root, learn virtualenv and refer to 1). Installation proceeding without root access... -******************************************************************************''') % e +******************************************************************************''') % (e,)) target='bash_completion.d' data_files = [(target, ['pythonpy/pycompletion.sh']),] From 22b003375ce7d7f8f0c76c4811e0f7b37443bfef Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 8 Apr 2015 15:39:45 -0700 Subject: [PATCH 225/254] 0.4.1 --- pythonpy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index abeeedb..f0ede3d 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.0' +__version__ = '0.4.1' diff --git a/setup.py b/setup.py index c92c2bf..8f90097 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ setup( name='pythonpy', - version='0.4.0', + version='0.4.1', description='python -c, with tab completion and shorthand', data_files=data_files, license='MIT', From cb47bee28bc800d6de84f20225328809ccc8fa38 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 9 Apr 2015 12:43:16 -0700 Subject: [PATCH 226/254] Update CHANGELOG.md --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 457dc4f..c5100d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,4 @@ Removed documentation for flags --ji, --jo, --so, --si, --i, and -fx. These flags are not deprecated. Users should feel comfortable continuing to use them, but from some simple polling I have found that they are infrequently used due to -their complexity and unorthodox double-dash form. If some of them turn out to be sorely -missed, they may be reincarnated with more conventional single dash, single letter -forms. +their complexity and unorthodox double-dash form. From b6ade13644791db3d9b1a5e8e189d515cfafa742 Mon Sep 17 00:00:00 2001 From: Russell Date: Fri, 10 Apr 2015 00:09:18 -0700 Subject: [PATCH 227/254] Simplified the warning message when installing without root. --- setup.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index 8f90097..0a5ac27 100755 --- a/setup.py +++ b/setup.py @@ -16,23 +16,17 @@ with tempfile.TemporaryFile(dir=target) as t: pass except OSError as e: - print(( + print( '''****************************************************************************** -Pythonpy can't create a file in: - /etc/bash_completion.d -The error was: - %s -It looks like you either didn't run this command using sudo, or don't have -bash completions set up. -1) If this is intentional (e.g., because you're in a virtualenv), you can - configure tab completion without root using: +Pythonpy was not able to install bash completion because it does not have write +access to /etc/bash_completion.d. +If you would still like to install bash completion, either: +1) Reinstall with `sudo pip install pythonpy` +2) Configure tab completion manually: source /path/to/virtualenv/bash_completion.d/pycompletion.sh -2) Otherwise, remember that pip requires sudo by default - on most systems. py is a simple python script that does not require any - root access or special privileges. If you don't like using root, - learn virtualenv and refer to 1). + Installation proceeding without root access... -******************************************************************************''') % (e,)) +******************************************************************************''') target='bash_completion.d' data_files = [(target, ['pythonpy/pycompletion.sh']),] From 95a922944fdaea300c56cae3339d5eda449a863d Mon Sep 17 00:00:00 2001 From: Russell Date: Fri, 10 Apr 2015 00:09:59 -0700 Subject: [PATCH 228/254] 0.4.2 --- pythonpy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index f0ede3d..a987347 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.1' +__version__ = '0.4.2' diff --git a/setup.py b/setup.py index 0a5ac27..94c8144 100755 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ setup( name='pythonpy', - version='0.4.1', + version='0.4.2', description='python -c, with tab completion and shorthand', data_files=data_files, license='MIT', From dc0d49e41b3b146c959b3be62e319102e9ab7817 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 11 Apr 2015 22:20:07 -0700 Subject: [PATCH 229/254] Update README.rst --- README.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 55c2c7c..f1bc4c4 100644 --- a/README.rst +++ b/README.rst @@ -8,9 +8,11 @@ Installation :: -py 'expression' ≅ python -c 'print(expression)' +Usage ----------------------------------------------- +Pythonpy will evaluate any python expression from the command line. + Float Arithmetic ~~~~~~~~~~~~~~~~ @@ -156,5 +158,5 @@ Count words beginning with each letter :: -If you haven't had enough yet, check out the `wiki `__ +For more examples, check out the `wiki `__ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From d784acf359ef45392e4b856aa0b62f8a443db49e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 9 Jun 2015 13:37:31 -0700 Subject: [PATCH 230/254] Update README.rst --- README.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index f1bc4c4..f433d2a 100644 --- a/README.rst +++ b/README.rst @@ -61,7 +61,7 @@ Lists are printed row by row py -x 'foo(x)' will apply foo to each line of input --------------------------------------------------- -Multiply each line of input by 7. +Multiply each line of input by 7 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: @@ -73,6 +73,17 @@ Multiply each line of input by 7. :: +Grab the second column of a csv +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + $ echo $'a1,b1,c1\na2,b2,c2' | py -x 'x.split(",")[1]' + b1 + b2 + +:: + Append ".txt" to each line of input ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 438de098b5f3db16c2f2f8c9f51e66bc0ce1b174 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 9 Jun 2015 13:55:59 -0700 Subject: [PATCH 231/254] Update README.rst --- README.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index f433d2a..5200b0c 100644 --- a/README.rst +++ b/README.rst @@ -107,16 +107,16 @@ Append ".txt" to every file in the directory :: -Get only even numbers +Get only 2 digit numbers ~~~~~~~~~~~~~~~~~~~~~ :: - $ py 'range(8)' | py -x 'x if int(x)%2 == 0 else None' - 0 - 2 - 4 - 6 + $ py 'range(14)' | py -x 'x if len(x) == 2 else None' + 10 + 11 + 12 + 13 :: From 78e79ec17dcecef6a6e1d2c9bfbf57295b3f8eca Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 9 Jun 2015 13:57:33 -0700 Subject: [PATCH 232/254] Update README.rst --- README.rst | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.rst b/README.rst index 5200b0c..ba24afc 100644 --- a/README.rst +++ b/README.rst @@ -43,21 +43,6 @@ Import any module automatically :: -Lists are printed row by row -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - $ py 'range(3)' - 0 - 1 - 2 - - $ py '[range(3)]' - [0, 1, 2] - -:: - py -x 'foo(x)' will apply foo to each line of input --------------------------------------------------- @@ -123,6 +108,21 @@ Get only 2 digit numbers py -l will set l = list(sys.stdin) ------------------------------------------- +Lists are printed row by row +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + $ py 'range(3)' + 0 + 1 + 2 + + $ py '[range(3)]' + [0, 1, 2] + +:: + Reverse the input ~~~~~~~~~~~~~~~~~ From cc122003e3b2a3b1bd76811b091daff3f803f8c3 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 23 Jun 2015 23:16:55 -0700 Subject: [PATCH 233/254] Update README.rst --- README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rst b/README.rst index ba24afc..58175f1 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,6 @@ Installation :: sudo pip install pythonpy - # restart your shell for tab completion to take effect :: From 1c2f6e46fb5e2714187588d2b70760fd17f2f316 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Thu, 17 Sep 2015 14:40:30 -0700 Subject: [PATCH 234/254] Added find command example --- README.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.rst b/README.rst index 58175f1..e279d06 100644 --- a/README.rst +++ b/README.rst @@ -91,6 +91,15 @@ Append ".txt" to every file in the directory :: +Remove every file returned by the find command +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + $ find . -type f | py -x '"rm %s" % x' | sh + +:: + Get only 2 digit numbers ~~~~~~~~~~~~~~~~~~~~~ From c28b722fab2562f8339b4891a663e58bc69dfa56 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 23 Sep 2015 14:02:25 -0700 Subject: [PATCH 235/254] Added sorting example. --- README.rst | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index e279d06..f63a524 100644 --- a/README.rst +++ b/README.rst @@ -68,18 +68,6 @@ Grab the second column of a csv :: -Append ".txt" to each line of input -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - $ py 'range(3)' | py -x 'x + ".txt"' - 0.txt - 1.txt - 2.txt - -:: - Append ".txt" to every file in the directory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -153,13 +141,14 @@ Sum the input :: -Count the lines of input -~~~~~~~~~~~~~~~~~~~~~~~~ +Sort a csv by the second column +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: - $ py 'range(17)' | py -l 'len(l)' - 17 + $ echo $'a,2\nb,1' | py -l 'sorted(l, key=lambda x: x.split(",")[1])' + b,1 + a,2 :: From 3893a08e05af62316acba2cc53508b8f5fb11398 Mon Sep 17 00:00:00 2001 From: Asger Hautop Drewsen Date: Sat, 21 Nov 2015 21:22:46 +0100 Subject: [PATCH 236/254] Exit with exit code 1 if an exception is raised --- pythonpy/__main__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index e9a5519..a5cf50f 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -202,6 +202,8 @@ def format(output): if not foundexpr and line.lstrip().startswith(exprheader) and not isinstance(ex, SyntaxError): sys.stderr.write(' {}\n'.format(args.expression)) foundexpr = True + + sys.exit(1) def main(): pass From c9b66674d0bb094ee84dfa084e30379f3b7d26ce Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sat, 21 Nov 2015 12:26:11 -0800 Subject: [PATCH 237/254] Added sys.exit(1) over sys.exit() on case of error. --- pythonpy/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index a5cf50f..d630c41 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -93,7 +93,7 @@ def inspect_source(obj): args = parser.parse_args() if sum([args.list_of_stdin, args.lines_of_stdin, args.filter_result]) > 1: sys.stderr.write('Pythonpy accepts at most one of [-x, -l] flags\n') - sys.exit() + sys.exit(1) if args.json_input: def loads(str_): From 12eaa7fb79e6799e94e5255f86d64765589cec4f Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Jan 2016 21:34:39 -0800 Subject: [PATCH 238/254] Added special support for np->numpy and pd->pandas --- pythonpy/__main__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pythonpy/__main__.py b/pythonpy/__main__.py index d630c41..8602b28 100755 --- a/pythonpy/__main__.py +++ b/pythonpy/__main__.py @@ -25,10 +25,16 @@ def import_matches(query, prefix=''): matches = set(re.findall(r"(%s[a-zA-Z_][a-zA-Z0-9_]*)\.?" % prefix, query)) - for module_name in matches: + for raw_module_name in matches: + if re.match('np(\..*)?$', raw_module_name): + module_name = re.sub('^np', 'numpy', raw_module_name) + elif re.match('pd(\..*)?$', raw_module_name): + module_name = re.sub('^pd', 'pandas', raw_module_name) + else: + module_name = raw_module_name try: module = __import__(module_name) - globals()[module_name] = module + globals()[raw_module_name] = module import_matches(query, prefix='%s.' % module_name) except ImportError as e: pass From ff985ca91651e68f76debe4b3055808f6f9706f8 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 12 Jan 2016 21:34:55 -0800 Subject: [PATCH 239/254] 0.4.3 --- pythonpy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index a987347..908c0bb 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.2' +__version__ = '0.4.3' diff --git a/setup.py b/setup.py index 94c8144..c589afa 100755 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ setup( name='pythonpy', - version='0.4.2', + version='0.4.3', description='python -c, with tab completion and shorthand', data_files=data_files, license='MIT', From 6241f482e24a5ffd61d41173d57420e5ea30b297 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 13 Jan 2016 00:03:22 -0800 Subject: [PATCH 240/254] Propagated np and pd changes to completion. --- pythonpy/pycompleter.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pythonpy/pycompleter.py b/pythonpy/pycompleter.py index 76e3125..2e14d1d 100755 --- a/pythonpy/pycompleter.py +++ b/pythonpy/pycompleter.py @@ -30,10 +30,16 @@ def lazy_imports(*args): query = ' '.join([x for x in args if x]) regex = re.compile("([a-zA-Z_][a-zA-Z0-9_]*)\.?") matches = regex.findall(query) - for module_name in matches: + for raw_module_name in matches: + if re.match('np(\..*)?$', raw_module_name): + module_name = re.sub('^np', 'numpy', raw_module_name) + elif re.match('pd(\..*)?$', raw_module_name): + module_name = re.sub('^pd', 'pandas', raw_module_name) + else: + module_name = raw_module_name try: module = __import__(module_name) - globals()[module_name] = module + globals()[raw_module_name] = module except ImportError as e: pass From 66515c1f59877991a07b4debfa1c6a071fc0bed3 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 13 Jan 2016 00:03:38 -0800 Subject: [PATCH 241/254] 0.4.4 --- pythonpy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index 908c0bb..9a8e054 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.3' +__version__ = '0.4.4' diff --git a/setup.py b/setup.py index c589afa..f86ce28 100755 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ setup( name='pythonpy', - version='0.4.3', + version='0.4.4', description='python -c, with tab completion and shorthand', data_files=data_files, license='MIT', From 701b606af0bf0426b9ffbbbfee54c064ed870f21 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Tue, 19 Jan 2016 20:21:28 -0800 Subject: [PATCH 242/254] Added MIT License. --- LICENSE | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c02be80 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (C) 2016 The Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 59418c9412307e092662748566a07a97501712af Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 16 Mar 2016 16:03:05 -0700 Subject: [PATCH 243/254] Moved pycompletion.sh to data directory. --- pythonpy/data/__init__.py | 0 pythonpy/{ => data}/debug_pycompletion.sh | 0 pythonpy/{ => data}/pycompletion.sh | 0 setup.py | 34 ++--------------------- 4 files changed, 3 insertions(+), 31 deletions(-) create mode 100644 pythonpy/data/__init__.py rename pythonpy/{ => data}/debug_pycompletion.sh (100%) rename pythonpy/{ => data}/pycompletion.sh (100%) diff --git a/pythonpy/data/__init__.py b/pythonpy/data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pythonpy/debug_pycompletion.sh b/pythonpy/data/debug_pycompletion.sh similarity index 100% rename from pythonpy/debug_pycompletion.sh rename to pythonpy/data/debug_pycompletion.sh diff --git a/pythonpy/pycompletion.sh b/pythonpy/data/pycompletion.sh similarity index 100% rename from pythonpy/pycompletion.sh rename to pythonpy/data/pycompletion.sh diff --git a/setup.py b/setup.py index f86ce28..19d888d 100755 --- a/setup.py +++ b/setup.py @@ -1,35 +1,6 @@ #!/usr/bin/env python from setuptools import setup -import os import sys -import tempfile - -for path in os.environ['PATH'].split(':'): - target = os.path.join(os.path.dirname(path), 'etc', 'bash_completion.d') - if os.path.isdir(target): - break -else: - # Fall back to the default used by many Linux distros - target = '/etc/bash_completion.d' - -try: - with tempfile.TemporaryFile(dir=target) as t: - pass -except OSError as e: - print( -'''****************************************************************************** -Pythonpy was not able to install bash completion because it does not have write -access to /etc/bash_completion.d. -If you would still like to install bash completion, either: -1) Reinstall with `sudo pip install pythonpy` -2) Configure tab completion manually: - source /path/to/virtualenv/bash_completion.d/pycompletion.sh - -Installation proceeding without root access... -******************************************************************************''') - target='bash_completion.d' - -data_files = [(target, ['pythonpy/pycompletion.sh']),] py_entry = 'py%s = pythonpy.__main__:main' pycompleter_entry = 'pycompleter%s = pythonpy.pycompleter:main' @@ -43,11 +14,12 @@ name='pythonpy', version='0.4.4', description='python -c, with tab completion and shorthand', - data_files=data_files, + #data_files=data_files, license='MIT', url='https://github.com/Russell91/pythonpy', long_description='https://github.com/Russell91/pythonpy', - packages = ['pythonpy'], + packages=['pythonpy'], + package_data={'pythonpy': ['data/pycompletion.sh']}, entry_points = { 'console_scripts': entry_points_scripts }, From 9f1e10e3fc4ee98500c10a791fca062e9e628d37 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 16 Mar 2016 16:03:30 -0700 Subject: [PATCH 244/254] Removed sudo from install instructions. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index f63a524..800e78c 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ Installation :: - sudo pip install pythonpy + pip install pythonpy :: From 03b97508b54352a980b747d6ca8b8e0e91ec0814 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 16 Mar 2016 16:08:55 -0700 Subject: [PATCH 245/254] Renamed pythonpy.data to pythonpy.completion. --- pythonpy/{data => completion}/__init__.py | 0 pythonpy/{data => completion}/debug_pycompletion.sh | 0 pythonpy/{data => completion}/pycompletion.sh | 0 setup.py | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename pythonpy/{data => completion}/__init__.py (100%) rename pythonpy/{data => completion}/debug_pycompletion.sh (100%) rename pythonpy/{data => completion}/pycompletion.sh (100%) diff --git a/pythonpy/data/__init__.py b/pythonpy/completion/__init__.py similarity index 100% rename from pythonpy/data/__init__.py rename to pythonpy/completion/__init__.py diff --git a/pythonpy/data/debug_pycompletion.sh b/pythonpy/completion/debug_pycompletion.sh similarity index 100% rename from pythonpy/data/debug_pycompletion.sh rename to pythonpy/completion/debug_pycompletion.sh diff --git a/pythonpy/data/pycompletion.sh b/pythonpy/completion/pycompletion.sh similarity index 100% rename from pythonpy/data/pycompletion.sh rename to pythonpy/completion/pycompletion.sh diff --git a/setup.py b/setup.py index 19d888d..bc5e900 100755 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ url='https://github.com/Russell91/pythonpy', long_description='https://github.com/Russell91/pythonpy', packages=['pythonpy'], - package_data={'pythonpy': ['data/pycompletion.sh']}, + package_data={'pythonpy': ['completion/pycompletion.sh']}, entry_points = { 'console_scripts': entry_points_scripts }, From 25e04cb96e291b7f3c11d304fd4e6dd7e1a7ad23 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 16 Mar 2016 16:11:43 -0700 Subject: [PATCH 246/254] 0.4.5. --- CHANGELOG.md | 8 ++++++++ pythonpy/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5100d4..b72b130 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,3 +3,11 @@ Removed documentation for flags --ji, --jo, --so, --si, --i, and -fx. These flags are not deprecated. Users should feel comfortable continuing to use them, but from some simple polling I have found that they are infrequently used due to their complexity and unorthodox double-dash form. + +# 0.4.5 +Pythonpy no longer automatically tries to install pycompletion.sh into /etc/bash_completion.d +The .sh script is provided in the completion directory and users that want this feature may add + + source $(python -c 'import os; import pkg_resources; print os.path.abspath(pkg_resources.resource_filename("pythonpy.completion", "pycompletion.sh"))') + +to their .bashrc. diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index 9a8e054..68eb9b6 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.4' +__version__ = '0.4.5' diff --git a/setup.py b/setup.py index bc5e900..96e04eb 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='pythonpy', - version='0.4.4', + version='0.4.5', description='python -c, with tab completion and shorthand', #data_files=data_files, license='MIT', From ec60bec165f03359029d385f70d0164f4345e941 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 16 Mar 2016 16:20:59 -0700 Subject: [PATCH 247/254] 0.4.6 --- pythonpy/__init__.py | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index 68eb9b6..ab45471 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.5' +__version__ = '0.4.6' diff --git a/setup.py b/setup.py index 96e04eb..ddb4acb 100755 --- a/setup.py +++ b/setup.py @@ -12,14 +12,14 @@ setup( name='pythonpy', - version='0.4.5', + version='0.4.6', description='python -c, with tab completion and shorthand', #data_files=data_files, license='MIT', url='https://github.com/Russell91/pythonpy', long_description='https://github.com/Russell91/pythonpy', packages=['pythonpy'], - package_data={'pythonpy': ['completion/pycompletion.sh']}, + package_data={'pythonpy': ['completion/pycompletion.sh, completion/__init__.py']}, entry_points = { 'console_scripts': entry_points_scripts }, From 25df605fb3d0f9fa60fe15fb673de1ab61417eaa Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 16 Mar 2016 16:33:40 -0700 Subject: [PATCH 248/254] 0.4.7 --- pythonpy/__init__.py | 2 +- setup.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index ab45471..1e4826d 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.6' +__version__ = '0.4.7' diff --git a/setup.py b/setup.py index ddb4acb..48c16af 100755 --- a/setup.py +++ b/setup.py @@ -12,14 +12,14 @@ setup( name='pythonpy', - version='0.4.6', + version='0.4.7', description='python -c, with tab completion and shorthand', #data_files=data_files, license='MIT', url='https://github.com/Russell91/pythonpy', long_description='https://github.com/Russell91/pythonpy', - packages=['pythonpy'], - package_data={'pythonpy': ['completion/pycompletion.sh, completion/__init__.py']}, + packages=['pythonpy', 'pythonpy.completion'], + package_data={'pythonpy': ['completion/pycompletion.sh']}, entry_points = { 'console_scripts': entry_points_scripts }, From a535c48cc4a12d3abd0ab383da4b3e7d13677aed Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 16 Mar 2016 16:42:19 -0700 Subject: [PATCH 249/254] Added pycompletion.sh information to readme. --- README.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 800e78c..ab60f61 100644 --- a/README.rst +++ b/README.rst @@ -166,5 +166,12 @@ Count words beginning with each letter :: -For more examples, check out the `wiki `__ -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For more examples, check out the `wiki `__. + +Pythonpy also supports ipython style tab completion, which you can enable as follows + +:: + + $ source $(python -c 'import os; import pkg_resources; print(os.path.abspath(pkg_resources.resource_filename("pythonpy.completion", "pycompletion.sh")))') + +:: From d4437c5c2370a24421b9578ce650bf3196fd3faf Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 16 Mar 2016 16:52:56 -0700 Subject: [PATCH 250/254] 0.4.8 --- README.rst | 2 +- pythonpy/__init__.py | 2 +- pythonpy/find_pycompletion.py | 3 +++ setup.py | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100755 pythonpy/find_pycompletion.py diff --git a/README.rst b/README.rst index ab60f61..61d1d3b 100644 --- a/README.rst +++ b/README.rst @@ -172,6 +172,6 @@ Pythonpy also supports ipython style tab completion, which you can enable as fol :: - $ source $(python -c 'import os; import pkg_resources; print(os.path.abspath(pkg_resources.resource_filename("pythonpy.completion", "pycompletion.sh")))') + $ source `find_pycompletion.py` :: diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index 1e4826d..5bf52d5 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.7' +__version__ = '0.4.8' diff --git a/pythonpy/find_pycompletion.py b/pythonpy/find_pycompletion.py new file mode 100755 index 0000000..4117fe0 --- /dev/null +++ b/pythonpy/find_pycompletion.py @@ -0,0 +1,3 @@ +import os +import pkg_resources +print(os.path.abspath(pkg_resources.resource_filename("pythonpy.completion", "pycompletion.sh"))) diff --git a/setup.py b/setup.py index 48c16af..a8cfa30 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='pythonpy', - version='0.4.7', + version='0.4.8', description='python -c, with tab completion and shorthand', #data_files=data_files, license='MIT', @@ -20,6 +20,7 @@ long_description='https://github.com/Russell91/pythonpy', packages=['pythonpy', 'pythonpy.completion'], package_data={'pythonpy': ['completion/pycompletion.sh']}, + scripts=['pythonpy/find_pycompletion.py'], entry_points = { 'console_scripts': entry_points_scripts }, From 82463476382171b51b8bd9159513551b2e7af824 Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Wed, 16 Mar 2016 16:59:01 -0700 Subject: [PATCH 251/254] Added find_pycompletion.sh --- CHANGELOG.md | 4 ++-- README.rst | 2 +- pythonpy/__init__.py | 2 +- pythonpy/{find_pycompletion.py => find_pycompletion.sh} | 2 ++ setup.py | 4 ++-- 5 files changed, 8 insertions(+), 6 deletions(-) rename pythonpy/{find_pycompletion.py => find_pycompletion.sh} (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b72b130..da7eb23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,10 @@ These flags are not deprecated. Users should feel comfortable continuing to use but from some simple polling I have found that they are infrequently used due to their complexity and unorthodox double-dash form. -# 0.4.5 +# 0.4.9 Pythonpy no longer automatically tries to install pycompletion.sh into /etc/bash_completion.d The .sh script is provided in the completion directory and users that want this feature may add - source $(python -c 'import os; import pkg_resources; print os.path.abspath(pkg_resources.resource_filename("pythonpy.completion", "pycompletion.sh"))') + source `find_pycompletion.sh` to their .bashrc. diff --git a/README.rst b/README.rst index 61d1d3b..68ac8fe 100644 --- a/README.rst +++ b/README.rst @@ -172,6 +172,6 @@ Pythonpy also supports ipython style tab completion, which you can enable as fol :: - $ source `find_pycompletion.py` + $ if command -v find_pycompletion.sh>/dev/null; then source `find_pycompletion.sh`; fi :: diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index 5bf52d5..2d80271 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.8' +__version__ = '0.4.9' diff --git a/pythonpy/find_pycompletion.py b/pythonpy/find_pycompletion.sh similarity index 90% rename from pythonpy/find_pycompletion.py rename to pythonpy/find_pycompletion.sh index 4117fe0..08dc6e5 100755 --- a/pythonpy/find_pycompletion.py +++ b/pythonpy/find_pycompletion.sh @@ -1,3 +1,5 @@ +python -c ' import os import pkg_resources print(os.path.abspath(pkg_resources.resource_filename("pythonpy.completion", "pycompletion.sh"))) +' diff --git a/setup.py b/setup.py index a8cfa30..dd3c8e3 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='pythonpy', - version='0.4.8', + version='0.4.9', description='python -c, with tab completion and shorthand', #data_files=data_files, license='MIT', @@ -20,7 +20,7 @@ long_description='https://github.com/Russell91/pythonpy', packages=['pythonpy', 'pythonpy.completion'], package_data={'pythonpy': ['completion/pycompletion.sh']}, - scripts=['pythonpy/find_pycompletion.py'], + scripts=['pythonpy/find_pycompletion.sh'], entry_points = { 'console_scripts': entry_points_scripts }, From 43482e0e1067b93465c6bd5d4e46a87362db6b4b Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Fri, 25 Mar 2016 15:04:59 -0700 Subject: [PATCH 252/254] Fixed OSX completion readline bug. --- pythonpy/__init__.py | 2 +- pythonpy/completion/pycompletion.sh | 14 +++++++------- setup.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index 2d80271..b94cbb0 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.9' +__version__ = '0.4.10' diff --git a/pythonpy/completion/pycompletion.sh b/pythonpy/completion/pycompletion.sh index e218f7c..50a6528 100644 --- a/pythonpy/completion/pycompletion.sh +++ b/pythonpy/completion/pycompletion.sh @@ -1,6 +1,6 @@ _py() { - COMPREPLY=($(pycompleter "${COMP_WORDS[@]}" 2>/dev/null )) + COMPREPLY=($(pycompleter "${COMP_WORDS[@]}" 2>/dev/null | sed 's/.*1034h//')) if [[ ${COMPREPLY[0]} == '_longopt' ]]; then COMPREPLY=() _longopt 2>/dev/null @@ -9,7 +9,7 @@ _py() _py2() { - COMPREPLY=($(pycompleter2 "${COMP_WORDS[@]}" 2>/dev/null )) + COMPREPLY=($(pycompleter2 "${COMP_WORDS[@]}" 2>/dev/null | sed 's/.*1034h//')) if [[ ${COMPREPLY[0]} == '_longopt' ]]; then COMPREPLY=() _longopt 2>/dev/null @@ -18,7 +18,7 @@ _py2() _py2.6() { - COMPREPLY=($(pycompleter2.6 "${COMP_WORDS[@]}" 2>/dev/null )) + COMPREPLY=($(pycompleter2.6 "${COMP_WORDS[@]}" 2>/dev/null | sed 's/.*1034h//')) if [[ ${COMPREPLY[0]} == '_longopt' ]]; then COMPREPLY=() _longopt 2>/dev/null @@ -27,7 +27,7 @@ _py2.6() _py2.7() { - COMPREPLY=($(pycompleter2.7 "${COMP_WORDS[@]}" 2>/dev/null )) + COMPREPLY=($(pycompleter2.7 "${COMP_WORDS[@]}" 2>/dev/null | sed 's/.*1034h//')) if [[ ${COMPREPLY[0]} == '_longopt' ]]; then COMPREPLY=() _longopt 2>/dev/null @@ -36,7 +36,7 @@ _py2.7() _py3() { - COMPREPLY=($(pycompleter3 "${COMP_WORDS[@]}" 2>/dev/null )) + COMPREPLY=($(pycompleter3 "${COMP_WORDS[@]}" 2>/dev/null | sed 's/.*1034h//')) if [[ ${COMPREPLY[0]} == '_longopt' ]]; then COMPREPLY=() _longopt 2>/dev/null @@ -45,7 +45,7 @@ _py3() _py3.3() { - COMPREPLY=($(pycompleter3.3 "${COMP_WORDS[@]}" 2>/dev/null )) + COMPREPLY=($(pycompleter3.3 "${COMP_WORDS[@]}" 2>/dev/null | sed 's/.*1034h//')) if [[ ${COMPREPLY[0]} == '_longopt' ]]; then COMPREPLY=() _longopt 2>/dev/null @@ -53,7 +53,7 @@ _py3.3() } _py3.4() { - COMPREPLY=($(pycompleter3.4 "${COMP_WORDS[@]}" 2>/dev/null )) + COMPREPLY=($(pycompleter3.4 "${COMP_WORDS[@]}" 2>/dev/null | sed 's/.*1034h//')) if [[ ${COMPREPLY[0]} == '_longopt' ]]; then COMPREPLY=() _longopt 2>/dev/null diff --git a/setup.py b/setup.py index dd3c8e3..33361ab 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='pythonpy', - version='0.4.9', + version='0.4.10', description='python -c, with tab completion and shorthand', #data_files=data_files, license='MIT', From f64063d22ff134adee918692a0794da0c2b1db1e Mon Sep 17 00:00:00 2001 From: Russell Stewart Date: Sun, 27 Mar 2016 23:16:30 -0700 Subject: [PATCH 253/254] Changed find_pycompletion to use py to deal with python2 vs 3. --- pythonpy/__init__.py | 2 +- pythonpy/find_pycompletion.sh | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pythonpy/__init__.py b/pythonpy/__init__.py index b94cbb0..d4f3346 100755 --- a/pythonpy/__init__.py +++ b/pythonpy/__init__.py @@ -1 +1 @@ -__version__ = '0.4.10' +__version__ = '0.4.11' diff --git a/pythonpy/find_pycompletion.sh b/pythonpy/find_pycompletion.sh index 08dc6e5..c60e3ad 100755 --- a/pythonpy/find_pycompletion.sh +++ b/pythonpy/find_pycompletion.sh @@ -1,4 +1,4 @@ -python -c ' +py -c ' import os import pkg_resources print(os.path.abspath(pkg_resources.resource_filename("pythonpy.completion", "pycompletion.sh"))) diff --git a/setup.py b/setup.py index 33361ab..67d269b 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='pythonpy', - version='0.4.10', + version='0.4.11', description='python -c, with tab completion and shorthand', #data_files=data_files, license='MIT', From e6b851506553ba1d84207a7a4b2d3c598c965f7a Mon Sep 17 00:00:00 2001 From: Tiago Ilieve Date: Wed, 30 Mar 2016 19:56:58 -0300 Subject: [PATCH 254/254] Fix test suite There were two problems with the test suite. The first one is that it lacked the `__init__.py` file in the `test/` directory, so the tests couldn't be auto discovered. The second one was that one of them was failing with a `TypeError: Unicode-objects must be encoded before hashing` that can be easily fixed. --- test/__init__.py | 0 test/test_pythonpy.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 test/__init__.py diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_pythonpy.py b/test/test_pythonpy.py index 441eddd..45fff1e 100644 --- a/test/test_pythonpy.py +++ b/test/test_pythonpy.py @@ -30,7 +30,7 @@ def test_imports(self): "calendar.weekday(1955, 11, 5)", "csv.list_dialects()", "datetime.timedelta(hours=-5)", - "hashlib.sha224(\"Nobody inspects the spammish repetition\").hexdigest()", + "hashlib.sha224(\"Nobody inspects the spammish repetition\".encode(\"utf-8\")).hexdigest()", "glob.glob('*')", "itertools.product(['a','b'], [1,2])", "json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))",