diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 4f3904cbf..6f6025408 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -15,6 +15,33 @@ develop xonsh. .. note:: All code changes must go through the pull request review procedure. + +Making Your First Change +======================== + +First, install xonsh from source and open a xonsh shell in your favorite +terminal application. See installation instructions for details. + +Next, make a trivial change (e.g. ``print("hello!")`` in ``main.py``). + +Finally, run the following commands. You should see the effects of your change +(e.g. ``hello!``):: + + $ $XONSH_DEBUG=1 + $ xonsh + +The xonsh build process collapses all Python source files into a single +``__amalgam__.py`` file. When xonsh is started with a falsy value for +`$XONSH_DEBUG `_, it imports Python modules straight from +``__amalgam__.py``, which decreases startup times by eliminating the cost of +runtime imports. But setting ``$ $XONSH_DEBUG=1`` will suppress amalgamated +imports. Reloading the xonsh shell (``$ xonsh``) won't simply import the stale +``__amalgam__.py`` file that doesn't contain your new change, but will instead +import the unamalgamated source code which does contain your change. You can now +load every subsequent change by reloading xonsh, and if your code changes don't +seem to have any effect, make sure you check ``$XONSH_DEBUG`` first! + + Changelog ========= Pull requests will often have CHANGELOG entries associated with. However, @@ -169,7 +196,7 @@ Run all the tests using pytest:: $ py.test -q -Use "-q" to keep pytest from outputting a bunch of info for every test. +Use "-q" to keep pytest from outputting a bunch of info for every test. ---------------------------------- Running the Tests - Advanced diff --git a/docs/index.rst b/docs/index.rst index 6e7efd56f..c603cea6b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -244,7 +244,9 @@ Contributing We highly encourage contributions to xonsh! If you would like to contribute, it is as easy as forking the repository on GitHub, making your changes, and issuing a pull request. If you have any questions about this process don't -hesitate to ask the mailing list (xonsh@googlegroups.com). +hesitate to ask the mailing list (xonsh@googlegroups.com) or the `Gitter `_ channel. + +See the `Developer's Guide `_ for more information about contributing. ========== Contact Us diff --git a/docs/talks_and_articles.rst b/docs/talks_and_articles.rst index b0406bfab..8349aec76 100644 --- a/docs/talks_and_articles.rst +++ b/docs/talks_and_articles.rst @@ -5,9 +5,9 @@ Here are some talks, articles, and other sundry about your favorite shell. Talks ============ -**PyCon 2016:** presented by Anthony Scopatz +**Python Nordeste 2016:** presented by Lucas Inojosa http://lucasicf.github.io/talks/shell_python/ -**Python Nordeste 2016:** presented by Lucas Inojosa +**PyCon 2016:** presented by Anthony Scopatz .. raw:: html diff --git a/news/enc.rst b/news/enc.rst new file mode 100644 index 000000000..2851325d1 --- /dev/null +++ b/news/enc.rst @@ -0,0 +1,16 @@ +**Added:** + +* ``xon.sh`` script now sets ``$LANG=C.UTF8`` in the event that no encoding + is detected. + +**Changed:** + +* xonfig command now dumps more encoding related settings. + +**Deprecated:** None + +**Removed:** None + +**Fixed:** None + +**Security:** None diff --git a/news/vox_opts.rst b/news/vox_opts.rst new file mode 100644 index 000000000..c49729578 --- /dev/null +++ b/news/vox_opts.rst @@ -0,0 +1,14 @@ +**Added:** None + +**Changed:** + +* The vox xontrib now takes flags very similar to Python's venv tool. Use + ``vox --help `` to learn more. + +**Deprecated:** None + +**Removed:** None + +**Fixed:** None + +**Security:** None diff --git a/scripts/xon.sh b/scripts/xon.sh index e9b39b88f..be9674819 100755 --- a/scripts/xon.sh +++ b/scripts/xon.sh @@ -1,2 +1,10 @@ #!/bin/sh + +# set locale if it is totally undefined +if [ -z "${LC_ALL+x}" ] && [ -z "${LC_CTYPE+x}" ] && \ + [ -z "${LANG+x}" ] && [ -z "${LANGUAGE+x}" ]; then + export LANG=C.UTF-8 +fi + +# run python /usr/bin/env PYTHONUNBUFFERED=1 python3 -u -m xonsh $@ diff --git a/xonsh/base_shell.py b/xonsh/base_shell.py index 6aa9107a1..e051b3e89 100644 --- a/xonsh/base_shell.py +++ b/xonsh/base_shell.py @@ -23,6 +23,7 @@ class _TeeOut(object): self.buffer = buf self.stdout = sys.stdout self.encoding = self.stdout.encoding + self.errors = self.stdout.errors sys.stdout = self def __del__(self): @@ -55,6 +56,7 @@ class _TeeErr(object): self.buffer = buf self.stderr = sys.stderr self.encoding = self.stderr.encoding + self.errors = self.stderr.errors sys.stderr = self def __del__(self): diff --git a/xonsh/environ.py b/xonsh/environ.py index 312293c1e..9abe0a5fe 100644 --- a/xonsh/environ.py +++ b/xonsh/environ.py @@ -110,6 +110,7 @@ DEFAULT_ENSURERS = LazyObject(lambda: { 'IGNOREEOF': (is_bool, to_bool, bool_to_str), 'INTENSIFY_COLORS_ON_WIN': (always_false, intensify_colors_on_win_setter, bool_to_str), + 'LANG': (is_string, ensure_string, ensure_string), 'LC_COLLATE': (always_false, locale_convert('LC_COLLATE'), ensure_string), 'LC_CTYPE': (always_false, locale_convert('LC_CTYPE'), ensure_string), 'LC_MESSAGES': (always_false, locale_convert('LC_MESSAGES'), ensure_string), @@ -253,6 +254,7 @@ def DEFAULT_VALUES(): 'IGNOREEOF': False, 'INDENT': ' ', 'INTENSIFY_COLORS_ON_WIN': True, + 'LANG': 'C.UTF-8', 'LC_CTYPE': locale.setlocale(locale.LC_CTYPE), 'LC_COLLATE': locale.setlocale(locale.LC_COLLATE), 'LC_TIME': locale.setlocale(locale.LC_TIME), @@ -429,6 +431,7 @@ DEFAULT_DOCS = LazyObject(lambda: { 'which are hard to read, are replaced with cyan. Other colors are ' 'generally replaced by their bright counter parts.', configurable=ON_WINDOWS), + 'LANG': VarDocs('Fallback locale setting for systems where it matters'), 'LOADED_CONFIG': VarDocs( 'Whether or not the xonsh config file was loaded', configurable=False), diff --git a/xonsh/xonfig.py b/xonsh/xonfig.py index 8b3daf50d..9e1b470fc 100644 --- a/xonsh/xonfig.py +++ b/xonsh/xonfig.py @@ -336,6 +336,7 @@ def _xonfig_format_json(data): def _info(ns): + env = builtins.__xonsh_env__ data = [ ('xonsh', XONSH_VERSION), ('Git SHA', githash()), @@ -343,9 +344,9 @@ def _info(ns): ('PLY', ply.__version__), ('have readline', is_readline_available()), ('prompt toolkit', ptk_version() or None), - ('shell type', builtins.__xonsh_env__.get('SHELL_TYPE')), + ('shell type', env.get('SHELL_TYPE')), ('pygments', pygments_version()), - ('on posix', ON_POSIX), + ('on posix', bool(ON_POSIX)), ('on linux', ON_LINUX)] if ON_LINUX: data.append(('distro', linux_distro())) @@ -355,6 +356,8 @@ def _info(ns): ('on cygwin', ON_CYGWIN), ('is superuser', is_superuser()), ('default encoding', DEFAULT_ENCODING), + ('xonsh encoding', env.get('XONSH_ENCODING')), + ('encoding errors', env.get('XONSH_ENCODING_ERRORS')), ]) formatter = _xonfig_format_json if ns.json else _xonfig_format_human s = formatter(data) diff --git a/xontrib/vox.py b/xontrib/vox.py index 129339c82..fb5dbffdf 100644 --- a/xontrib/vox.py +++ b/xontrib/vox.py @@ -2,77 +2,108 @@ import sys as _sys import xontrib.voxapi as _voxapi +import xonsh.lazyasd as _lazyasd class _VoxHandler: """Vox is a virtual environment manager for xonsh.""" + def parser(): + from argparse import ArgumentParser + parser = ArgumentParser(prog='vox', description=__doc__) + subparsers = parser.add_subparsers(dest='command') + + create = subparsers.add_parser( + 'new', aliases=['create'], + help='Create a new virtual environment' + ) + create.add_argument('name', metavar='ENV', + help='The environments to create') + + create.add_argument('--system-site-packages', default=False, + action='store_true', dest='system_site', + help='Give the virtual environment access to the ' + 'system site-packages dir.') + + from xonsh.platform import ON_WINDOWS + group = create.add_mutually_exclusive_group() + group.add_argument('--symlinks', default=not ON_WINDOWS, + action='store_true', dest='symlinks', + help='Try to use symlinks rather than copies, ' + 'when symlinks are not the default for ' + 'the platform.') + group.add_argument('--copies', default=ON_WINDOWS, + action='store_false', dest='symlinks', + help='Try to use copies rather than symlinks, ' + 'even when symlinks are the default for ' + 'the platform.') + create.add_argument('--without-pip', dest='with_pip', + default=True, action='store_false', + help='Skips installing or upgrading pip in the ' + 'virtual environment (pip is bootstrapped ' + 'by default)') + + activate = subparsers.add_parser( + 'activate', aliases=['workon', 'enter'], + help='Activate virtual environment' + ) + activate.add_argument('name', metavar='ENV', + help='The environment to activate') + subparsers.add_parser('deactivate', aliases=['exit'], help='Deactivate current virtual environment') + subparsers.add_parser('list', aliases=['ls'], help='List all available environments') + remove = subparsers.add_parser('remove', aliases=['rm', 'delete', 'del'], help='Remove virtual environment') + remove.add_argument('names', metavar='ENV', nargs='+', + help='The environments to remove') + subparsers.add_parser('help', help='Show this help message') + return parser + + parser = _lazyasd.LazyObject(parser, locals(), 'parser') + + aliases = { + 'create': 'new', + 'workon': 'activate', + 'enter': 'activate', + 'exit': 'deactivate', + 'ls': 'list', + 'rm': 'remove', + 'delete': 'remove', + 'del': 'remove', + } + def __init__(self): - """Ensure that $VIRTUALENV_HOME is defined and declare the available vox commands""" - self.vox = _voxapi.Vox() - self.commands = { - ('new',): self.create_env, - ('activate', 'workon', 'enter'): self.activate_env, - ('deactivate', 'exit'): self.deactivate_env, - ('list', 'ls'): self.list_envs, - ('remove', 'rm', 'delete', 'del'): self.remove_envs, - ('help', '-h', '--help'): self.show_help - } - def __call__(self, args, stdin=None): """Call the right handler method for a given command.""" - if not args: - self.show_help() - return None + args = self.parser.parse_args(args) + cmd = self.aliases.get(args.command, args.command) + if cmd is None: + self.parser.print_usage() + else: + getattr(self, 'cmd_'+cmd)(args, stdin) - command_name, params = args[0], args[1:] - - try: - command = [ - self.commands[aliases] for aliases in self.commands - if command_name in aliases - ][0] - - command(*params) - - except IndexError: - print('Command "%s" doesn\'t exist.\n' % command_name) - self.print_commands() - - def create_env(self, name): + def cmd_new(self, args, stdin=None): """Create a virtual environment in $VIRTUALENV_HOME with python3's ``venv``. - - Parameters - ---------- - name : str - Virtual environment name """ print('Creating environment...') - self.vox.create(name) + self.vox.create(args.name) msg = 'Environment {0!r} created. Activate it with "vox activate {0}".\n' - print(msg.format(name)) + print(msg.format(args.name)) - def activate_env(self, name): + def cmd_activate(self, args, stdin=None): """Activate a virtual environment. - - Parameters - ---------- - name : str - Virtual environment name """ try: - self.vox.activate(name) + self.vox.activate(args.name) except KeyError: print('This environment doesn\'t exist. Create it with "vox new %s".\n' % name, file=_sys.stderr) return None else: - print('Activated "%s".\n' % name) + print('Activated "%s".\n' % args.name) - def deactivate_env(self): + def cmd_deactivate(self, args, stdin=None): """Deactive the active virtual environment.""" if self.vox.active() is None: @@ -81,11 +112,11 @@ class _VoxHandler: env_name = self.vox.deactivate() print('Deactivated "%s".\n' % env_name) - def list_envs(self): + def cmd_list(self, args, stdin=None): """List available virtual environments.""" try: - envs = list(self.vox.keys()) + envs = sorted(self.vox.keys()) except PermissionError: print('No permissions on VIRTUALENV_HOME') return None @@ -97,15 +128,10 @@ class _VoxHandler: print('Available environments:') print('\n'.join(envs)) - def remove_envs(self, *names): + def cmd_remove(self, args, stdin=None): """Remove virtual environments. - - Parameters - ---------- - names : list - list of virtual environment names """ - for name in names: + for name in args.names: try: del self.vox[name] except _voxapi.EnvironmentInUse: @@ -116,35 +142,8 @@ class _VoxHandler: print('Environment "%s" removed.' % name) print() - def show_help(self): - """Show help.""" - - print(self.__doc__, '\n') - self.print_commands() - - @staticmethod - def print_commands(): - """Print available vox commands.""" - - print("""Available commands: - vox new - Create new virtual environment in $VIRTUALENV_HOME - - vox activate (workon, enter) - Activate virtual environment - - vox deactivate (exit) - Deactivate current virtual environment - - vox list (ls) - List all available environments - - vox remove (rm, delete, del) ... - Remove virtual environments - - vox help (-h, --help) - Show help -""") + def cmd_help(self, args, stdin=None): + self.parser.print_help() @classmethod def handle(cls, args, stdin=None):