diff --git a/.bashrc b/.bashrc index 87b863d0052d00895abe45334745987372aa5b53..8af1def1a1ebdaca3c56866c6492b36ac5bb7bc0 100644 --- a/.bashrc +++ b/.bashrc @@ -16,7 +16,7 @@ export PATH="$HOME/Documents/Shore/ssl-ca:$PATH" export PATH="$HOME/.cargo/bin:$PATH" export PATH="$HOME/.cabal/bin:$PATH" export PATH="$HOME/Documents/bin:$PATH" -export PYTHONSTARTUP=~/.config/python/startup.py +export PYTHONSTARTUP=~/.config/pythonrc/pythonrc.py export AWS_DEFAULT_PROFILE='shore' export ANSIBLE_VERBOSITY=2 export ANSIBLE_COMMAND_WARNINGS=True diff --git a/.config/python/startup/10_pythonrc.py b/.config/python/startup/10_pythonrc.py deleted file mode 100644 index 80407af81aee8bbae7140068b969c9c42b2c778a..0000000000000000000000000000000000000000 --- a/.config/python/startup/10_pythonrc.py +++ /dev/null @@ -1,561 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# The MIT License (MIT) -# -# Copyright (c) 2015-2017 Steven Fernandez -# -# 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. - -"""pymp - lonetwin's pimped-up pythonrc - -This file will be executed when the Python interactive shell is started, if -$PYTHONSTARTUP is in your environment and points to this file. You could -also make this file executable and call it directly. - -This file creates an InteractiveConsole instance, which provides: - * execution history - * colored prompts and pretty printing - * auto-indentation - * intelligent tab completion:¹ - * source code listing for objects - * session history editing using your $EDITOR, as well as editing of - source files for objects or regular files - * temporary escape to $SHELL or ability to execute a shell command and - capturing the result into the '_' variable - * convenient printing of doc stings and search for entries in online docs - * auto-execution of a virtual env specific (`.venv_rc.py`) file at startup - -If you have any other good ideas please feel free to submit issues/pull requests. - -¹ Since python 3.4 the default interpreter also has tab completion -enabled however it does not do pathname completion -""" - - -# Fix for Issue #5 -# - Exit if being called from within ipython -try: - import sys - __IPYTHON__ and sys.exit(0) -except NameError: - pass - -try: - import builtins -except ImportError: - import __builtin__ as builtins -import atexit -import glob -import inspect -import keyword -import os -import pkgutil -import pprint -import re -import readline -import rlcompleter -import shlex -import signal -import subprocess -import webbrowser - -from code import InteractiveConsole -from collections import namedtuple -from functools import partial -from tempfile import NamedTemporaryFile - - -__version__ = "0.6.4" - - -config = dict( - HISTFILE = os.path.expanduser("~/.python_history"), - HISTSIZE = -1, - EDITOR = os.getenv('EDITOR', 'vi'), - SHELL = os.getenv('SHELL', '/bin/bash'), - EDIT_CMD = '\e', - SH_EXEC = '!', - DOC_CMD = '?', - DOC_URL = "https://docs.python.org/{sys.version_info.major}/search.html?q={term}", - HELP_CMD = '\h', - LIST_CMD = '\l', - VENV_RC = ".venv_rc.py" -) - - -class ImprovedConsole(InteractiveConsole, object): - """ - Welcome to lonetwin's pimped up python prompt - - You've got color, tab completion, auto-indentation, pretty-printing - and more ! - - * A tab with preceding text will attempt auto-completion of - keywords, names in the current namespace, attributes and methods. - If the preceding text has a '/', filename completion will be - attempted. Without preceding text four spaces will be inserted. - - * History will be saved in {HISTFILE} when you exit. - - * If you create a file named {VENV_RC} in the current directory, the - contents will be executed in this session before the prompt is - shown. - - * Typing out a defined name followed by a '{DOC_CMD}' will print out - the object's __doc__ attribute if one exists. - (eg: []? / str? / os.getcwd? ) - - * Typing '{DOC_CMD}{DOC_CMD}' after something will search for the - term at {DOC_URL} - (eg: try webbrowser.open??) - - * Open the your editor with current session history, source code of - objects or arbitrary files, using the '{EDIT_CMD}' command. - - * List source code for objects using the '{LIST_CMD}' command. - - * Execute shell commands using the '{SH_EXEC}' command. - - Try `<cmd> -h` for any of the commands to learn more. - - The EDITOR, SHELL, command names and more can be changed in the - config dict at the top of this file. Make this your own ! - """ - - def __init__(self, tab=' ', *args, **kwargs): - self.session_history = [] # This holds the last executed statements - self.buffer = [] # This holds the statement to be executed - self.tab = tab - self._indent = '' - super(ImprovedConsole, self).__init__(*args, **kwargs) - self.init_color_functions() - self.init_readline() - self.init_prompt() - self.init_pprint() - - def init_color_functions(self): - """Populates globals dict with some helper functions for colorizing text - """ - def colorize(color_code, text, bold=True, readline_workaround=False): - reset = '\033[0m' - color = '\033[{0}{1}m'.format('1;' if bold else '', color_code) - # - reason for readline_workaround: http://bugs.python.org/issue20359 - if readline_workaround: - color = '\001{color}\002'.format(color=color) - reset = '\001{reset}\002'.format(reset=reset) - return "{color}{text}{reset}".format(**vars()) - - g = globals() - for code, color in enumerate(['red', 'green', 'yellow', 'blue', 'purple', 'cyan'], 31): - g[color] = partial(colorize, code) - - def init_readline(self): - """Activates history and tab completion - """ - # - mainly borrowed from site.enablerlcompleter() from py3.4+ - - # Reading the initialization (config) file may not be enough to set a - # completion key, so we set one first and then read the file. - readline_doc = getattr(readline, '__doc__', '') - if readline_doc is not None and 'libedit' in readline_doc: - readline.parse_and_bind('bind ^I rl_complete') - else: - readline.parse_and_bind('tab: complete') - - try: - readline.read_init_file() - except OSError: - # An OSError here could have many causes, but the most likely one - # is that there's no .inputrc file (or .editrc file in the case of - # Mac OS X + libedit) in the expected location. In that case, we - # want to ignore the exception. - pass - - if readline.get_current_history_length() == 0: - # If no history was loaded, default to .python_history. - # The guard is necessary to avoid doubling history size at - # each interpreter exit when readline was already configured - # see: http://bugs.python.org/issue5845#msg198636 - try: - readline.read_history_file(config['HISTFILE']) - except IOError: - pass - atexit.register(readline.write_history_file, - config['HISTFILE']) - readline.set_history_length(config['HISTSIZE']) - - # - replace default completer - readline.set_completer(self.improved_rlcompleter()) - - # - enable auto-indenting - readline.set_pre_input_hook(self.auto_indent_hook) - - def init_prompt(self): - """Activates color on the prompt based on python version. - - Also adds the hosts IP if running on a remote host over a - ssh connection. - """ - prompt_color = green if sys.version_info.major == 2 else yellow - sys.ps1 = prompt_color('>>> ', readline_workaround=True) - sys.ps2 = red('... ', readline_workaround=True) - # - if we are over a remote connection, modify the ps1 - if os.getenv('SSH_CONNECTION'): - _, _, this_host, _ = os.getenv('SSH_CONNECTION').split() - sys.ps1 = prompt_color('[{}]>>> '.format(this_host), readline_workaround=True) - sys.ps2 = red('[{}]... '.format(this_host), readline_workaround=True) - - def init_pprint(self): - """Activates pretty-printing of output values. - """ - keys_re = re.compile(r'([\'\("]+(.*?[\'\)"]: ))+?') - color_dict = partial(keys_re.sub, lambda m: purple(m.group())) - format_func = pprint.pformat - if sys.version_info.major > 3 and sys.version.minor > 3: - format_func = partial(pprint.pformat, compact=True) - - def pprint_callback(value): - if value is not None: - try: - rows, cols = os.get_teminal_size() - except AttributeError: - try: - rows, cols = map(int, subprocess.check_output(['stty', 'size']).split()) - except: - cols = 80 - builtins._ = value - formatted = format_func(value, width=cols) - print(color_dict(formatted) if issubclass(type(value), dict) else blue(formatted)) - - sys.displayhook = pprint_callback - - def improved_rlcompleter(self): - """Enhances the default rlcompleter - - The function enhances the default rlcompleter by also doing - pathname completion and module name completion for import - statements. Additionally, it inserts a tab instead of attempting - completion if there is no preceding text. - """ - completer = rlcompleter.Completer(namespace=self.locals) - # - remove / from the delimiters to help identify possibility for path completion - readline.set_completer_delims(readline.get_completer_delims().replace('/', '')) - modlist = frozenset(name for _, name, _ in pkgutil.iter_modules()) - - def complete_wrapper(text, state): - line = readline.get_line_buffer().strip() - if line == '': - return None if state > 0 else self.tab - if state == 0: - if line.startswith('import') or line.startswith('from'): - completer.matches = [name for name in modlist if name.startswith(text)] - else: - match = completer.complete(text, state) - if match is None and '/' in text: - completer.matches = glob.glob(text+'*') - try: - match = completer.matches[state] - return '{}{}'.format(match, ' ' if keyword.iskeyword(match) else '') - except IndexError: - return None - return complete_wrapper - - def auto_indent_hook(self): - """Hook called by readline between printing the prompt and - starting to read input. - """ - readline.insert_text(self._indent) - readline.redisplay() - - def raw_input(self, *args): - """Read the input and delegate if necessary. - """ - line = InteractiveConsole.raw_input(self, *args) - if line == config['HELP_CMD']: - print(cyan(self.__doc__).format(**config)) - line = '' - elif line.startswith(config['EDIT_CMD']): - offset = len(config['EDIT_CMD']) - line = self.process_edit_cmd(line[offset:].strip()) - elif line.startswith(config['SH_EXEC']): - offset = len(config['SH_EXEC']) - line = self.process_sh_cmd(line[offset:].strip()) - elif line.startswith(config['LIST_CMD']): - # - strip off the possible tab-completed '(' - line = line.rstrip('(') - offset = len(config['LIST_CMD']) - line = self.process_list_cmd(line[offset:].strip()) - elif line.endswith(config['DOC_CMD']): - if line.endswith(config['DOC_CMD']*2): - # search for line in online docs - # - strip off the '??' and the possible tab-completed - # '(' or '.' and replace inner '.' with '+' to create the - # query search string - line = line.rstrip(config['DOC_CMD'] + '.(').replace('.', '+') - webbrowser.open(config['DOC_URL'].format(sys=sys, term=line)) - line = '' - else: - line = line.rstrip(config['DOC_CMD'] + '.(') - if not line: - line = 'dir()' - elif keyword.iskeyword(line): - line = 'help("{}")'.format(line) - else: - line = 'print({}.__doc__)'.format(line) - elif line.startswith(self.tab) or self._indent: - if line.strip(): - # if non empty line with an indent, check if the indent - # level has been changed - leading_space = line[:line.index(line.lstrip()[0])] - if self._indent != leading_space: - # indent level changed, update self._indent - self._indent = leading_space - else: - # - empty line, decrease indent - self._indent = self._indent[:-len(self.tab)] - line = self._indent - elif line.startswith('%'): - self.writeline('Y U NO LIKE ME?') - return line - return line or '' - - def push(self, line): - """Wrapper around InteractiveConsole's push method for adding an - indent on start of a block. - """ - more = super(ImprovedConsole, self).push(line) - if more: - if line[-1] in (":", '[', '{', '('): - self._indent += self.tab - else: - self._indent = '' - return more - - def write(self, data): - """Write out data to stderr - """ - sys.stderr.write(red(data)) - - def writeline(self, data): - """Same as write but adds a newline to the end - """ - return self.write('{}\n'.format(data)) - - def resetbuffer(self): - self._indent = '' - previous = '' - for line in self.buffer: - # - replace multiple empty lines with one before writing to session history - stripped = line.strip() - if stripped or stripped != previous: - self.session_history.append(line) - previous = stripped - return super(ImprovedConsole, self).resetbuffer() - - def _doc_to_usage(method): - def inner(self, arg): - arg = arg.strip() - if arg.startswith('-h') or arg.startswith('--help'): - return self.writeline(blue(method.__doc__.strip().format(**config))) - return method(self, arg) - return inner - - def _mktemp_buffer(self, lines): - """Writes lines to a temp file and returns the filename. - """ - with NamedTemporaryFile(mode='w+', suffix='.py', delete=False) as tempbuf: - tempbuf.write('\n'.join(lines)) - return tempbuf.name - - def _exec_from_file(self, filename, quiet=False): - previous = '' - for stmt in open(filename): - # - skip over multiple empty lines - stripped = stmt.strip() - if stripped == '' and stripped == previous: - continue - if not quiet: - self.write(cyan("... {}".format(stmt))) - if not stripped.startswith('#'): - line = stmt.strip('\n') - self.push(line) - readline.add_history(line) - previous = stripped - - def lookup(self, name, namespace=None): - """Lookup the (dotted) object specified with the string `name` - in the specified namespace or in the current namespace if - unspecified. - """ - components = name.split('.', 1) - name = components.pop(0) - obj = getattr(namespace, name, namespace) if namespace else self.locals.get(name) - return self.lookup(components[0], obj) if components else obj - - @_doc_to_usage - def process_edit_cmd(self, arg=''): - """{EDIT_CMD} [object|filename] - - Open {EDITOR} with session history, provided filename or - object's source file. - - - without arguments, a temporary file containing session history is - created and opened in {EDITOR}. On quitting the editor, all - the non commented lines in the file are executed. - - - with a filename argument, the file is opened in the editor. On - close, you are returned bay to the interpreter. - - - with an object name argument, an attempt is made to lookup the - source file of the object and it is opened if found. Else the - argument is treated as a filename. - """ - if arg: - obj = self.lookup(arg) - try: - filename = inspect.getsourcefile(obj) if obj else arg - except (IOError, TypeError, NameError) as e: - return self.writeline(e) - else: - # - make a list of all lines in session history, commenting - # any non-blank lines. - filename = self._mktemp_buffer("# {}".format(line) if line else '' - for line in (line.strip('\n') for line in self.session_history)) - - # - shell out to the editor - os.system('{} {}'.format(config['EDITOR'], filename)) - - # - if arg was not provided (we edited session history), execute - # it in the current namespace - if not arg: - self._exec_from_file(filename) - os.unlink(filename) - - @_doc_to_usage - def process_sh_cmd(self, cmd): - """{SH_EXEC} [cmd [args ...] | {{fmt string}}] - - Escape to {SHELL} or execute `cmd` in {SHELL} - - - without arguments, the current interpreter will be suspended - and you will be dropped in a {SHELL} prompt. Use fg to return. - - - with arguments, the text will be executed in {SHELL} and the - output/error will be displayed. Additionally '_' will contain - a named tuple with the (<stdout>, <stderror>, <return_code>) - for the execution of the command. - - You may pass strings from the global namespace to the command - line using the `.format()` syntax. for example: - - >>> filename = '/does/not/exist' - >>> !ls {{filename}} - ls: cannot access /does/not/exist: No such file or directory - >>> _ - CmdExec(out='', err='ls: cannot access /does/not/exist: No such file or directory\n', rc=2) - """ - if cmd: - try: - cmd = cmd.format(**self.locals) - cmd = shlex.split(cmd) - if cmd[0] == 'cd': - os.chdir(os.path.expanduser(os.path.expandvars(' '.join(cmd[1:]) or '${HOME}'))) - else: - cmd_exec = namedtuple('CmdExec', ['out', 'err', 'rc']) - process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = process.communicate() - rc = process.returncode - print (red(err.decode('utf-8')) if err else green(out.decode('utf-8'), bold=False)) - builtins._ = cmd_exec(out, err, rc) - del cmd_exec - except: - self.showtraceback() - else: - if os.getenv('SSH_CONNECTION'): - # I use the bash function similar to the one below in my - # .bashrc to directly open a python prompt on remote - # systems I log on to. - # function rpython { ssh -t $1 -- "python" } - # Unfortunately, suspending this ssh session, does not place me - # in a shell, so I need to create one: - os.system(config['SHELL']) - else: - os.kill(os.getpid(), signal.SIGSTOP) - - @_doc_to_usage - def process_list_cmd(self, arg): - """ - {LIST_CMD} <object> - List source code for object, if possible. - """ - try: - if not arg: - self.writeline('source list command requires an argument ' - '(eg: {} foo)\n'.format(config['LIST_CMD'])) - src_lines, offset = inspect.getsourcelines(self.lookup(arg)) - except (IOError, TypeError, NameError) as e: - self.writeline(e) - else: - for line_no, line in enumerate(src_lines, offset+1): - self.write(cyan("{0:03d}: {1}".format(line_no, line))) - - def interact(self): - """A forgiving wrapper around InteractiveConsole.interact() - """ - venv_rc_done = '(no venv rc found)' - try: - self._exec_from_file(config['VENV_RC'], quiet=True) - venv_rc_done = green('Successfully executed venv rc !') - except IOError: - pass - - banner = ("Welcome to the ImprovedConsole (version {version})\n" - "Type in {HELP_CMD} for list of features.\n" - "{venv_rc_done}").format( - version=__version__, venv_rc_done=venv_rc_done, **config) - - retries = 2 - while retries: - try: - super(ImprovedConsole, self).interact(banner=banner) - except SystemExit: - # Fixes #2: exit when 'quit()' invoked - break - except: - import traceback - retries -= 1 - print(red("I'm sorry, ImprovedConsole could not handle that !\n" - "Please report an error with this traceback, " - "I would really appreciate that !")) - traceback.print_exc() - - print(red("I shall try to restore the crashed session.\n" - "If the crash occurs again, please exit the session")) - banner = blue("Your crashed session has been restored") - else: - # exit with a Ctrl-D - break - - # Exit the Python shell on exiting the InteractiveConsole - sys.exit() - - -if not os.getenv('SKIP_PYMP'): - # - create our pimped out console and fire it up ! - pymp = ImprovedConsole() - pymp.interact() diff --git a/.config/python/startup/50_pprint.py b/.config/python/startup/50_pprint.py deleted file mode 100755 index 9f9a1997e2841f7209444abb3007575c05409d38..0000000000000000000000000000000000000000 --- a/.config/python/startup/50_pprint.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python -from pprint import PrettyPrinter -pprint = PrettyPrinter().pprint diff --git a/.config/pythonrc b/.config/pythonrc new file mode 160000 index 0000000000000000000000000000000000000000..d9f2c7a9caf0076c011bbe0aa42fca641432ea57 --- /dev/null +++ b/.config/pythonrc @@ -0,0 +1 @@ +Subproject commit d9f2c7a9caf0076c011bbe0aa42fca641432ea57 diff --git a/.githooks/post-merge b/.githooks/post-merge index 42ae6f97a5f7f781b61f4def2e2b6d82a30aaa34..3b0fd3c72aa5755c7c87d09f4a7180fd283d2d3c 100755 --- a/.githooks/post-merge +++ b/.githooks/post-merge @@ -9,8 +9,6 @@ echo Loading dconf config >> /dev/stderr Documents/bin/dconf-load echo Configuring Git repo >> /dev/stderr git config --local status.showUntrackedFiles no -echo Creating Python startup file >> /dev/stderr -Documents/bin/gen-python-startup echo Creating Bash completion scripts >> /dev/stderr Documents/bin/gen-bash-completion echo Adding Cron job >> /dev/stderr diff --git a/.gitmodules b/.gitmodules index 8479cf99a040d5c69dce5c0c06400fbca90b09a7..2f43042a0e50abb675312a69b942b44217a887f0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,3 +21,6 @@ [submodule "Documents/Shore/poquita"] path = Documents/Shore/poquita url = https://www.shore.co.il/git/poquita +[submodule ".config/pythonrc"] + path = .config/pythonrc + url = https://github.com/lonetwin/pythonrc.git diff --git a/Documents/bin/gen-python-startup b/Documents/bin/gen-python-startup deleted file mode 100755 index e25b6cc6e3779fd85fff136782d237b9ba3f16fb..0000000000000000000000000000000000000000 --- a/Documents/bin/gen-python-startup +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -set -eu -find "$HOME/.config/python/startup" -type f \! -name '.*' -print0 | sort --zero | xargs -0 cat > "$HOME/.config/python/startup.py"