From f79fb24846eaf5ff4394dccbcc0e17d7397665a3 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Thu, 9 Jun 2016 23:00:45 -0400 Subject: [PATCH 01/15] enable running aliases without adding them to the aliases mapping --- xonsh/built_ins.py | 7 +++++++ xonsh/environ.py | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/xonsh/built_ins.py b/xonsh/built_ins.py index 1652e370a..c74c3af60 100644 --- a/xonsh/built_ins.py +++ b/xonsh/built_ins.py @@ -402,6 +402,13 @@ def run_subproc(cmds, captured=False): stderr = builtins.__xonsh_stderr_uncaptured__ uninew = (ix == last_cmd) and (not _capture_streams) alias = builtins.aliases.get(cmd[0], None) + if builtins.__xonsh_env__.get('BARE_ALIASES') and alias is None: + val = builtins.__xonsh_ctx__.get(cmd[0], None) + if callable(val): + numargs = len(inspect.signature(val).parameters) + if numargs in {2, 4}: + # this looks like it could be an alias. try it + alias = val procinfo['alias'] = alias if (alias is None and builtins.__xonsh_env__.get('AUTO_CD') and diff --git a/xonsh/environ.py b/xonsh/environ.py index 17d3e50ba..87421e028 100644 --- a/xonsh/environ.py +++ b/xonsh/environ.py @@ -81,6 +81,7 @@ DEFAULT_ENSURERS = { 'AUTO_CD': (is_bool, to_bool, bool_to_str), 'AUTO_PUSHD': (is_bool, to_bool, bool_to_str), 'AUTO_SUGGEST': (is_bool, to_bool, bool_to_str), + 'BARE_ALIASES': (is_bool, to_bool, bool_to_str), 'BASH_COMPLETIONS': (is_env_path, str_to_env_path, env_path_to_str), 'CASE_SENSITIVE_COMPLETIONS': (is_bool, to_bool, bool_to_str), re.compile('\w*DIRS$'): (is_env_path, str_to_env_path, env_path_to_str), @@ -191,6 +192,7 @@ DEFAULT_VALUES = { 'AUTO_CD': False, 'AUTO_PUSHD': False, 'AUTO_SUGGEST': True, + 'BARE_ALIASES': True, 'BASH_COMPLETIONS': BASH_COMPLETIONS_DEFAULT, 'CASE_SENSITIVE_COMPLETIONS': ON_LINUX, 'CDPATH': (), @@ -291,6 +293,9 @@ DEFAULT_DOCS = { 'Enable automatic command suggestions based on history, like in the fish ' 'shell.\n\nPressing the right arrow key inserts the currently ' 'displayed suggestion. Only usable with $SHELL_TYPE=prompt_toolkit.'), + 'BARE_ALIASES': VarDocs( + 'Enables running Python functions as aliases even if they are not ' + 'explicitly in the aliases mapping.'), 'BASH_COMPLETIONS': VarDocs( 'This is a list (or tuple) of strings that specifies where the BASH ' 'completion files may be found. The default values are platform ' From c72b6f0905abe3d4b9bef12494ca6a1ef084be21 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Thu, 9 Jun 2016 23:37:59 -0400 Subject: [PATCH 02/15] allow functions to be escaped with @(), allowing anonymous aliases --- xonsh/built_ins.py | 32 +++++++++++++++++++++++++------- xonsh/parsers/base.py | 2 +- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/xonsh/built_ins.py b/xonsh/built_ins.py index c74c3af60..3d5781c24 100644 --- a/xonsh/built_ins.py +++ b/xonsh/built_ins.py @@ -401,14 +401,19 @@ def run_subproc(cmds, captured=False): elif builtins.__xonsh_stderr_uncaptured__ is not None: stderr = builtins.__xonsh_stderr_uncaptured__ uninew = (ix == last_cmd) and (not _capture_streams) + alias = builtins.aliases.get(cmd[0], None) - if builtins.__xonsh_env__.get('BARE_ALIASES') and alias is None: - val = builtins.__xonsh_ctx__.get(cmd[0], None) - if callable(val): - numargs = len(inspect.signature(val).parameters) - if numargs in {2, 4}: - # this looks like it could be an alias. try it - alias = val + if builtins.__xonsh_env__.get('BARE_ALIASES'): + if callable(cmd[0]): + alias = cmd[0] + if alias is None: + val = builtins.__xonsh_ctx__.get(cmd[0], None) + if callable(val): + numargs = len(inspect.signature(val).parameters) + if numargs in {2, 4}: + # this looks like it could be an alias. try it + alias = val + procinfo['alias'] = alias if (alias is None and builtins.__xonsh_env__.get('AUTO_CD') and @@ -633,6 +638,17 @@ def ensure_list_of_strs(x): return rtn +def list_of_strs_or_callables(x): + """Ensures that x is a list of strings or functions""" + if isinstance(x, str) or callable(x): + rtn = [x] + elif isinstance(x, Sequence): + rtn = [i if isinstance(i, str) or callable(i) else str(i) for i in x] + else: + rtn = [str(x)] + return rtn + + def load_builtins(execer=None, config=None, login=False, ctx=None): """Loads the xonsh builtins into the Python builtins. Sets the BUILTINS_LOADED variable to True. @@ -664,6 +680,7 @@ def load_builtins(execer=None, config=None, login=False, ctx=None): builtins.__xonsh_commands_cache__ = CommandsCache() builtins.__xonsh_all_jobs__ = {} builtins.__xonsh_ensure_list_of_strs__ = ensure_list_of_strs + builtins.__xonsh_list_of_strs_or_callables__ = list_of_strs_or_callables # public built-ins builtins.XonshError = XonshError builtins.XonshBlockError = XonshBlockError @@ -733,6 +750,7 @@ def unload_builtins(): 'default_aliases', '__xonsh_all_jobs__', '__xonsh_ensure_list_of_strs__', + '__xonsh_list_of_strs_or_callables__', '__xonsh_history__', ] for name in names: diff --git a/xonsh/parsers/base.py b/xonsh/parsers/base.py index ee0bae846..85f21aee8 100644 --- a/xonsh/parsers/base.py +++ b/xonsh/parsers/base.py @@ -2182,7 +2182,7 @@ class BaseParser(object): def p_subproc_atom_pyeval(self, p): """subproc_atom : AT_LPAREN test RPAREN""" - p0 = xonsh_call('__xonsh_ensure_list_of_strs__', [p[2]], + p0 = xonsh_call('__xonsh_ensure_list_of_strs_or_callables__', [p[2]], lineno=self.lineno, col=self.col) p0._cliarg_action = 'extend' p[0] = p0 From daaab24c29edf3e9706b2a1762d051c6b4d37fc8 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Thu, 9 Jun 2016 23:39:44 -0400 Subject: [PATCH 03/15] changelog --- CHANGELOG.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 84b41bc88..245d4a602 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -26,6 +26,11 @@ Current Developments * ``!(command)`` is now usefully iterable, yielding lines of stdout * Added XonshCalledProcessError, which includes the relevant CompletedCommand. Also handles differences between Py3.4 and 3.5 in CalledProcessError +* Functions are now allowed in subprocess mode arg lists if ``$BARE_ALIASES`` + is ``True``. This allows for aliases to be invoked without having been + explicitly added to the ``aliases`` mapping. +* ``@()`` now passes through functions as well as strings, which allows for + the use of anonymous aliases. **Changed:** From f70e7153375a3b47606cdec53bf52766f8b75e1f Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Thu, 9 Jun 2016 23:52:46 -0400 Subject: [PATCH 04/15] fix parser error --- xonsh/parsers/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xonsh/parsers/base.py b/xonsh/parsers/base.py index 85f21aee8..be3ea1d92 100644 --- a/xonsh/parsers/base.py +++ b/xonsh/parsers/base.py @@ -2182,7 +2182,7 @@ class BaseParser(object): def p_subproc_atom_pyeval(self, p): """subproc_atom : AT_LPAREN test RPAREN""" - p0 = xonsh_call('__xonsh_ensure_list_of_strs_or_callables__', [p[2]], + p0 = xonsh_call('__xonsh_list_of_strs_or_callables__', [p[2]], lineno=self.lineno, col=self.col) p0._cliarg_action = 'extend' p[0] = p0 From e8c10ddc752dd85f8fe40112dc728993e2ea4f24 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Thu, 9 Jun 2016 23:52:53 -0400 Subject: [PATCH 05/15] fix timing issue --- xonsh/built_ins.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/xonsh/built_ins.py b/xonsh/built_ins.py index 3d5781c24..0e247c3e1 100644 --- a/xonsh/built_ins.py +++ b/xonsh/built_ins.py @@ -494,11 +494,6 @@ def run_subproc(cmds, captured=False): raise XonshError(e) procs.append(proc) prev_proc = proc - for proc in procs[:-1]: - try: - proc.stdout.close() - except OSError: - pass if not prev_is_proxy: add_job({ 'cmds': cmds, @@ -519,6 +514,11 @@ def run_subproc(cmds, captured=False): if prev_is_proxy: prev_proc.wait() wait_for_active_job() + for proc in procs[:-1]: + try: + proc.stdout.close() + except OSError: + pass hist = builtins.__xonsh_history__ hist.last_cmd_rtn = prev_proc.returncode # get output From f73d92532498dd5cc883a37c70835f8e6e7bcadc Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Fri, 10 Jun 2016 14:46:04 -0400 Subject: [PATCH 06/15] remove bare alias option --- xonsh/built_ins.py | 19 ++++++------------- xonsh/environ.py | 5 ----- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/xonsh/built_ins.py b/xonsh/built_ins.py index 0e247c3e1..5cd0ed3d6 100644 --- a/xonsh/built_ins.py +++ b/xonsh/built_ins.py @@ -402,24 +402,18 @@ def run_subproc(cmds, captured=False): stderr = builtins.__xonsh_stderr_uncaptured__ uninew = (ix == last_cmd) and (not _capture_streams) - alias = builtins.aliases.get(cmd[0], None) - if builtins.__xonsh_env__.get('BARE_ALIASES'): - if callable(cmd[0]): - alias = cmd[0] - if alias is None: - val = builtins.__xonsh_ctx__.get(cmd[0], None) - if callable(val): - numargs = len(inspect.signature(val).parameters) - if numargs in {2, 4}: - # this looks like it could be an alias. try it - alias = val + if callable(cmd[0]): + alias = cmd[0] + else: + alias = builtins.aliases.get(cmd[0], None) + n = locate_binary(cmd[0]) procinfo['alias'] = alias if (alias is None and builtins.__xonsh_env__.get('AUTO_CD') and len(cmd) == 1 and os.path.isdir(cmd[0]) and - locate_binary(cmd[0]) is None): + n is None): cmd.insert(0, 'cd') alias = builtins.aliases.get('cd', None) @@ -428,7 +422,6 @@ def run_subproc(cmds, captured=False): else: if alias is not None: cmd = alias + cmd[1:] - n = locate_binary(cmd[0]) if n is None: aliased_cmd = cmd else: diff --git a/xonsh/environ.py b/xonsh/environ.py index 87421e028..17d3e50ba 100644 --- a/xonsh/environ.py +++ b/xonsh/environ.py @@ -81,7 +81,6 @@ DEFAULT_ENSURERS = { 'AUTO_CD': (is_bool, to_bool, bool_to_str), 'AUTO_PUSHD': (is_bool, to_bool, bool_to_str), 'AUTO_SUGGEST': (is_bool, to_bool, bool_to_str), - 'BARE_ALIASES': (is_bool, to_bool, bool_to_str), 'BASH_COMPLETIONS': (is_env_path, str_to_env_path, env_path_to_str), 'CASE_SENSITIVE_COMPLETIONS': (is_bool, to_bool, bool_to_str), re.compile('\w*DIRS$'): (is_env_path, str_to_env_path, env_path_to_str), @@ -192,7 +191,6 @@ DEFAULT_VALUES = { 'AUTO_CD': False, 'AUTO_PUSHD': False, 'AUTO_SUGGEST': True, - 'BARE_ALIASES': True, 'BASH_COMPLETIONS': BASH_COMPLETIONS_DEFAULT, 'CASE_SENSITIVE_COMPLETIONS': ON_LINUX, 'CDPATH': (), @@ -293,9 +291,6 @@ DEFAULT_DOCS = { 'Enable automatic command suggestions based on history, like in the fish ' 'shell.\n\nPressing the right arrow key inserts the currently ' 'displayed suggestion. Only usable with $SHELL_TYPE=prompt_toolkit.'), - 'BARE_ALIASES': VarDocs( - 'Enables running Python functions as aliases even if they are not ' - 'explicitly in the aliases mapping.'), 'BASH_COMPLETIONS': VarDocs( 'This is a list (or tuple) of strings that specifies where the BASH ' 'completion files may be found. The default values are platform ' From 1d4c81274f698928e4748c5d8a2a54d655fc382f Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Fri, 10 Jun 2016 14:48:02 -0400 Subject: [PATCH 07/15] changelog update --- CHANGELOG.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 245d4a602..2a7008851 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -26,11 +26,9 @@ Current Developments * ``!(command)`` is now usefully iterable, yielding lines of stdout * Added XonshCalledProcessError, which includes the relevant CompletedCommand. Also handles differences between Py3.4 and 3.5 in CalledProcessError -* Functions are now allowed in subprocess mode arg lists if ``$BARE_ALIASES`` - is ``True``. This allows for aliases to be invoked without having been - explicitly added to the ``aliases`` mapping. -* ``@()`` now passes through functions as well as strings, which allows for - the use of anonymous aliases. +* ``@()`` now passes through functions as well as strings, which allows for the + use of anonymous aliases and aliases not explicitly added to the ``aliases`` + mapping. **Changed:** From b11d77dad70394cb2609d91373b43cf5e01b9c69 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Fri, 10 Jun 2016 14:48:47 -0400 Subject: [PATCH 08/15] update changelog --- CHANGELOG.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2a7008851..f20179956 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -63,6 +63,8 @@ v0.3.4 attempted to load. * Only show the prompt for the wizard if we did not attempt to load any run control files (as opposed to if none were successfully loaded). +* On Windows if bash is not on the path look in the registry for the defaults + install directory for GitForWindows. **Fixed:** @@ -107,8 +109,6 @@ v0.3.3 * RC files are now executed directly in the appropriate context. * ``_`` is now updated by ``![]``, to contain the appropriate ``CompletedCommand`` object. -* On Windows if bash is not on the path look in the registry for the defaults - install directory for GitForWindows. From 8e665439c0ec105b559b7f8c251dd658fb4335db Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Fri, 10 Jun 2016 15:02:39 -0400 Subject: [PATCH 09/15] update tests --- tests/test_imphooks.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_imphooks.py b/tests/test_imphooks.py index 27e4037bc..aacb22040 100644 --- a/tests/test_imphooks.py +++ b/tests/test_imphooks.py @@ -25,23 +25,23 @@ def teardown(): unload_builtins() def test_import(): - with mock_xonsh_env({}): + with mock_xonsh_env({'PATH': []}): import sample assert_equal('hello mom jawaka\n', sample.x) def test_absolute_import(): - with mock_xonsh_env({}): + with mock_xonsh_env({'PATH': []}): from xpack import sample assert_equal('hello mom jawaka\n', sample.x) def test_relative_import(): - with mock_xonsh_env({}): + with mock_xonsh_env({'PATH': []}): from xpack import relimp assert_equal('hello mom jawaka\n', relimp.sample.x) assert_equal('hello mom jawaka\ndark chest of wonders', relimp.y) def test_sub_import(): - with mock_xonsh_env({}): + with mock_xonsh_env({'PATH': []}): from xpack.sub import sample assert_equal('hello mom jawaka\n', sample.x) From 5a785dd4ca4c8b735737fb645cdd3672bd51f91d Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Sat, 11 Jun 2016 01:28:32 -0400 Subject: [PATCH 10/15] rename 'n' variable --- xonsh/built_ins.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/xonsh/built_ins.py b/xonsh/built_ins.py index 5cd0ed3d6..b7f9b96c0 100644 --- a/xonsh/built_ins.py +++ b/xonsh/built_ins.py @@ -406,14 +406,14 @@ def run_subproc(cmds, captured=False): alias = cmd[0] else: alias = builtins.aliases.get(cmd[0], None) - n = locate_binary(cmd[0]) + binary_loc = locate_binary(cmd[0]) procinfo['alias'] = alias if (alias is None and builtins.__xonsh_env__.get('AUTO_CD') and len(cmd) == 1 and os.path.isdir(cmd[0]) and - n is None): + binary_loc is None): cmd.insert(0, 'cd') alias = builtins.aliases.get('cd', None) @@ -422,11 +422,12 @@ def run_subproc(cmds, captured=False): else: if alias is not None: cmd = alias + cmd[1:] - if n is None: + if binary_loc is None: aliased_cmd = cmd else: try: - aliased_cmd = get_script_subproc_command(n, cmd[1:]) + aliased_cmd = get_script_subproc_command(binary_loc, + cmd[1:]) except PermissionError: e = 'xonsh: subprocess mode: permission denied: {0}' raise XonshError(e.format(cmd[0])) From db39040b31ee4243ac5138012dcd795226d127a5 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Sat, 11 Jun 2016 01:40:26 -0400 Subject: [PATCH 11/15] add tests for new callable list --- tests/test_builtins.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_builtins.py b/tests/test_builtins.py index 8150981cb..d8c119a2c 100644 --- a/tests/test_builtins.py +++ b/tests/test_builtins.py @@ -10,7 +10,7 @@ from nose.tools import assert_equal, assert_true, assert_not_in from xonsh import built_ins from xonsh.built_ins import reglob, regexpath, helper, superhelper, \ - ensure_list_of_strs + ensure_list_of_strs, list_of_strs_or_callables from xonsh.environ import Env from xonsh.tools import ON_WINDOWS @@ -105,5 +105,13 @@ def test_ensure_list_of_strs(): obs = ensure_list_of_strs(inp) yield assert_equal, exp, obs +def test_list_of_strs_or_callables(): + f = lambda x: 20 + cases = [(['yo'], 'yo'), (['yo'], ['yo']), (['42'], 42), (['42'], [42]), + ([f], f), ([f], [f])] + for exp, inp in cases: + obs = list_of_strs_or_callables(inp) + yield assert_equal, exp, obs + if __name__ == '__main__': nose.runmodule() From 30460959618e6879bdd7ccc5eba38781b8c6bd89 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Sat, 11 Jun 2016 01:53:11 -0400 Subject: [PATCH 12/15] docs --- docs/tutorial.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index cf2ec3b2e..3a0ea6670 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -460,10 +460,12 @@ Python Evaluation with ``@()`` =============================== The ``@()`` operator form works in subprocess mode, and will evaluate -arbitrary Python code. The result is appended to the subprocess command -list. If the result is a string, it is appended to the argument list. If the -result is a list or other non-string sequence, the contents are converted to -strings and appended to the argument list in order. Otherwise, the result is +arbitrary Python code. The result is appended to the subprocess command list. +If the result is a string, it is appended to the argument list. If the result +is a list or other non-string sequence, the contents are converted to strings +and appended to the argument list in order. If the result is a function, it is +treated as an alias (see the section on `Aliases`_ below), even if it was not +explicitly added to the ``aliases`` mapping. Otherwise, the result is automatically converted to a string. For example, .. code-block:: xonshcon @@ -476,6 +478,8 @@ automatically converted to a string. For example, 4 >>> echo @([42, 'yo']) 42 yo + >>> echo "potato" | @(lambda a, s=None: a[0]+' '+s.strip()+'es\n') mashed + mashed potatoes This syntax can be used inside of a captured or uncaptured subprocess, and can be used to generate any of the tokens in the subprocess command list. From 7db65f7eb6b9a04182d844afc3e21b0bf67eea26 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Sat, 11 Jun 2016 20:52:47 -0400 Subject: [PATCH 13/15] update docs --- docs/tutorial.rst | 65 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 3a0ea6670..6156617fb 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -463,10 +463,10 @@ The ``@()`` operator form works in subprocess mode, and will evaluate arbitrary Python code. The result is appended to the subprocess command list. If the result is a string, it is appended to the argument list. If the result is a list or other non-string sequence, the contents are converted to strings -and appended to the argument list in order. If the result is a function, it is -treated as an alias (see the section on `Aliases`_ below), even if it was not -explicitly added to the ``aliases`` mapping. Otherwise, the result is -automatically converted to a string. For example, +and appended to the argument list in order. If the result in the first position +is a function, it is treated as an alias (see the section on `Aliases`_ below), +even if it was not explicitly added to the ``aliases`` mapping. Otherwise, the +result is automatically converted to a string. For example, .. code-block:: xonshcon @@ -478,8 +478,8 @@ automatically converted to a string. For example, 4 >>> echo @([42, 'yo']) 42 yo - >>> echo "potato" | @(lambda a, s=None: a[0]+' '+s.strip()+'es\n') mashed - mashed potatoes + >>> echo "hello" | @(lambda a, s=None: s.strip + " world") + hello world This syntax can be used inside of a captured or uncaptured subprocess, and can be used to generate any of the tokens in the subprocess command list. @@ -992,9 +992,12 @@ If you were to run ``gco feature-fabulous`` with the above aliases in effect, the command would reduce to ``['git', 'checkout', 'feature-fabulous']`` before being executed. + +Callable Aliases +---------------- Lastly, if an alias value is a function (or other callable), then this function is called *instead* of going to a subprocess command. Such functions -must have the following signature: +must have one of the following two signatures .. code-block:: python @@ -1033,6 +1036,28 @@ must have the following signature: # examples the return code would be 0/success. return (None, "I failed", 2) + +.. code-block:: python + + def _mycmd2(args, stdin, stdout, stderr): + """args will be a list of strings representing the arguments to this + command. stdin is a read-only file-like object, and stdout and stderr + are write-only file-like objects + """ + # This form allows "streaming" data to stdout and stderr + import time + for i in range(5): + time.sleep(i) + print(i, file=stdout) + + # In this form, the return value should be a single integer + # representing the "return code" of the alias (zero if successful, + # non-zero otherwise) + return 0 + + +Adding and Removing Aliases +--------------------------- We can dynamically alter the aliases present simply by modifying the built-in mapping. Here is an example using a function value: @@ -1050,11 +1075,27 @@ built-in mapping. Here is an example using a function value: Otherwise, they may shadow the alias itself, as Python variables take precedence over aliases when xonsh executes commands. -Usually, callable alias commands will be run in a separate thread so that -users may background them interactively. However, some aliases may need to be -executed on the thread that they were called from. This is mostly useful for debuggers -and profilers. To make an alias run in the foreground, decorate its function -with the ``xonsh.proc.foreground`` decorator. + +Anonymous Aliases +----------------- +As mentioned above, it is also possible to treat functions outside this mapping +as aliases, by wrapping them in ``@()``. For example: + +.. code-block:: xonshcon + + >>> @(_banana) + 'My spoon is tooo big!' + >>> echo "hello" | @(lambda args, stdin=None: stdin.strip() + args[0]) world + hello world + + +Foreground-only Aliases +----------------------- +Usually, callable alias commands will be run in a separate thread so that users +they may be run in the background. However, some aliases may need to be +executed on the thread that they were called from. This is mostly useful for +debuggers and profilers. To make an alias run in the foreground, decorate its +function with the ``xonsh.proc.foreground`` decorator. .. code-block:: python From 4a53fb71cdf680a4c407ed3ae3db30f5dfb5d536 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Sat, 11 Jun 2016 21:02:28 -0400 Subject: [PATCH 14/15] add a few test cases --- tests/test_execer.py | 11 +++++++++++ tests/test_parser.py | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/tests/test_execer.py b/tests/test_execer.py index 9a0642754..1f5b43a02 100644 --- a/tests/test_execer.py +++ b/tests/test_execer.py @@ -43,6 +43,17 @@ def test_simple_func(): " return '{user}'.format(user='me')\n") yield check_parse, code +def test_lookup_alias(): + code = ( + 'def foo(a, s=None):\n' + ' return "bar"\n' + '@(foo)\n') + yield check_parse, code + +def test_lookup_anon_alias(): + code = ('echo "hi" | @(lambda a, s=None: a[0]) foo bar baz\n') + yield check_parse, code + def test_simple_func_broken(): code = ('def prompt():\n' " return '{user}'.format(\n" diff --git a/tests/test_parser.py b/tests/test_parser.py index 615ee960b..dda991567 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1553,6 +1553,12 @@ def test_dollar_sub_space(): def test_ls_dot(): yield check_xonsh_ast, {}, '$(ls .)', False +def test_lambda_in_atparens(): + yield check_xonsh_ast, {}, '$(echo hello | @(lambda a, s=None: "hey!") foo bar baz)', False + +def test_nested_madness(): + yield check_xonsh_ast, {}, '$(@$(which echo) ls | @(lambda a, s=None: $(@(s.strip()) @(a[1]))) foo -la baz)', False + def test_ls_dot_nesting(): yield check_xonsh_ast, {}, '$(ls @(None or "."))', False From 2346b8800dfece6440e42722075f95ee67fdc309 Mon Sep 17 00:00:00 2001 From: adam j hartz Date: Sat, 11 Jun 2016 21:13:02 -0400 Subject: [PATCH 15/15] move windows fix to correct version number --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8e44b6bc3..8264150bf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -31,6 +31,8 @@ v0.3.4 ``activate``/``deactivate`` aliases for the conda environements. * Fixed crash resulting from errors other than syntax errors in run control file. +* On Windows if bash is not on the path look in the registry for the defaults + install directory for GitForWindows. v0.3.3