Repository: conda/conda

Issue:
conda command-line tool provides a convenience command to run the Python executable from a specified conda environment
I often want to run the Python interpreter from a specific conda environment while knowing only the name of that environment. I know that `conda -e` gives the path to each conda environment, from which I can derive the path of an environment-specific Python interpreter or the `activate` shell script, but this is inconvenient extra step.

It would be convenient to have a conda command like `conda intepreter -n ${environment} -- [args]` that invokes the environment-specific Python interpreter, inherits STDIN and command-line arguments, and returns exit code of Python. Ideally, it would be a drop-in replacement for directly running the Python interpreter, as documented https://docs.python.org/2/tutorial/interpreter.html.

My shell-fu is weak, but I think that something like `"exec /path/to/environment/bin/python "$@"` might work.

conda command-line tool provides a convenience command to run the Python executable from a specified conda environment
I often want to run the Python interpreter from a specific conda environment while knowing only the name of that environment. I know that `conda -e` gives the path to each conda environment, from which I can derive the path of an environment-specific Python interpreter or the `activate` shell script, but this is inconvenient extra step.

It would be convenient to have a conda command like `conda intepreter -n ${environment} -- [args]` that invokes the environment-specific Python interpreter, inherits STDIN and command-line arguments, and returns exit code of Python. Ideally, it would be a drop-in replacement for directly running the Python interpreter, as documented https://docs.python.org/2/tutorial/interpreter.html.

My shell-fu is weak, but I think that something like `"exec /path/to/environment/bin/python "$@"` might work.



Discussion:
Maybe the new conda run command could be used to do this. 

Maybe the new conda run command could be used to do this. 


Resolving patch:
diff --git a/conda/cli/main_run.py b/conda/cli/main_run.py
--- a/conda/cli/main_run.py
+++ b/conda/cli/main_run.py
@@ -7,6 +7,7 @@
 from __future__ import print_function, division, absolute_import
 
 import sys
+import logging
 
 from conda.cli import common
 
@@ -17,6 +18,7 @@ def configure_parser(sub_parsers):
                                description = descr,
                                help = descr)
     common.add_parser_prefix(p)
