Repository: conda/conda

Issue:
ENH: add --force option to conda env create
Both conda env create and conda create need a --force option that will overwrite the environment if it already exists.

conda could warn the user and prompt for confirmation but must also allow for --yes option to not prompt so conda can be used effectively in automation scripts that won't have human interaction.

ENH: add --force option to conda env create
Both conda env create and conda create need a --force option that will overwrite the environment if it already exists.

conda could warn the user and prompt for confirmation but must also allow for --yes option to not prompt so conda can be used effectively in automation scripts that won't have human interaction.



Discussion:
FWIW, the `--mkdir` flag on `conda install` creates a new environment if it doesn't already exists.  Depending on your use case, it might be a close alternative that will work just as well.
+1 on this. In general I've been having a really hard time using conda with automation.

* You can't install it via package manager
* There are tonnes of prompts you have to -y through (plus the non-obvious `-b` in the install script)
* Ubuntu has dash shell by default, which breaks environment activation
* Hard to make it work with Fabric
* No idempotent way to create environments

It seems like provisioning servers should be an important workflow, but using the tool it feels like not much thought has gone into this?
FWIW, the `--mkdir` flag on `conda install` creates a new environment if it doesn't already exists.  Depending on your use case, it might be a close alternative that will work just as well.
+1 on this. In general I've been having a really hard time using conda with automation.

* You can't install it via package manager
* There are tonnes of prompts you have to -y through (plus the non-obvious `-b` in the install script)
* Ubuntu has dash shell by default, which breaks environment activation
* Hard to make it work with Fabric
* No idempotent way to create environments

It seems like provisioning servers should be an important workflow, but using the tool it feels like not much thought has gone into this?

Resolving patch:
diff --git a/conda/cli/common.py b/conda/cli/common.py
--- a/conda/cli/common.py
+++ b/conda/cli/common.py
@@ -7,6 +7,7 @@
 from .._vendor.auxlib.ish import dals
 from ..base.constants import ROOT_ENV_NAME
 from ..base.context import context
+from ..common.constants import NULL
 from ..common.io import swallow_broken_pipe
 from ..common.path import paths_equal
 from ..common.serialize import json_dump
@@ -43,18 +44,18 @@ def confirm(message="Proceed", choices=('yes', 'no'), default='yes'):
             return choices[user_choice]
 
 
-def confirm_yn(message="Proceed", default='yes'):
-    if context.dry_run:
+def confirm_yn(message="Proceed", default='yes', dry_run=NULL):
+    dry_run = context.dry_run if dry_run is NULL else dry_run
+    if dry_run:
         from ..exceptions import DryRunExit
         raise DryRunExit()
     if context.always_yes:
         return True
     try:
-        choice = confirm(message=message, choices=('yes', 'no'),
-                         default=default)
+        choice = confirm(message=message, choices=('yes', 'no'))
     except KeyboardInterrupt as e:  # pragma: no cover
         from ..exceptions import CondaSystemExit
-        raise CondaSystemExit("\nOperation aborted.  Exiting.", e)
+        raise CondaSystemExit("\nOperation aborted.  Exiting.")
     if choice == 'no':
         from ..exceptions import CondaSystemExit
         raise CondaSystemExit("Exiting.")
diff --git a/conda/cli/main_create.py b/conda/cli/main_create.py
--- a/conda/cli/main_create.py
+++ b/conda/cli/main_create.py
@@ -3,10 +3,36 @@
 
 from __future__ import absolute_import, division, print_function, unicode_literals
 
+from logging import getLogger
+from os.path import isdir
+
+from .common import confirm_yn
 from .install import install
-from ..gateways.disk.delete import delete_trash
+from ..base.context import context
+from ..common.path import paths_equal
+from ..exceptions import CondaValueError
+from ..gateways.disk.delete import delete_trash, rm_rf
+from ..gateways.disk.test import is_conda_environment
+
+log = getLogger(__name__)
 
 
 def execute(args, parser):
+    if is_conda_environment(context.target_prefix):
+        if paths_equal(context.target_prefix, context.root_prefix):
+            raise CondaValueError("The target prefix is the base prefix. Aborting.")
+        confirm_yn("WARNING: A conda environment already exists at '%s'\n"
+                   "Removing existing environment" % context.target_prefix,
+                   default='no',
+                   dry_run=False)
+        log.info("Removing existing environment %s", context.target_prefix)
+        rm_rf(context.target_prefix)
+    elif isdir(context.target_prefix):
+        confirm_yn("WARNING: A directory already exists at the target location '%s'\n"
+                   "but it is not a conda environment.\n"
+                   "Continue creating environment" % context.target_prefix,
+                   default='no',
+                   dry_run=False)
+
     install(args, parser, 'create')
     delete_trash()
diff --git a/conda/exceptions.py b/conda/exceptions.py
--- a/conda/exceptions.py
+++ b/conda/exceptions.py
@@ -285,7 +285,7 @@ def __init__(self, message, *args):
 
 class DryRunExit(CondaExitZero):
     def __init__(self):
-        msg = 'Dry run exiting'
+        msg = 'Dry run. Exiting.'
         super(DryRunExit, self).__init__(msg)
 
 
@@ -295,11 +295,6 @@ def __init__(self, *args):
         super(CondaSystemExit, self).__init__(msg)
 
 
-class SubprocessExit(CondaExitZero):
-    def __init__(self, *args, **kwargs):
-        super(SubprocessExit, self).__init__(*args, **kwargs)
-
-
 class PaddingError(CondaError):
     def __init__(self, dist, placeholder, placeholder_length):
         msg = ("Placeholder of length '%d' too short in package %s.\n"
@@ -787,17 +782,25 @@ def maybe_raise(error, context):
 
 def print_conda_exception(exc_val, exc_tb=None):
     from .base.context import context
+    rc = getattr(exc_val, 'return_code', None)
     if context.debug or context.verbosity > 0:
         sys.stderr.write(_format_exc(exc_val, exc_tb))
         sys.stderr.write('\n')
     elif context.json:
-        import json
-        stdoutlog = getLogger('conda.stdout')
-        exc_json = json.dumps(exc_val.dump_map(), indent=2, sort_keys=True, cls=EntityEncoder)
-        stdoutlog.info("%s\n" % exc_json)
+        if rc == 0:
+            # suppress DryRunExit and CondaSystemExit messages
+            pass
+        else:
+            import json
+            stdoutlog = getLogger('conda.stdout')
+            exc_json = json.dumps(exc_val.dump_map(), indent=2, sort_keys=True, cls=EntityEncoder)
+            stdoutlog.info("%s\n" % exc_json)
     else:
         stderrlog = getLogger('conda.stderr')
-        stderrlog.info("\n%r\n", exc_val)
+        if rc == 0:
+            stderrlog.info("\n%s\n", exc_val)
+        else:
+            stderrlog.info("\n%r\n", exc_val)
 
 
 def _format_exc(exc_val=None, exc_tb=None):
@@ -842,9 +845,6 @@ def error_upload_url(self):
         return context.error_upload_url
 
     def handle_exception(self, exc_val, exc_tb):
-        return_code = getattr(exc_val, 'return_code', None)
-        if return_code == 0:
-            return 0
         if isinstance(exc_val, CondaHTTPError):
             return self.handle_reportable_application_exception(exc_val, exc_tb)
         if isinstance(exc_val, CondaError):


Test patch: