Repository: conda/conda

Issue:
test.py error "prefix is not writeable" 
While attempting to use "pyinstaller" to create a standalone conda-index executable, the resulting binary failed in “conda/gateways/disk/test.py” prefix_is_writable because that method assumes the prefix is a python root directory, and in the case of the standalone, the “prefix” is the executable’s current directory. If I add append sys.executable to the end of the “find_first_existing” call parameter list, then it worked as expected.

Of course, this particular writability check seems unnecessary for conda-index, because the only writing is to the target, not the conda-meta directory.

Any change that would allow the creation of a standalone conda-index is appreciated.


Discussion:
Adding @msarahan since we had an email conversation about this
Hey @tomashek, not opposed to adding `sys.executable` at the moment.  It's possible it won't be completely future proof though, and it might be better to understand why conda-build's index needs to call that function.  Can you attach the stack trace for the failure so we can see the code path?
Here is the stacktrace. Note that pyinstaller bundles the necessary files into a self-extracting executable that extracts to /tmp then executes the script from that location. That's why you see '/tmp/_MEI5zjz52' as the prefix.  The first stack is only the relevant conda files. I also included the full stack trace that has the pyinstaller wrappers as well:

Traceback (most recent call last):
  File "conda-index.py", line 7, in <module>
  File "site-packages/conda_build/cli/main_index.py", line 7, in <module>
  File "site-packages/conda_build/conda_interface.py", line 44, in <module>
  File "site-packages/conda/exports.py", line 75, in <module>
  File "site-packages/conda/config.py", line 70, in <module>
  File "site-packages/conda/base/context.py", line 285, in root_writable
  File "site-packages/conda/gateways/disk/test.py", line 70, in prefix_is_writable
conda.CondaError: Unable to determine if prefix '/tmp/_MEI5zjz52' is writable.
[19264] Failed to execute script conda-index

Traceback (most recent call last):
  File "conda-index.py", line 7, in <module>
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "/localdisk/work/miniconda3/lib/python3.5/site-packages/PyInstaller/loader/pyimod03_importers.py", line 631, in exec_module
    exec(bytecode, module.__dict__)
  File "site-packages/conda_build/cli/main_index.py", line 7, in <module>
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "/localdisk/work/miniconda3/lib/python3.5/site-packages/PyInstaller/loader/pyimod03_importers.py", line 631, in exec_module
    exec(bytecode, module.__dict__)
  File "site-packages/conda_build/conda_interface.py", line 44, in <module>
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "/localdisk/work/miniconda3/lib/python3.5/site-packages/PyInstaller/loader/pyimod03_importers.py", line 631, in exec_module
    exec(bytecode, module.__dict__)
  File "site-packages/conda/exports.py", line 75, in <module>
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "/localdisk/work/miniconda3/lib/python3.5/site-packages/PyInstaller/loader/pyimod03_importers.py", line 631, in exec_module
    exec(bytecode, module.__dict__)
  File "site-packages/conda/config.py", line 70, in <module>
  File "site-packages/conda/base/context.py", line 285, in root_writable
  File "site-packages/conda/gateways/disk/test.py", line 70, in prefix_is_writable
conda.CondaError: Unable to determine if prefix '/tmp/_MEI5zjz52' is writable.
[19264] Failed to execute script conda-index

@kalefranz, any other ideas on a fix? Can the writability check be avoided for this operation?

Resolving patch:
diff --git a/conda/base/context.py b/conda/base/context.py
--- a/conda/base/context.py
+++ b/conda/base/context.py
@@ -282,7 +282,13 @@ def root_dir(self):
     @property
     def root_writable(self):
         from ..gateways.disk.test import prefix_is_writable
-        return prefix_is_writable(self.root_prefix)
+        try:
+            return prefix_is_writable(self.root_prefix)
+        except CondaError:
+            # With pyinstaller, conda code can sometimes be called even though sys.prefix isn't
+            # a conda environment with a conda-meta/ directory.  In this case, it's safe to return
+            # False, because conda shouldn't itself be mutating that environment. See #6243
+            return False
 
     @property
     def envs_dirs(self):