+    common.add_parser_quiet(p)
     common.add_parser_json(p)
     p.add_argument(
         'package',
@@ -47,6 +49,9 @@ def execute(args, parser):
 
     prefix = common.get_prefix(args)
 
+    if args.quiet:
+        logging.disable(logging.CRITICAL)
+
     if args.package.endswith('.tar.bz2'):
         if app_is_installed(args.package, prefixes=[prefix]):
             fn = args.package
@@ -65,20 +70,38 @@ def execute(args, parser):
             if name == args.package:
                 installed = [conda.resolve.Package(pkg + '.tar.bz2',
                                                    conda.install.is_linked(prefix, pkg))]
+                break
 
-        if not installed:
-            error_message = "App {} not installed.".format(args.package)
-            common.error_and_exit(error_message, json=args.json,
-                                  error_type="AppNotInstalled")
+        if installed:
+            package = max(installed)
+            fn = package.fn
 
-        package = max(installed)
-        fn = package.fn
+            try:
+                subprocess = launch(fn, prefix=prefix,
+                                    additional_args=args.arguments,
+                                    background=args.json)
+                if args.json:
+                    common.stdout_json(dict(fn=fn, pid=subprocess.pid))
+                elif not args.quiet:
+                    print("Started app. Some apps may take a while to finish loading.")
+            except TypeError:
+                execute_command(args.package, prefix, args.arguments, args.json)
+            except Exception as e:
+                common.exception_and_exit(e, json=args.json)
+        else:
+            # Try interpreting it as a command
+            execute_command(args.package, prefix, args.arguments, args.json)
 
+def execute_command(cmd, prefix, additional_args, json=False):
+    from conda.misc import execute_in_environment
     try:
-        subprocess = launch(fn, prefix=prefix, additional_args=args.arguments)
-        if args.json:
-            common.stdout_json(dict(fn=fn, pid=subprocess.pid))
+        process = execute_in_environment(
+            cmd, prefix=prefix, additional_args=additional_args, inherit=not json)
+        if not json:
+            sys.exit(process.wait())
         else:
-            print("Started app. Some apps may take a while to finish loading.")
-    except Exception as e:
-        common.exception_and_exit(e, json=args.json)
+            common.stdout_json(dict(cmd=cmd, pid=process.pid))
+    except OSError:
+        error_message = "App {} not installed.".format(cmd)
+        common.error_and_exit(error_message, json=json,
+                              error_type="AppNotInstalled")
diff --git a/conda/misc.py b/conda/misc.py
--- a/conda/misc.py
+++ b/conda/misc.py
@@ -199,21 +199,29 @@ def install_local_packages(prefix, paths, verbose=False):
     execute_actions(actions, verbose=verbose)
 
 
-def launch(fn, prefix=config.root_dir, additional_args=None):
+def environment_for_conda_environment(prefix=config.root_dir):
+    # prepend the bin directory to the path
+    fmt = r'%s\Scripts' if sys.platform == 'win32' else '%s/bin'
+    binpath = fmt % abspath(prefix)
+    path = r'%s;%s' if sys.platform == 'win32' else '%s:%s'
+    path = path % (binpath, os.getenv('PATH'))
+    env = {'PATH': path}
+    # copy existing environment variables, but not anything with PATH in it
+    for k, v in iteritems(os.environ):
+        if k != 'PATH':
+            env[k] = v
+    return binpath, env
+
+
+def launch(fn, prefix=config.root_dir, additional_args=None, background=False):
     info = install.is_linked(prefix, fn[:-8])
     if info is None:
         return None
 
     if not info.get('type') == 'app':
-        raise Exception('Not an application: %s' % fn)
+        raise TypeError('Not an application: %s' % fn)
 
-    # prepend the bin directory to the path
-    fmt = r'%s\Scripts;%s' if sys.platform == 'win32' else '%s/bin:%s'
-    env = {'PATH': fmt % (abspath(prefix), os.getenv('PATH'))}
-    # copy existing environment variables, but not anything with PATH in it
-    for k, v in iteritems(os.environ):
-        if k != 'PATH':
-            env[k] = v
+    binpath, env = environment_for_conda_environment(prefix)
     # allow updating environment variables from metadata
     if 'app_env' in info:
         env.update(info['app_env'])
@@ -229,7 +237,44 @@ def launch(fn, prefix=config.root_dir, additional_args=None):
     cwd = abspath(expanduser('~'))
     if additional_args:
         args.extend(additional_args)
-    return subprocess.Popen(args, cwd=cwd, env=env, close_fds=False)
+    if sys.platform == 'win32' and background:
+        return subprocess.Popen(args, cwd=cwd, env=env, close_fds=False,
+                                creationflags=subprocess.CREATE_NEW_CONSOLE)
+    else:
+        return subprocess.Popen(args, cwd=cwd, env=env, close_fds=False)
+
+
+def execute_in_environment(cmd, prefix=config.root_dir, additional_args=None,
+                           inherit=True):
+    """Runs ``cmd`` in the specified environment.
+
+    ``inherit`` specifies whether the child inherits stdio handles (for JSON
+    output, we don't want to trample this process's stdout).
+    """
+    binpath, env = environment_for_conda_environment(prefix)
+
+    if sys.platform == 'win32' and cmd == 'python':
+        # python is located one directory up on Windows
+        cmd = join(binpath, '..', cmd)
+    else:
+        cmd = join(binpath, cmd)
+
+    args = [cmd]
+    if additional_args:
+        args.extend(additional_args)
+
+    if inherit:
+        stdin, stdout, stderr = None, None, None
+    else:
+        stdin, stdout, stderr = subprocess.PIPE, subprocess.PIPE, subprocess.PIPE
+
+    if sys.platform == 'win32' and not inherit:
+        return subprocess.Popen(args, env=env, close_fds=False,
+                                stdin=stdin, stdout=stdout, stderr=stderr,
+                                creationflags=subprocess.CREATE_NEW_CONSOLE)
+    else:
+        return subprocess.Popen(args, env=env, close_fds=False,
+                                stdin=stdin, stdout=stdout, stderr=stderr)
 
 
 def make_icon_url(info):


Test patch: