Repository: conda/conda

Issue:
No error generated on non-existent sub-command
```
[root@70a337fa2e68 build_scripts]# conda info

     active environment : None
       user config file : /root/.condarc
 populated config files : /opt/miniconda/.condarc
          conda version : 4.4.0rc0
    conda-build version : 3.0.9
         python version : 3.6.1.final.0
       base environment : /opt/miniconda  (writable)
           channel URLs : https://conda.anaconda.org/c3i_test/linux-64
                          https://conda.anaconda.org/c3i_test/noarch
                          https://repo.continuum.io/pkgs/free/linux-64
                          https://repo.continuum.io/pkgs/free/noarch
                          https://repo.continuum.io/pkgs/r/linux-64
                          https://repo.continuum.io/pkgs/r/noarch
                          https://repo.continuum.io/pkgs/pro/linux-64
                          https://repo.continuum.io/pkgs/pro/noarch
          package cache : /opt/miniconda/pkgs
                          /root/.conda/pkgs
       envs directories : /opt/miniconda/envs
                          /root/.conda/envs
               platform : linux-64
             user-agent : conda/4.4.0rc0 requests/2.14.2 CPython/3.6.1 Linux/3.10.0-514.el7.x86_64 centos/6.9 glibc/2.12
                UID:GID : 0:0
             netrc file : None
           offline mode : False

[root@70a337fa2e68 build_scripts]# conda this-is-not-a-subcommand
[root@70a337fa2e68 build_scripts]# echo $?
1
```


Discussion:
We just need to switch conda over to click already.

Resolving patch:
diff --git a/conda/cli/conda_argparse.py b/conda/cli/conda_argparse.py
--- a/conda/cli/conda_argparse.py
+++ b/conda/cli/conda_argparse.py
@@ -122,14 +122,13 @@ def error(self, message):
             else:
                 argument = None
             if argument and argument.dest == "cmd":
-                m = re.compile(r"invalid choice: u?'([\w\-]+)'").match(exc.message)
+                m = re.match(r"invalid choice: u?'(\w+)'", exc.message)
                 if m:
                     cmd = m.group(1)
                     executable = find_executable('conda-' + cmd)
                     if not executable:
                         from ..exceptions import CommandNotFoundError
                         raise CommandNotFoundError(cmd)
-
                     args = [find_executable('conda-' + cmd)]
                     args.extend(sys.argv[2:])
                     os.execv(args[0], args)
@@ -140,10 +139,13 @@ def print_help(self):
         super(ArgumentParser, self).print_help()
 
         if sys.argv[1:] in ([], ['help'], ['-h'], ['--help']):
-            print(dedent("""
-            other commands, such as "conda build", are available when additional conda
-            packages (e.g. conda-build) are installed
-            """))
+            from .find_commands import find_commands
+            other_commands = find_commands()
+            if other_commands:
+                builder = ['']
+                builder.append("conda commands available from other packages:")
+                builder.extend('  %s' % cmd for cmd in sorted(other_commands))
+                print('\n'.join(builder))
 
 
 class NullCountAction(_CountAction):
diff --git a/conda/exceptions.py b/conda/exceptions.py
--- a/conda/exceptions.py
+++ b/conda/exceptions.py
@@ -170,6 +170,21 @@ def __init__(self, target_path, incompatible_package_dists, context):
 
 class CommandNotFoundError(CondaError):
     def __init__(self, command):
+        conda_commands = {
+            'clean',
+            'config',
+            'create',
+            'help',
+            'info',
+            'install',
+            'list',
+            'package',
+            'remove',
+            'search',
+            'uninstall',
+            'update',
+            'upgrade',
+        }
         build_commands = {
             'build',
             'convert',
@@ -180,22 +195,19 @@ def __init__(self, command):
             'render',
             'skeleton',
         }
-        needs_source = {
-            'activate',
-            'deactivate'
-        }
         if command in build_commands:
-            message = dals("""
-            You need to install conda-build in order to
-            use the 'conda %(command)s' command.
-            """)
-        elif command in needs_source and not on_win:
-            message = dals("""
-            '%(command)s is not a conda command.
-            Did you mean 'source %(command)s'?
-            """)
+            message = "To use 'conda %(command)s', install conda-build."
         else:
-            message = "'%(command)s'"
+            from difflib import get_close_matches
+            from .cli.find_commands import find_commands
+            message = "Error: No command 'conda %(command)s'."
+            choices = conda_commands | build_commands | set(find_commands())
+            close = get_close_matches(command, choices)
+            if close:
+                message += "\nDid you mean 'conda %s'?" % close[0]
+        from .base.context import context
+        from .cli.main import init_loggers
+        init_loggers(context)
         super(CommandNotFoundError, self).__init__(message, command=command)
 
 


Test patch:
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
--- a/tests/test_exceptions.py
+++ b/tests/test_exceptions.py
@@ -338,7 +338,8 @@ def test_CommandNotFoundError_simple(self):
                 conda_exception_handler(_raise_helper, exc)
 
         assert not c.stdout
-        assert c.stderr.strip() == "CommandNotFoundError: 'instate'"
+        assert c.stderr.strip() == ("CommandNotFoundError: Error: No command 'conda instate'.\n"
+                                    "Did you mean 'conda install'?")
 
     def test_CommandNotFoundError_conda_build(self):
         cmd = "build"
@@ -359,35 +360,7 @@ def test_CommandNotFoundError_conda_build(self):
                 conda_exception_handler(_raise_helper, exc)
 
         assert not c.stdout
-        assert c.stderr.strip() == ("CommandNotFoundError: You need to install conda-build in order to\n" \
-                                    "use the 'conda build' command.")
-
-    def test_CommandNotFoundError_activate(self):
-        cmd = "activate"
-        exc = CommandNotFoundError(cmd)
-
-        with env_var("CONDA_JSON", "yes", reset_context):
-            with captured() as c:
-                conda_exception_handler(_raise_helper, exc)
-
-        json_obj = json.loads(c.stdout)
-        assert not c.stderr
-        assert json_obj['exception_type'] == "<class 'conda.exceptions.CommandNotFoundError'>"
-        assert json_obj['message'] == text_type(exc)
-        assert json_obj['error'] == repr(exc)
-
-        with env_var("CONDA_JSON", "no", reset_context):
-            with captured() as c:
-                conda_exception_handler(_raise_helper, exc)
-
-        assert not c.stdout
-
-        if on_win:
-            message = "CommandNotFoundError: 'activate'"
-        else:
-            message = ("CommandNotFoundError: 'activate is not a conda command.\n"
-                       "Did you mean 'source activate'?")
-        assert c.stderr.strip() == message
+        assert c.stderr.strip() == ("CommandNotFoundError: To use 'conda build', install conda-build.")
 
     @patch('requests.post', side_effect=(
             AttrDict(headers=AttrDict(Location='somewhere.else'), status_code=302,