Test patch:
diff --git a/tests/test_activate.py b/tests/test_activate.py
--- a/tests/test_activate.py
+++ b/tests/test_activate.py
@@ -191,7 +191,7 @@ def test_activate_bad_env_keeps_existing_good_env(shell):
 
 @pytest.mark.installed
 def test_activate_deactivate(shell):
-    if shell == "bash.exe" and datetime.now() < datetime(2017, 11, 1):
+    if shell == "bash.exe" and datetime.now() < datetime(2018, 1, 1):
         pytest.xfail("fix this soon")
     shell_vars = _format_vars(shell)
     with TemporaryDirectory(prefix=ENVS_PREFIX, dir=dirname(__file__)) as envs:
@@ -208,7 +208,7 @@ def test_activate_deactivate(shell):
 
 @pytest.mark.installed
 def test_activate_root_simple(shell):
-    if shell == "bash.exe" and datetime.now() < datetime(2017, 11, 1):
+    if shell == "bash.exe" and datetime.now() < datetime(2018, 1, 1):
         pytest.xfail("fix this soon")
     shell_vars = _format_vars(shell)
     with TemporaryDirectory(prefix=ENVS_PREFIX, dir=dirname(__file__)) as envs:
diff --git a/tests/test_create.py b/tests/test_create.py
--- a/tests/test_create.py
+++ b/tests/test_create.py
@@ -552,7 +552,7 @@ def test_remove_all(self):
             assert not exists(prefix)
 
     @pytest.mark.skipif(on_win, reason="nomkl not present on windows")
-    @pytest.mark.xfail(conda.__version__.startswith('4.3') and datetime.now() < datetime(2017, 11, 1), reason='currently broken in 4.3')
+    @pytest.mark.xfail(conda.__version__.startswith('4.3') and datetime.now() < datetime(2018, 1, 1), reason='currently broken in 4.3')
     def test_remove_features(self):
         with make_temp_env("python=2 numpy nomkl") as prefix:
             assert exists(join(prefix, PYTHON_BINARY))
@@ -701,7 +701,7 @@ def test_update_deps_flag_absent(self):
             assert package_is_installed(prefix, 'itsdangerous-0.23')
             assert package_is_installed(prefix, 'flask')
 
-    @pytest.mark.xfail(datetime.now() < datetime(2017, 11, 1), reason="#5263", strict=True)
+    @pytest.mark.xfail(datetime.now() < datetime(2018, 1, 1), reason="#5263", strict=True)
     def test_update_deps_flag_present(self):
         with make_temp_env("python=2 itsdangerous=0.23") as prefix:
             assert package_is_installed(prefix, 'python-2')
diff --git a/tests/test_export.py b/tests/test_export.py
--- a/tests/test_export.py
+++ b/tests/test_export.py
@@ -31,7 +31,7 @@ def test_basic(self):
             output2, error= run_command(Commands.LIST, prefix2, "-e")
             self.assertEqual(output, output2)
 
-    @pytest.mark.xfail(datetime.now() < datetime(2017, 11, 1), reason="Bring back `conda list --export` #3445", strict=True)
+    @pytest.mark.xfail(datetime.now() < datetime(2018, 1, 1), reason="Bring back `conda list --export` #3445", strict=True)
     def test_multi_channel_export(self):
         """
             When try to import from txt
diff --git a/tests/test_priority.py b/tests/test_priority.py
--- a/tests/test_priority.py
+++ b/tests/test_priority.py
@@ -15,7 +15,7 @@
 class PriorityIntegrationTests(TestCase):
 
     @pytest.mark.skipif(on_win, reason="xz packages are different on windows than unix")
-    @pytest.mark.skipif(conda.__version__.startswith('4.3') and datetime.now() < datetime(2017, 11, 1),
+    @pytest.mark.skipif(conda.__version__.startswith('4.3') and datetime.now() < datetime(2018, 1, 1),
                         reason='currently broken in 4.3')
     def test_channel_order_channel_priority_true(self):
         with make_temp_env("python=3.5 pycosat==0.6.1") as prefix: