Skip to content

Commit

Permalink
Merge pull request conda#7746 from kalefranz/4.6.x-crash-fixes
Browse files Browse the repository at this point in the history
4.6.x crash fixes
  • Loading branch information
kalefranz authored Sep 7, 2018
2 parents 03e2fad + c767b22 commit 21a24f0
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 18 deletions.
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ init:

install:
- '%PYTHON_ROOT%\\Scripts\\conda install -y -c conda-canary -c defaults -c conda-forge python=%PYTHON_VERSION% pycosat conda requests ruamel_yaml pytest pytest-cov pytest-timeout mock responses pexpect pywin32'
- "%PYTHON_ROOT%\\Scripts\\conda install -yq conda-build"
- "%PYTHON_ROOT%\\Scripts\\conda install -yq conda-build=3.13"
# conda install -y -c defaults -c conda-forge pytest pytest-cov pytest-timeout mock responses pexpect xonsh
# TODO: add xonsh for PY3 builds
- "%PYTHON_ROOT%\\Scripts\\pip install codecov==2.0.5"
Expand Down
4 changes: 2 additions & 2 deletions conda.recipe/bld.bat
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
echo %PKG_VERSION% > conda\.version
python setup.py install --single-version-externally-managed --record record.txt
%PYTHON% setup.py install --single-version-externally-managed --record record.txt
if %errorlevel% neq 0 exit /b %errorlevel%

python -m conda init --install
%PYTHON% -m conda init --install
if %errorlevel% neq 0 exit /b %errorlevel%
4 changes: 2 additions & 2 deletions conda.recipe/build.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
echo $PKG_VERSION > conda/.version
python setup.py install --single-version-externally-managed --record record.txt
$PYTHON setup.py install --single-version-externally-managed --record record.txt
rm -rf "$SP_DIR/conda/shell/*.exe"
python -m conda init --install
$PYTHON -m conda init --install
5 changes: 4 additions & 1 deletion conda/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ def __init__(self, errors):
super(CondaMultiError, self).__init__(None)

def __repr__(self):
return '\n'.join(repr(e) for e in self.errors) + '\n'
return '\n'.join(text_type(e)
if isinstance(e, EnvironmentError) and not isinstance(e, CondaError)
else repr(e)
for e in self.errors) + '\n'

