Repository: ESSS/conda-devenv

Issue:
devenv command fails if conda is not on path
I was just updating an env in a linux machine with `/full/path/to/conda devenv -f environment.devenv.yml` and got this traceback:
```
Traceback (most recent call last):
  File "/home/company/Work/miniconda/bin/conda-devenv", line 11, in <module>
    load_entry_point('conda-devenv==0.9.0', 'console_scripts', 'conda-devenv')()
  File "/home/company/Work/miniconda/lib/python2.7/site-packages/conda_devenv/devenv.py", line 356, in main
    retcode = __call_conda_env_update(args, output_filename)
  File "/home/company/Work/miniconda/lib/python2.7/site-packages/conda_devenv/devenv.py", line 268, in __call_conda_env_update
    return subprocess.call(command)
  File "/home/company/Work/miniconda/lib/python2.7/subprocess.py", line 168, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/home/company/Work/miniconda/lib/python2.7/subprocess.py", line 390, in _init_
    errread, errwrite)
  File "/home/company/Work/miniconda/lib/python2.7/subprocess.py", line 1024, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
```
After some time investigating I found out it was because I didn't have conda on PATH. I think it should either not require it to be on path (using absolute path instead) or give a better error message.


Discussion:

Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index a808c8c..796bf32 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -2,6 +2,15 @@
 History
 =======
 
+
+0.9.3 (2017-04-10)
+------------------
+
+* ``conda-devenv`` no longer requires ``conda`` to be on ``PATH`` to work (`#45`_).
+
+.. _`#45`: https://github.com/ESSS/conda-devenv/issues/45
+
+
 0.9.2 (2017-03-27)
 ------------------
 
diff --git a/conda_devenv/devenv.py b/conda_devenv/devenv.py
index 356c4f1..66b6bf9 100644
--- a/conda_devenv/devenv.py
+++ b/conda_devenv/devenv.py
@@ -247,7 +247,7 @@ def __write_conda_environment_file(args, filename, rendered_contents):
 
 
 def __call_conda_env_update(args, output_filename):
-    import subprocess
+    import sys
     command = [
         "conda",
         "env",
@@ -265,7 +265,23 @@ def __call_conda_env_update(args, output_filename):
     if not args.quiet:
         print("> Executing: %s" % ' '.join(command))
 
-    return subprocess.call(command)
+    old_argv = sys.argv[:]
+    try:
+        del command[0]
+        sys.argv = command
+        return _call_conda()
+    finally:
+        sys.argv = old_argv
+
+
+def _call_conda():
+    """
+    Calls conda-env directly using its internal API. ``sys.argv`` must already be configured at this point.
+
+    We have this indirection here so we can mock this function during testing.
+    """
+    from conda_env.cli.main import main
+    return main()
 
 
 def write_activate_deactivate_scripts(args, conda_yaml_dict, environment):


Test patch:
diff --git a/tests/test_main.py b/tests/test_main.py
index cc20b26..3fd56e2 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1,5 +1,6 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 
+import sys
 import textwrap
 
 import pytest
@@ -12,8 +13,7 @@ def patch_conda_calls(mocker):
     Patches all necessary functions so that we can do integration testing without actually calling
     conda.
     """
-    import subprocess
-    mocker.patch.object(subprocess, 'call', autospec=True, return_value=0)
+    mocker.patch.object(devenv, '_call_conda', autospec=True, return_value=0)
     mocker.patch.object(devenv, 'write_activate_deactivate_scripts', autospec=True)
 
 
@@ -22,11 +22,17 @@ def patch_conda_calls(mocker):
     ('environment.yml', 'environment.yml'),
 ])
 @pytest.mark.usefixtures('patch_conda_calls')
-def test_handle_input_file(tmpdir, patch_conda_calls, input_name, expected_output_name):
+def test_handle_input_file(tmpdir, input_name, expected_output_name):
     """
     Test how conda-devenv handles input files: devenv.yml and pure .yml files.
     """
-    import subprocess
+    argv = []
+    def call_conda_mock():
+        argv[:] = sys.argv[:]
+        return 0
+
+    devenv._call_conda.side_effect = call_conda_mock
+    
     filename = tmpdir.join(input_name)
     filename.write(textwrap.dedent('''\
         name: a
@@ -34,10 +40,9 @@ def test_handle_input_file(tmpdir, patch_conda_calls, input_name, expected_outpu
           - a_dependency
     '''))
     assert devenv.main(['--file', str(filename), '--quiet']) == 0
-    assert subprocess.call.call_count == 1
-    args, kwargs = subprocess.call.call_args
-    cmdline = 'conda env update --file {} --prune --quiet'.format(tmpdir.join(expected_output_name))
-    assert args == (cmdline.split(),)
+    assert devenv._call_conda.call_count == 1
+    cmdline = 'env update --file {} --prune --quiet'.format(tmpdir.join(expected_output_name))
+    assert argv == cmdline.split()
 
 
 @pytest.mark.parametrize('input_name', ['environment.devenv.yml', 'environment.yml'])