def __str__(self):
return '\n'.join(text_type(e) for e in self.errors) + '\n'
Expand Down
2 changes: 0 additions & 2 deletions conda/cli/main_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ def list_packages(prefix, regex=None, format='human',
result.append('='.join((prec.name, prec.version, prec.build)))
continue

stdout_json(prec)

features = set(prec.get('features') or ())
disp = '%(name)-25s %(version)-15s %(build)15s' % prec # NOQA lgtm [py/percent-format/wrong-arguments]
disp += ' %s' % disp_features(features)
Expand Down
31 changes: 27 additions & 4 deletions conda/core/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@
from .._vendor.auxlib.collection import first
from .._vendor.auxlib.ish import dals
from .._vendor.toolz import concat, concatv, interleave, take
from ..base.constants import DEFAULTS_CHANNEL_NAME, SafetyChecks
from ..base.constants import DEFAULTS_CHANNEL_NAME, SafetyChecks, PREFIX_MAGIC_FILE
from ..base.context import context
from ..common.compat import ensure_text_type, iteritems, itervalues, odict, on_win, text_type
from ..common.io import Spinner, dashlist, time_recorder
from ..common.path import (explode_directories, get_all_directories, get_major_minor_version,
get_python_site_packages_short_path)
from ..common.signals import signal_handler
from ..exceptions import (DisallowedPackageError, KnownPackageClobberError, LinkError, RemoveError,
SharedLinkPathClobberError, UnknownPackageClobberError, maybe_raise)
SharedLinkPathClobberError, UnknownPackageClobberError, maybe_raise,
EnvironmentNotWritableError)
from ..gateways.disk import mkdir_p
from ..gateways.disk.delete import rm_rf
from ..gateways.disk.read import isfile, lexists, read_package_info
Expand Down Expand Up @@ -291,7 +292,6 @@ def _prepare(cls, transaction_context, target_prefix, unlink_precs, link_precs,
transaction_context['target_site_packages_short_path'] = sp

transaction_context['temp_dir'] = join(target_prefix, '.condatmp')
mkdir_p(transaction_context['temp_dir'])

unlink_action_groups = tuple(ActionGroup(
'unlink',
Expand Down Expand Up @@ -424,6 +424,7 @@ def _verify_transaction_level(prefix_setups):
# 2. make sure we're not removing a conda dependency from conda's env
# 3. enforce context.disallowed_packages
# 4. make sure we're not removing pinned packages without no-pin flag
# 5. make sure conda-meta/history for each prefix is writable
# TODO: Verification 4

conda_prefixes = (join(context.root_prefix, 'envs', '_conda_'), context.root_prefix)
Expand Down Expand Up @@ -484,14 +485,36 @@ def _verify_transaction_level(prefix_setups):
if any(d.match(prec) for d in disallowed):
yield DisallowedPackageError(prec)

# Verification 5. make sure conda-meta/history for each prefix is writable
for prefix_setup in itervalues(prefix_setups):
test_path = join(prefix_setup.target_prefix, PREFIX_MAGIC_FILE)
test_path_existed = lexists(test_path)
dir_existed = None
try:
dir_existed = mkdir_p(dirname(test_path))
open(test_path, "a").close()
except EnvironmentError:
if dir_existed is False:
rm_rf(dirname(test_path))
yield EnvironmentNotWritableError(prefix_setup.target_prefix)
else:
if not dir_existed:
rm_rf(dirname(test_path))
elif not test_path_existed:
rm_rf(test_path)

@classmethod
def _verify(cls, prefix_setups, prefix_action_groups):
transaction_exceptions = tuple(
exc for exc in cls._verify_transaction_level(prefix_setups) if exc
)
if transaction_exceptions:
return transaction_exceptions
exceptions = tuple(exc for exc in concatv(
concat(cls._verify_individual_level(prefix_group)
for prefix_group in itervalues(prefix_action_groups)),
concat(cls._verify_prefix_level(target_prefix, prefix_group)
for target_prefix, prefix_group in iteritems(prefix_action_groups)),
cls._verify_transaction_level(prefix_setups),
) if exc)
return exceptions

Expand Down
9 changes: 6 additions & 3 deletions conda/core/path_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from ..gateways.connection.download import download
from ..gateways.disk.create import (compile_pyc, copy, create_hard_link_or_copy,
create_link, create_python_entry_point, extract_tarball,
make_menu, write_as_json_to_file)
make_menu, mkdir_p, write_as_json_to_file)
from ..gateways.disk.delete import rm_rf, try_rmdir_all_empty
from ..gateways.disk.permissions import make_writable
from ..gateways.disk.read import (compute_md5sum, compute_sha256sum, islink, lexists,
Expand Down Expand Up @@ -350,6 +350,7 @@ def verify(self):
# return
assert False, "I don't think this is the right place to ignore this"

mkdir_p(self.transaction_context['temp_dir'])
self.intermediate_path = join(self.transaction_context['temp_dir'], text_type(uuid4()))

log.trace("copying %s => %s", self.source_full_path, self.intermediate_path)
Expand Down Expand Up @@ -737,6 +738,7 @@ def __init__(self, transaction_context, package_info, target_prefix, target_shor
self.requested_link_type = requested_link_type
self.requested_spec = requested_spec
self.all_link_path_actions = all_link_path_actions
self._execute_successful = False

def execute(self):
link = Link(
Expand Down Expand Up @@ -770,11 +772,12 @@ def execute(self):

log.trace("creating linked package record %s", self.target_full_path)
PrefixData(self.target_prefix).insert(self.prefix_record)
self._execute_successful = True

def reverse(self):
log.trace("reversing linked package record creation %s", self.target_full_path)
# TODO: be careful about failure here, and being too strict
PrefixData(self.target_prefix).remove(self.package_info.repodata_record.name)
if self._execute_successful:
PrefixData(self.target_prefix).remove(self.package_info.repodata_record.name)


class UpdateHistoryAction(CreateInPrefixPathAction):
Expand Down
9 changes: 7 additions & 2 deletions conda/core/prefix_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,14 @@ def _load_site_packages(self):
# also deleting the record on disk in the conda-meta/ directory.
for conda_anchor_file in clobbered_conda_anchor_files:
prefix_rec = self._prefix_records.pop(conda_python_packages[conda_anchor_file].name)
try:
extracted_package_dir = basename(prefix_rec.extracted_package_dir)
except AttributeError:
extracted_package_dir = "-".join((
prefix_rec.name, prefix_rec.version, prefix_rec.build
))
prefix_rec_json_path = join(
self.prefix_path, "conda-meta",
'%s.json' % basename(prefix_rec.extracted_package_dir)
self.prefix_path, "conda-meta", '%s.json' % extracted_package_dir
)
try:
rm_rf(prefix_rec_json_path)
Expand Down
25 changes: 25 additions & 0 deletions conda/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,31 @@ def __init__(self, pkgs_dirs, **kwargs):
super(NoWritablePkgsDirError, self).__init__(message, pkgs_dirs=pkgs_dirs, **kwargs)


class EnvironmentNotWritableError(CondaError):

def __init__(self, environment_location, **kwargs):
kwargs.update({
'environment_location': environment_location,
})
if on_win:
message = dals("""
The current user does not have write permissions to the target environment.
environment location: %(environment_location)s
""")
else:
message = dals("""
The current user does not have write permissions to the target environment.
environment location: %(environment_location)s
uid: %(uid)s
gid: %(gid)s
""")
kwargs.update({
'uid': os.geteuid(),
'gid': os.getegid(),
})
super(EnvironmentNotWritableError, self).__init__(message, **kwargs)


class CondaDependencyError(CondaError):
def __init__(self, message):
super(CondaDependencyError, self).__init__(message)
Expand Down
11 changes: 10 additions & 1 deletion tests/test_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from glob import glob

from conda._vendor.toolz.itertoolz import groupby
from conda.gateways.disk.permissions import make_read_only
from conda.models.channel import Channel
from conda.resolve import Resolve
from itertools import chain
Expand All @@ -32,7 +33,8 @@
CONDA_PACKAGE_ROOT
from conda._vendor.auxlib.entity import EntityEncoder
from conda._vendor.auxlib.ish import dals
from conda.base.constants import CONDA_TARBALL_EXTENSION, PACKAGE_CACHE_MAGIC_FILE, SafetyChecks
from conda.base.constants import CONDA_TARBALL_EXTENSION, PACKAGE_CACHE_MAGIC_FILE, SafetyChecks, \
PREFIX_MAGIC_FILE
from conda.base.context import Context, context, reset_context
from conda.cli.conda_argparse import do_call
from conda.cli.main import generate_parser, init_loggers
Expand Down Expand Up @@ -461,6 +463,13 @@ def assert_json_parsable(content):
finally:
rmtree(prefix, ignore_errors=True)

def test_not_writable_env_raises_EnvironmentNotWritableError(self):
with make_temp_env() as prefix:
make_read_only(join(prefix, PREFIX_MAGIC_FILE))
stdout, stderr = run_command(Commands.INSTALL, prefix, "openssl", use_exception_handler=True)
assert "EnvironmentNotWritableError" in stderr
assert prefix in stderr

def test_conda_update_package_not_installed(self):
with make_temp_env() as prefix:
with pytest.raises(PackageNotInstalledError):
Expand Down

0 comments on commit 21a24f0

Please sign in to comment.