├── MANIFEST.in ├── .gitattributes ├── .gitignore ├── conda ├── __main__.py ├── exceptions.py ├── cli │ ├── __init__.py │ ├── misc.py │ ├── main_update.py │ ├── main_help.py │ ├── help.py │ ├── main_install.py │ ├── main_create.py │ ├── pscheck.py │ ├── main_init.py │ ├── find_commands.py │ ├── main_bundle.py │ ├── main_run.py │ ├── activate.py │ ├── main_package.py │ ├── main_list.py │ ├── conda_argparse.py │ ├── main_remove.py │ ├── main.py │ └── main_info.py ├── __init__.py ├── README.txt ├── progressbar │ ├── compat.py │ └── widgets.py ├── signature.py ├── lock.py ├── toposort.py ├── pip.py ├── share.py ├── instructions.py ├── utils.py ├── packup.py ├── bundle.py ├── api.py ├── compat.py ├── console.py ├── history.py └── _version.py ├── bin └── conda ├── tests ├── __init__.py ├── test_misc.py ├── reg1 │ └── setup.sh ├── simple.plan ├── decorators.py ├── execute.py ├── mk_index.py ├── test_exceptions.py ├── test_history.py ├── test_toposort.py ├── test_info.py ├── test_import.py ├── condarc ├── test_pip.py ├── helpers.py ├── test_instructions.py └── test_utils.py ├── runtests.sh ├── sdist.sh ├── utils ├── smoketest.sh ├── smoketest.py └── win_batlink.py ├── .travis.yml ├── LICENSE.txt ├── setup.py ├── appveyor.yml └── README.rst /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include versioneer.py 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | conda/_version.py export-subst 2 | *.py diff=python 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | *.egg-info 4 | build/ 5 | dist/ 6 | docs/build 7 | .idea/ 8 | -------------------------------------------------------------------------------- /conda/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from conda.cli import main 3 | 4 | sys.exit(main()) 5 | -------------------------------------------------------------------------------- /bin/conda: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | from conda.cli import main 4 | 5 | sys.exit(main()) 6 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # This is just here so that tests is a package, so that dotted relative 2 | # imports work. 3 | -------------------------------------------------------------------------------- /runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Running tests on any file change" 3 | echo 4 | echo "Hit Ctrl+C to stop" 5 | echo 6 | watchmedo shell-command \ 7 | -c "py.test -m 'not slow' $@" \ 8 | -p "*.py" -R 9 | -------------------------------------------------------------------------------- /conda/exceptions.py: -------------------------------------------------------------------------------- 1 | class CondaException(Exception): 2 | pass 3 | 4 | 5 | class InvalidInstruction(CondaException): 6 | def __init__(self, instruction, *args, **kwargs): 7 | msg = "No handler for instruction: %r" % instruction 8 | super(InvalidInstruction, self).__init__(msg, *args, **kwargs) 9 | -------------------------------------------------------------------------------- /tests/test_misc.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from conda.fetch import cache_fn_url 4 | 5 | 6 | class TestMisc(unittest.TestCase): 7 | 8 | def test_cache_fn_url(self): 9 | url = "http://repo.continuum.io/pkgs/pro/osx-64/" 10 | self.assertEqual(cache_fn_url(url), '7618c8b6.json') 11 | 12 | 13 | if __name__ == '__main__': 14 | unittest.main() 15 | -------------------------------------------------------------------------------- /conda/cli/__init__.py: -------------------------------------------------------------------------------- 1 | # (c) 2012 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | from __future__ import absolute_import 7 | 8 | from conda.cli.main import main 9 | assert main # shut up pyflakes 10 | -------------------------------------------------------------------------------- /conda/cli/misc.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import sys 4 | 5 | import conda.config 6 | 7 | 8 | def main(): 9 | assert sys.argv[1] in ('..changeps1') 10 | 11 | if sys.argv[1] == '..changeps1': 12 | print(int(conda.config.changeps1)) 13 | sys.exit(0) 14 | 15 | 16 | if __name__ == '__main__': 17 | main() 18 | -------------------------------------------------------------------------------- /tests/reg1/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | TST_DIR=$HOME/ctest 4 | RT_BIN=$TST_DIR/anaconda/bin 5 | 6 | rm -rf $TST_DIR 7 | mkdir $TST_DIR 8 | cd $TST_DIR 9 | wget http://filer/miniconda/Miniconda-1.9.1-Linux-x86_64.sh 10 | bash Miniconda-1.9.1-Linux-x86_64.sh -b -p anaconda 11 | $RT_BIN/conda install --yes distribute 12 | 13 | cd $HOME/conda 14 | $RT_BIN/python setup.py develop 15 | $RT_BIN/conda info 16 | -------------------------------------------------------------------------------- /conda/__init__.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import absolute_import 8 | 9 | from conda._version import get_versions 10 | __version__ = get_versions()['version'] 11 | del get_versions 12 | -------------------------------------------------------------------------------- /tests/simple.plan: -------------------------------------------------------------------------------- 1 | # plan 2 | PRINT Fetching packages ... 3 | FETCH w3lib-1.2-py27_0 4 | FETCH pysam-0.6-py27_0 5 | 6 | PRINT Extracting packages ... 7 | PROGRESS 2 8 | EXTRACT w3lib-1.2-py27_0 9 | EXTRACT pysam-0.6-py27_0 10 | 11 | PRINT Linking packages ... 12 | PROGRESS 2 13 | LINK w3lib-1.2-py27_0 14 | LINK pysam-0.6-py27_0 15 | 16 | PRINT Unlinking packages ... 17 | PROGRESS 2 18 | UNLINK w3lib-1.2-py27_0 19 | UNLINK pysam-0.6-py27_0 20 | -------------------------------------------------------------------------------- /tests/decorators.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | import unittest 3 | 4 | try: 5 | from unittest import mock 6 | skip_mock = False 7 | except ImportError: 8 | try: 9 | import mock 10 | skip_mock = False 11 | except ImportError: 12 | skip_mock = True 13 | 14 | 15 | def skip_if_no_mock(func): 16 | @wraps(func) 17 | @unittest.skipIf(skip_mock, 'install mock library to test') 18 | def inner(*args, **kwargs): 19 | return func(*args, **kwargs) 20 | return inner 21 | -------------------------------------------------------------------------------- /tests/execute.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from optparse import OptionParser 3 | 4 | from conda.plan import execute_plan 5 | from conda.api import get_index 6 | 7 | 8 | def main(): 9 | p = OptionParser( 10 | usage="usage: %prog [options] FILENAME", 11 | description="execute an conda plan") 12 | 13 | p.add_option('-q', '--quiet', 14 | action="store_true") 15 | 16 | opts, args = p.parse_args() 17 | 18 | logging.basicConfig() 19 | 20 | if len(args) != 1: 21 | p.error('exactly one argument required') 22 | 23 | execute_plan(open(args[0]), get_index(), not opts.quiet) 24 | 25 | 26 | if __name__ == '__main__': 27 | main() 28 | -------------------------------------------------------------------------------- /tests/mk_index.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | 5 | from conda.api import get_index 6 | 7 | 8 | assert os.getenv('CIO_TEST') == '2' 9 | assert tuple.__itemsize__ == 8 10 | assert sys.platform.startswith('linux') 11 | 12 | index = get_index() 13 | for info in index.itervalues(): 14 | for key in 'md5', 'size', 'channel', 'build_channel', 'build_target': 15 | try: 16 | del info[key] 17 | except KeyError: 18 | pass 19 | 20 | print(len(index)) 21 | 22 | data = json.dumps(index, indent=2, sort_keys=True) 23 | data = '\n'.join(line.rstrip() for line in data.split('\n')) 24 | if not data.endswith('\n'): 25 | data += '\n' 26 | with open('index.json', 'w') as fo: 27 | fo.write(data) 28 | -------------------------------------------------------------------------------- /conda/README.txt: -------------------------------------------------------------------------------- 1 | 2 | +---------+ 3 | | cli | 4 | +---------+ 5 | | 6 | | 7 | +---------------------------+ 8 | | plan | 9 | +---------------------------+ 10 | / | | | | 11 | / | | | | 12 | resolve fetch | | progressbar 13 | / | \ | | 14 | / | \ | | 15 | verlib | utils config install 16 | | | 17 | | | 18 | | (optional) 19 | | | 20 | | | 21 | pycosat yaml 22 | -------------------------------------------------------------------------------- /tests/test_exceptions.py: -------------------------------------------------------------------------------- 1 | import random 2 | import unittest 3 | 4 | from conda import exceptions 5 | 6 | 7 | class InvalidExceptionTestCase(unittest.TestCase): 8 | def test_requires_an_instruction(self): 9 | with self.assertRaises(TypeError): 10 | exceptions.InvalidInstruction() 11 | 12 | def test_extends_from_conda_exception(self): 13 | e = exceptions.InvalidInstruction("foo") 14 | self.assertIsInstance(e, exceptions.CondaException) 15 | 16 | def test_creates_message_with_instruction_name(self): 17 | random_instruction = random.randint(100, 200) 18 | e = exceptions.InvalidInstruction(random_instruction) 19 | expected = "No handler for instruction: %s" % random_instruction 20 | self.assertEqual(expected, str(e)) 21 | -------------------------------------------------------------------------------- /sdist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Use this bash script to generate the conda source tarball which gets 4 | # uploaded to PyPI. 5 | 6 | VERSION=$(git describe --dirty) 7 | echo "VERSION: '$VERSION'" 8 | 9 | echo $VERSION | grep dirty 10 | if (( $? )); then 11 | echo "CLEAN" 12 | else 13 | echo "DIRTY" 14 | echo "Error: You must commit your changes before creating a tarball." 15 | exit 1 16 | fi 17 | 18 | rm -rf build dist docs/build conda.egg-info 19 | rm -f conda/_version.py* 20 | cat <conda/__init__.py 21 | __version__ = '$VERSION' 22 | EOF 23 | rm versioneer.py 24 | touch versioneer.py 25 | replace 'version=versioneer.get_version(),' "version='$VERSION'," setup.py 26 | replace 'cmdclass=versioneer.get_cmdclass(),' '' setup.py 27 | replace 'add_activate = True' 'add_activate = False' setup.py 28 | sdist 29 | git reset --hard 30 | -------------------------------------------------------------------------------- /tests/test_history.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from .decorators import skip_if_no_mock 4 | from .helpers import mock 5 | 6 | from conda import history 7 | 8 | class HistoryTestCase(unittest.TestCase): 9 | def test_works_as_context_manager(self): 10 | h = history.History("/path/to/prefix") 11 | self.assertTrue(getattr(h, '__enter__')) 12 | self.assertTrue(getattr(h, '__exit__')) 13 | 14 | @skip_if_no_mock 15 | def test_calls_update_on_enter_and_exit(self): 16 | h = history.History("/path/to/prefix") 17 | with mock.patch.object(h, 'update') as update: 18 | with h: 19 | self.assertEqual(1, update.call_count) 20 | pass 21 | self.assertEqual(2, update.call_count) 22 | 23 | @skip_if_no_mock 24 | def test_returns_history_object_as_context_object(self): 25 | h = history.History("/path/to/prefix") 26 | with mock.patch.object(h, 'update'): 27 | with h as h2: 28 | self.assertEqual(h, h2) 29 | -------------------------------------------------------------------------------- /conda/cli/main_update.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | from conda.cli import common, install 10 | 11 | 12 | descr = "Update conda packages to the current version." 13 | example = """ 14 | Examples: 15 | 16 | conda update -n myenv scipy 17 | 18 | """ 19 | 20 | def configure_parser(sub_parsers): 21 | p = sub_parsers.add_parser( 22 | 'update', 23 | description=descr, 24 | help=descr, 25 | epilog=example, 26 | ) 27 | common.add_parser_install(p) 28 | common.add_parser_json(p) 29 | p.add_argument( 30 | "--all", 31 | action="store_true", 32 | help="Update all installed packages in the environment.", 33 | ) 34 | p.set_defaults(func=execute) 35 | 36 | 37 | def execute(args, parser): 38 | install.install(args, parser, 'update') 39 | -------------------------------------------------------------------------------- /conda/cli/main_help.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | descr = "Displays a list of available conda commands and their help strings." 10 | 11 | example = """ 12 | Examples: 13 | 14 | conda help install 15 | """ 16 | def configure_parser(sub_parsers): 17 | p = sub_parsers.add_parser( 18 | 'help', 19 | description=descr, 20 | help=descr, 21 | epilog=example, 22 | ) 23 | p.add_argument( 24 | 'command', 25 | metavar='COMMAND', 26 | action="store", 27 | nargs='?', 28 | help="""Print help information for COMMAND (same as: conda COMMAND 29 | --help).""", 30 | ) 31 | p.set_defaults(func=execute) 32 | 33 | 34 | def execute(args, parser): 35 | if not args.command: 36 | parser.print_help() 37 | return 38 | 39 | import sys 40 | import subprocess 41 | 42 | subprocess.call([sys.executable, sys.argv[0], args.command, '-h']) 43 | -------------------------------------------------------------------------------- /utils/smoketest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | TESTLOG="conda-testlog.txt" 4 | 5 | echo "rm -rf ~/anaconda/envs/myenv" 6 | rm -rf ~/anaconda/envs/myenv 7 | 8 | if [[ -f $TESTLOG ]]; then 9 | rm $TESTLOG 10 | fi 11 | 12 | function log() 13 | { 14 | echo "$*" >> $TESTLOG 15 | } 16 | 17 | 18 | function run() 19 | { 20 | echo "-------------------------------------------------------------" 21 | echo "$*" 22 | echo "-------------------------------------------------------------" 23 | echo "" 24 | eval "$*" 25 | if [[ $? != 0 ]]; then 26 | echo "" 27 | echo "FAILED" 28 | log "$*" 29 | else 30 | echo "" 31 | echo "PASSED" 32 | fi 33 | echo "" 34 | } 35 | 36 | declare -a COND=( 37 | "conda info" 38 | "conda list ^m.*lib$" 39 | "conda search ^m.*lib$" 40 | "conda depends numpy" 41 | "conda info -e" 42 | "conda create --yes -n myenv sqlite" 43 | "conda install --yes -n myenv pandas=0.8.1" 44 | "conda update --yes -n myenv pandas" 45 | "conda env --yes -ap ~/anaconda/envs/myenv numba-0.3.1-np17py27_0" 46 | "conda env --yes -dn myenv sqlite-3.7.13-0" 47 | "conda local --yes -r zeromq-2.2.0-0" 48 | "conda local --yes -d zeromq-2.2.0-0" 49 | ) 50 | 51 | for i in "${COND[@]}"; do 52 | run $i 53 | done -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | # We don't actually use the system Python but this keeps it organized. 4 | - "2.7" 5 | - "3.3" 6 | - "3.4" 7 | env: 8 | - 9 | - PYCOSAT=0.6.0 10 | install: 11 | # TODO: Use a "latest" url 12 | - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then 13 | wget http://repo.continuum.io/miniconda/Miniconda-3.5.2-Linux-x86_64.sh -O miniconda.sh; 14 | elif [[ "$TRAVIS_PYTHON_VERSION" == "3.3" ]]; then 15 | wget http://repo.continuum.io/miniconda/Miniconda3-3.0.0-Linux-x86_64.sh -O miniconda.sh; 16 | else 17 | wget http://repo.continuum.io/miniconda/Miniconda3-3.5.2-Linux-x86_64.sh -O miniconda.sh; 18 | fi 19 | - bash miniconda.sh -b -p $HOME/miniconda 20 | - export PATH="$HOME/miniconda/bin:$PATH" 21 | - hash -r 22 | - conda config --set always_yes yes 23 | - bin/conda install --force --no-deps conda requests 24 | - conda --version 25 | - conda install -c conda pytest requests mock conda-env pycrypto 26 | - if [[ "$PYCOSAT" ]]; then 27 | conda install pycosat=$PYCOSAT; 28 | fi 29 | - python setup.py install 30 | - conda info -a 31 | 32 | script: 33 | - py.test 34 | - conda --help 35 | 36 | notifications: 37 | flowdock: ef3821a08a791106512ccfc04c92eccb 38 | 39 | addons: 40 | apt: 41 | packages: 42 | - zsh 43 | 44 | sudo: false 45 | -------------------------------------------------------------------------------- /conda/cli/help.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os.path import join 3 | 4 | import conda.config as config 5 | from conda.cli.common import name_prefix, error_and_exit 6 | 7 | 8 | 9 | def read_message(fn): 10 | res = [] 11 | for envs_dir in config.envs_dirs: 12 | path = join(envs_dir, '.conda-help', fn) 13 | try: 14 | with open(path) as fi: 15 | s = fi.read().decode('utf-8') 16 | s = s.replace('${envs_dir}', envs_dir) 17 | res.append(s) 18 | except IOError: 19 | pass 20 | return ''.join(res) 21 | 22 | 23 | def root_read_only(command, prefix, json=False): 24 | assert command in {'install', 'update', 'remove'} 25 | 26 | msg = read_message('ro.txt') 27 | if not msg: 28 | msg = """\ 29 | Missing write permissions in: ${root_dir} 30 | # 31 | # You don't appear to have the necessary permissions to ${command} packages 32 | # into the install area '${root_dir}'. 33 | # However you can clone this environment into your home directory and 34 | # then make changes to it. 35 | # This may be done using the command: 36 | # 37 | # $ conda create -n my_${name} --clone=${prefix} 38 | """ 39 | msg = msg.replace('${root_dir}', config.root_dir) 40 | msg = msg.replace('${prefix}', prefix) 41 | msg = msg.replace('${name}', name_prefix(prefix)) 42 | msg = msg.replace('${command}', command) 43 | error_and_exit(msg, json=json, error_type='RootNotWritable') 44 | -------------------------------------------------------------------------------- /conda/cli/main_install.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | from conda.cli import common, install 10 | 11 | 12 | help = "Install a list of packages into a specified conda environment." 13 | descr = help + """ 14 | The arguments may be packages specifications (e.g. bitarray=0.8), 15 | or explicit conda packages filenames (e.g. ./lxml-3.2.0-py27_0.tar.bz2) which 16 | must exist on the local filesystem. The two types of arguments cannot be 17 | mixed and the latter implies the --force and --no-deps options. 18 | """ 19 | example = """ 20 | Examples: 21 | 22 | conda install -n myenv scipy 23 | 24 | """ 25 | 26 | def configure_parser(sub_parsers): 27 | p = sub_parsers.add_parser( 28 | 'install', 29 | description=descr, 30 | help=help, 31 | epilog=example, 32 | ) 33 | p.add_argument( 34 | "--revision", 35 | action="store", 36 | help="Revert to the specified REVISION.", 37 | metavar='REVISION', 38 | ) 39 | common.add_parser_install(p) 40 | common.add_parser_json(p) 41 | p.set_defaults(func=execute) 42 | 43 | 44 | def execute(args, parser): 45 | install.install(args, parser, 'install') 46 | -------------------------------------------------------------------------------- /tests/test_toposort.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from conda.toposort import toposort, pop_key 3 | 4 | class TopoSortTests(unittest.TestCase): 5 | 6 | def test_pop_key(self): 7 | key = pop_key({'a':{'b', 'c'}, 'b':{'c'}}) 8 | self.assertEqual(key, 'b') 9 | 10 | key = pop_key({'a':{'b'}, 'b':{'c', 'a'}}) 11 | self.assertEqual(key, 'a') 12 | 13 | key = pop_key({'a':{'b'}, 'b':{'a'}}) 14 | self.assertEqual(key, 'a') 15 | 16 | def test_simple(self): 17 | data = {'a':'bc', 'b':'c'} 18 | results = toposort(data, safe=True) 19 | self.assertEqual(results, ['c', 'b', 'a']) 20 | results = toposort(data, safe=False) 21 | self.assertEqual(results, ['c', 'b', 'a']) 22 | 23 | def test_cycle(self): 24 | data = {'a':'b', 'b':'a'} 25 | 26 | with self.assertRaises(ValueError): 27 | toposort(data, False) 28 | 29 | results = toposort(data) 30 | # Results do not have an guaranteed order 31 | self.assertEqual(set(results), {'b', 'a'}) 32 | 33 | def test_cycle_best_effort(self): 34 | data = {'a':'bc', 'b':'c', '1':'2', '2':'1'} 35 | 36 | results = toposort(data) 37 | self.assertEqual(results[:3], ['c', 'b', 'a']) 38 | 39 | # Cycles come last 40 | # Results do not have an guaranteed order 41 | self.assertEqual(set(results[3:]), {'1', '2'}) 42 | 43 | 44 | if __name__ == '__main__': 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /tests/test_info.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import, division 2 | 3 | from conda import config 4 | 5 | from tests.helpers import run_conda_command 6 | 7 | 8 | def test_info(): 9 | conda_info_out, conda_info_err = run_conda_command('info') 10 | assert conda_info_err == '' 11 | for name in ['platform', 'conda version', 'root environment', 12 | 'default environment', 'envs directories', 'package cache', 13 | 'channel URLs', 'config file', 'is foreign system']: 14 | assert name in conda_info_out 15 | 16 | conda_info_e_out, conda_info_e_err = run_conda_command('info', '-e') 17 | assert 'root' in conda_info_e_out 18 | assert conda_info_e_err == '' 19 | 20 | conda_info_s_out, conda_info_s_err = run_conda_command('info', '-s') 21 | assert conda_info_s_err == '' 22 | for name in ['sys.version', 'sys.prefix', 'sys.executable', 'conda location', 23 | 'conda-build', 'CIO_TEST', 'CONDA_DEFAULT_ENV', 'PATH', 'PYTHONPATH']: 24 | assert name in conda_info_s_out 25 | if config.platform == 'linux': 26 | assert 'LD_LIBRARY_PATH' in conda_info_s_out 27 | if config.platform == 'osx': 28 | assert 'DYLD_LIBRARY_PATH' in conda_info_s_out 29 | 30 | conda_info_all_out, conda_info_all_err = run_conda_command('info', '--all') 31 | assert conda_info_all_err == '' 32 | assert conda_info_out in conda_info_all_out 33 | assert conda_info_e_out in conda_info_all_out 34 | assert conda_info_s_out in conda_info_all_out 35 | -------------------------------------------------------------------------------- /conda/cli/main_create.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | from conda.cli import common, install 10 | 11 | 12 | help = "Create a new conda environment from a list of specified packages. " 13 | descr = (help + 14 | "To use the created environment, use 'source activate " 15 | "envname' look in that directory first. This command requires either " 16 | "the -n NAME or -p PREFIX option.") 17 | 18 | example = """ 19 | Examples: 20 | 21 | conda create -n myenv sqlite 22 | 23 | """ 24 | 25 | def configure_parser(sub_parsers): 26 | p = sub_parsers.add_parser( 27 | 'create', 28 | description=descr, 29 | help=help, 30 | epilog=example, 31 | ) 32 | common.add_parser_install(p) 33 | common.add_parser_json(p) 34 | p.add_argument( 35 | "--clone", 36 | action="store", 37 | help='Path to (or name of) existing local environment.', 38 | metavar='ENV', 39 | ) 40 | p.add_argument( 41 | "--no-default-packages", 42 | action="store_true", 43 | help='Ignore create_default_packages in the .condarc file.', 44 | ) 45 | p.set_defaults(func=execute) 46 | 47 | 48 | def execute(args, parser): 49 | install.install(args, parser, 'create') 50 | -------------------------------------------------------------------------------- /conda/progressbar/compat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # progressbar - Text progress bar library for Python. 5 | # Copyright (c) 2005 Nilton Volpato 6 | # 7 | # This library is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU Lesser General Public 9 | # License as published by the Free Software Foundation; either 10 | # version 2.1 of the License, or (at your option) any later version. 11 | # 12 | # This library is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public 18 | # License along with this library; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | '''Compatability methods and classes for the progressbar module''' 22 | 23 | 24 | # Python 3.x (and backports) use a modified iterator syntax 25 | # This will allow 2.x to behave with 3.x iterators 26 | if not hasattr(__builtins__, 'next'): 27 | def next(iter): 28 | try: 29 | # Try new style iterators 30 | return iter.__next__() 31 | except AttributeError: 32 | # Fallback in case of a "native" iterator 33 | return iter.next() 34 | 35 | 36 | # Python < 2.5 does not have "any" 37 | if not hasattr(__builtins__, 'any'): 38 | def any(iterator): 39 | for item in iterator: 40 | if item: return True 41 | 42 | return False 43 | -------------------------------------------------------------------------------- /tests/test_import.py: -------------------------------------------------------------------------------- 1 | """ Test if we can import everything from conda. 2 | This basically tests syntax correctness and whether the internal imports work. 3 | Created to test py3k compatibility. 4 | """ 5 | 6 | from __future__ import print_function, division, absolute_import 7 | 8 | import os 9 | import sys 10 | import unittest 11 | import conda 12 | 13 | PREFIX = os.path.dirname(os.path.abspath(conda.__file__)) 14 | 15 | 16 | class TestImportAllConda(unittest.TestCase): 17 | 18 | def _test_import(self, subpackage): 19 | # Prepare 20 | prefix = PREFIX 21 | module_prefix = 'conda' 22 | if subpackage: 23 | prefix = os.path.join(prefix, subpackage) 24 | module_prefix = '%s.%s' % (module_prefix, subpackage) 25 | 26 | # Try importing root 27 | __import__(module_prefix) 28 | 29 | # Import each module in given (sub)package 30 | for fname in os.listdir(prefix): 31 | # Discard files that are not of interest 32 | if fname.startswith('__'): 33 | continue 34 | elif not fname.endswith('.py'): 35 | continue 36 | elif fname.startswith('windows') and sys.platform != 'win32': 37 | continue 38 | # Import 39 | modname = module_prefix + '.' + fname.split('.')[0] 40 | print('importing', modname) 41 | __import__(modname) 42 | 43 | 44 | def test_import_root(self): 45 | self._test_import('') 46 | 47 | def test_import_cli(self): 48 | self._test_import('cli') 49 | 50 | def test_import_progressbar(self): 51 | self._test_import('progressbar') 52 | 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | -------------------------------------------------------------------------------- /tests/condarc: -------------------------------------------------------------------------------- 1 | # This is a sample .condarc file 2 | 3 | # channel locations. These override conda defaults, i.e., conda will 4 | # search *only* the channels listed here, in the order given. Use "defaults" to 5 | # automatically include all default channels. Non-url channels will be 6 | # interpreted as binstar usernames (this can be changed by modifying the 7 | # channel_alias key; see below). 8 | channels: 9 | - binstar_username 10 | - http://some.custom/channel 11 | - defaults 12 | 13 | # Alias to use for non-url channels used with the -c flag. Default is https://conda.binstar.org/ 14 | 15 | channel_alias: https://your.repo/ 16 | 17 | # Proxy settings: http://[username]:[password]@[server]:[port] 18 | proxy_servers: 19 | http: http://user:pass@corp.com:8080 20 | https: https://user:pass@corp.com:8080 21 | 22 | # directory in which conda root is located (used by `conda init`) 23 | root_dir: ~/.local/conda_root 24 | 25 | # directories in which environments are located 26 | envs_dirs: 27 | - ~/my-envs 28 | - /opt/anaconda/envs 29 | 30 | # implies always using the --yes option whenever asked to proceed 31 | always_yes: True 32 | 33 | # disallow soft-linking (default is allow_softlinks: True, 34 | # i.e. soft-link when possible) 35 | allow_softlinks: False 36 | 37 | # change ps1 when using activate (default True) 38 | changeps1: False 39 | 40 | # use pip when installing and listing packages (default True) 41 | use_pip: False 42 | 43 | # binstar.org upload (not defined here means ask) 44 | binstar_upload: True 45 | 46 | # when creating new environments add these packages by default 47 | create_default_packages: 48 | - python 49 | - pip 50 | 51 | # disallowed specification names 52 | disallow: 53 | - anaconda 54 | 55 | # enable certain features to be tracked by default 56 | track_features: 57 | - mkl 58 | -------------------------------------------------------------------------------- /conda/signature.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import base64 3 | from os.path import abspath, expanduser, isfile, join 4 | 5 | try: 6 | from Crypto.Hash import SHA256 7 | from Crypto.PublicKey import RSA 8 | from Crypto.Signature import PKCS1_PSS 9 | except ImportError: 10 | sys.exit("""\ 11 | Error: could not import Crypto (required for signature verification). 12 | Run the following command: 13 | 14 | $ conda install -n root pycrypto 15 | """) 16 | 17 | KEYS = {} 18 | KEYS_DIR = abspath(expanduser('~/.conda/keys')) 19 | 20 | 21 | def hash_file(path): 22 | h = SHA256.new() 23 | with open(path, 'rb') as fi: 24 | while True: 25 | chunk = fi.read(262144) # process chunks of 256KB 26 | if not chunk: 27 | break 28 | h.update(chunk) 29 | return h 30 | 31 | 32 | class SignatureError(Exception): 33 | pass 34 | 35 | 36 | def verify(path): 37 | """ 38 | Verify the file `path`, with signature `path`.sig, against the key 39 | found under ~/.conda/keys/.pub. This function returns: 40 | - True, if the signature is valid 41 | - False, if the signature is invalid 42 | It raises SignatureError when the signature file, or the public key 43 | does not exist. 44 | """ 45 | sig_path = path + '.sig' 46 | if not isfile(sig_path): 47 | raise SignatureError("signature does not exist: %s" % sig_path) 48 | with open(sig_path) as fi: 49 | key_name, sig = fi.read().split() 50 | if key_name not in KEYS: 51 | key_path = join(KEYS_DIR, '%s.pub' % key_name) 52 | if not isfile(key_path): 53 | raise SignatureError("public key does not exist: %s" % key_path) 54 | KEYS[key_name] = RSA.importKey(open(key_path).read()) 55 | key = KEYS[key_name] 56 | verifier = PKCS1_PSS.new(key) 57 | return verifier.verify(hash_file(path), base64.b64decode(sig)) 58 | -------------------------------------------------------------------------------- /tests/test_pip.py: -------------------------------------------------------------------------------- 1 | import random 2 | import unittest 3 | 4 | from .helpers import mock 5 | from .decorators import skip_if_no_mock 6 | 7 | from conda import pip 8 | 9 | 10 | def generate_random_version(): 11 | return '%s.%s' % (random.randint(1, 5), random.randint(0, 20)) 12 | 13 | 14 | class PipPackageTestCase(unittest.TestCase): 15 | def test_acts_like_dict(self): 16 | p = pip.PipPackage(name='foo', version='0.1') 17 | self.assertIsInstance(p, dict) 18 | 19 | def test_simple_string_as_spec(self): 20 | version = generate_random_version() 21 | p = pip.PipPackage(name='bar', version=version) 22 | expected = 'bar-{version}-'.format(version=version) 23 | self.assertEqual(expected, str(p)) 24 | 25 | def test_handles_case_where_path_provided(self): 26 | version = generate_random_version() 27 | path = '/some/path/%s/foo' % random.randint(0, 10) 28 | p = pip.PipPackage(name='baz', path=path, version=version) 29 | 30 | expected = 'baz ({path})-{version}-'.format(path=path, 31 | version=version) 32 | self.assertEqual(expected, str(p)) 33 | 34 | 35 | class installed_test(unittest.TestCase): 36 | @skip_if_no_mock 37 | # TODO brittle test -- shows code that needs refactoring 38 | def test_stops_on_exception(self): 39 | with mock.patch.object(pip, 'subprocess') as subprocess: 40 | subprocess.check_output = mock.Mock(side_effect=[Exception(), ]) 41 | with mock.patch.object(pip, 'pip_args') as pip_args: 42 | # make sure that installed doesn't bail early 43 | pip_args.return_value = ['pip', 'list'] 44 | 45 | for pkg in pip.installed('/some/prefix'): 46 | self.fail('should never get here') 47 | 48 | self.assertEqual(1, subprocess.check_output.call_count) 49 | -------------------------------------------------------------------------------- /conda/cli/pscheck.py: -------------------------------------------------------------------------------- 1 | # NOTE: 2 | # This module is deprecated, and will be removed in the future. 3 | # pscheck is not imported anywhere in conda. 4 | 5 | from __future__ import print_function, division, absolute_import 6 | 7 | import os 8 | import sys 9 | from os.path import abspath 10 | 11 | from conda.cli import conda_argparse 12 | from conda.config import root_dir 13 | from conda.cli.common import confirm, add_parser_yes 14 | 15 | 16 | try: 17 | WindowsError 18 | except NameError: 19 | class WindowsError(Exception): 20 | pass 21 | 22 | 23 | def check_processes(dir=root_dir, verbose=True): 24 | # Conda should still work if psutil is not installed (it should not be a 25 | # hard dependency) 26 | try: 27 | import psutil 28 | except ImportError: 29 | return True 30 | 31 | if psutil.__version__ < '2.': 32 | # we now require psutil 2.0 or above 33 | return True 34 | 35 | ok = True 36 | curpid = os.getpid() 37 | for n in psutil.get_pid_list(): 38 | if n == curpid: # The Python that conda is running is OK 39 | continue 40 | try: 41 | p = psutil.Process(n) 42 | except psutil.NoSuchProcess: 43 | continue 44 | try: 45 | if abspath(p.exe()).startswith(dir): 46 | processcmd = ' '.join(p.cmdline()) 47 | if 'conda' in processcmd: 48 | continue 49 | if verbose: 50 | print("WARNING: the process %s (%d) is running" % 51 | (processcmd, n)) 52 | ok = False 53 | except (psutil.AccessDenied, WindowsError): 54 | pass 55 | if not ok and verbose: 56 | print("""\ 57 | WARNING: Continuing installation while the above processes are running is 58 | not recommended. Please, close all Anaconda programs before installing or 59 | updating things with conda. 60 | """) 61 | return ok 62 | 63 | 64 | def main(args, windowsonly=True): 65 | # Returns True for force, otherwise None 66 | if sys.platform == 'win32' or not windowsonly: 67 | if args.yes: 68 | check_processes() 69 | else: 70 | while not check_processes(): 71 | choice = confirm(args, message="Continue (yes/no/force)", 72 | choices=('yes', 'no', 'force'), default='no') 73 | if choice == 'no': 74 | sys.exit(1) 75 | if choice == 'force': 76 | return True 77 | 78 | 79 | if __name__ == '__main__': 80 | p = conda_argparse.ArgumentParser() 81 | add_parser_yes(p) 82 | args = p.parse_args() 83 | main(args, windowsonly=False) 84 | -------------------------------------------------------------------------------- /conda/cli/main_init.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | import sys 10 | from os.path import isdir, join 11 | 12 | import conda 13 | import conda.config as config 14 | 15 | descr = """ 16 | Initialize conda into a regular environment (when conda was installed as a 17 | Python package, e.g. using pip). (DEPRECATED) 18 | """ 19 | 20 | warning = """ 21 | WARNING: conda init is deprecated. The recommended way to manage pip installed 22 | conda is to use pip to manage the root environment and conda to manage new 23 | conda environments. 24 | 25 | Note that pip installing conda is not the recommended way for setting up your 26 | system. The recommended way for setting up a conda system is by installing 27 | Miniconda. See http://conda.pydata.org/miniconda.html.""" 28 | 29 | 30 | def configure_parser(sub_parsers): 31 | p = sub_parsers.add_parser( 32 | 'init', 33 | description=descr, 34 | epilog=warning, 35 | help=descr, 36 | ) 37 | p.set_defaults(func=execute) 38 | 39 | 40 | def is_initialized(): 41 | return isdir(join(config.root_dir, 'conda-meta')) 42 | 43 | 44 | def write_meta(meta_dir, info): 45 | import json 46 | 47 | info['files'] = [] 48 | with open(join(meta_dir, 49 | '%(name)s-%(version)s-0.json' % info), 'w') as fo: 50 | json.dump(info, fo, indent=2, sort_keys=True) 51 | 52 | 53 | def initialize(prefix=config.root_dir): 54 | import os 55 | 56 | meta_dir = join(prefix, 'conda-meta') 57 | try: 58 | os.mkdir(meta_dir) 59 | except OSError: 60 | sys.exit('Error: could not create: %s' % meta_dir) 61 | with open(join(meta_dir, 'foreign'), 'w') as fo: 62 | fo.write('python\n') 63 | if sys.platform != 'win32': 64 | fo.write('zlib sqlite readline tk openssl system\n') 65 | write_meta(meta_dir, dict(name='conda', 66 | version=conda.__version__.split('-')[0], build_number=0)) 67 | write_meta(meta_dir, dict(name='python', version=sys.version[:5], 68 | build_number=0, build="0")) 69 | with open(join(meta_dir, "pinned"), 'w') as f: 70 | f.write("python %s 0" % sys.version[:5]) 71 | 72 | 73 | def execute(args, parser): 74 | if is_initialized(): 75 | sys.exit('Error: conda appears to be already initalized in: %s' % 76 | config.root_dir) 77 | 78 | print(warning, file=sys.stderr) 79 | 80 | print('Initializing conda into: %s' % config.root_dir) 81 | initialize() 82 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Except where noted below, conda is released under the following terms: 2 | 3 | (c) 2012 Continuum Analytics, Inc. / http://continuum.io 4 | All Rights Reserved 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of Continuum Analytics, Inc. nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL CONTINUUM ANALYTICS BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | 29 | Exceptions 30 | ========== 31 | 32 | versioneer.py is Public Domain 33 | 34 | verlib.py is Public Domain 35 | 36 | The ProgressBar package is released under the following terms: 37 | 38 | # progressbar - Text progress bar library for Python. 39 | # Copyright (c) 2005 Nilton Volpato 40 | # 41 | # This library is free software; you can redistribute it and/or 42 | # modify it under the terms of the GNU Lesser General Public 43 | # License as published by the Free Software Foundation; either 44 | # version 2.1 of the License, or (at your option) any later version. 45 | # 46 | # This library is distributed in the hope that it will be useful, 47 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 48 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 49 | # Lesser General Public License for more details. 50 | # 51 | # You should have received a copy of the GNU Lesser General Public 52 | # License along with this library; if not, write to the Free Software 53 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 54 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # (c) 2012-2014 Continuum Analytics, Inc. / http://continuum.io 3 | # All Rights Reserved 4 | # 5 | # conda is distributed under the terms of the BSD 3-clause license. 6 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 7 | 8 | import sys 9 | import os 10 | 11 | if 'develop' in sys.argv: 12 | from setuptools import setup 13 | using_setuptools = True 14 | print("Using setuptools") 15 | else: 16 | from distutils.core import setup 17 | using_setuptools = False 18 | print("Not using setuptools") 19 | 20 | 21 | import versioneer 22 | 23 | 24 | if sys.version_info[:2] < (2, 7) or sys.version_info > (3, 0) and sys.version_info < (3, 3): 25 | sys.exit("conda is only meant for Python 2.7 or 3.3 and up. current version: %d.%d" % sys.version_info[:2]) 26 | 27 | try: 28 | if os.environ['CONDA_DEFAULT_ENV']: 29 | # Try to prevent accidentally installing conda into a non-root conda environment 30 | sys.exit("You appear to be in a non-root conda environment. Conda is only " 31 | "supported in the root environment. Deactivate and try again. If you believe " 32 | "this message is in error, run CONDA_DEFAULT_ENV='' python setup.py.") 33 | except KeyError: 34 | pass 35 | 36 | versioneer.versionfile_source = 'conda/_version.py' 37 | versioneer.versionfile_build = 'conda/_version.py' 38 | versioneer.tag_prefix = '' # tags are like 1.2.0 39 | versioneer.parentdir_prefix = 'conda-' # dirname like 'myproject-1.2.0' 40 | 41 | kwds = {'scripts': []} 42 | if sys.platform == 'win32' and using_setuptools: 43 | kwds['entry_points'] = dict(console_scripts = 44 | ["conda = conda.cli.main:main"]) 45 | else: 46 | kwds['scripts'].append('bin/conda') 47 | 48 | 49 | setup( 50 | name = "conda", 51 | version=versioneer.get_version(), 52 | cmdclass=versioneer.get_cmdclass(), 53 | author = "Continuum Analytics, Inc.", 54 | author_email = "ilan@continuum.io", 55 | url = "https://github.com/conda/conda", 56 | license = "BSD", 57 | classifiers = [ 58 | "Development Status :: 4 - Beta", 59 | "Intended Audience :: Developers", 60 | "Operating System :: OS Independent", 61 | "Programming Language :: Python :: 2", 62 | "Programming Language :: Python :: 2.7", 63 | "Programming Language :: Python :: 3", 64 | "Programming Language :: Python :: 3.3", 65 | "Programming Language :: Python :: 3.4", 66 | ], 67 | description = "package management tool", 68 | long_description = open('README.rst').read(), 69 | packages = ['conda', 'conda.cli', 'conda.progressbar'], 70 | install_requires = ['pycosat', 'pyyaml', 'requests'], 71 | **kwds 72 | ) 73 | -------------------------------------------------------------------------------- /conda/lock.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | """ 8 | Tools for working with locks 9 | 10 | A lock is just an empty directory. We use directories because this lets us use 11 | the race condition-proof os.makedirs. 12 | 13 | For now, there is one global lock for all of conda, because some things happen 14 | globally (such as downloading packages). 15 | 16 | We don't raise an error if the lock is named with the current PID 17 | """ 18 | 19 | import os 20 | import logging 21 | from os.path import join 22 | import glob 23 | from time import sleep 24 | 25 | LOCKFN = '.conda_lock' 26 | 27 | 28 | stdoutlog = logging.getLogger('stdoutlog') 29 | 30 | 31 | class Locked(object): 32 | """ 33 | Context manager to handle locks. 34 | """ 35 | def __init__(self, path): 36 | self.path = path 37 | self.end = "-" + str(os.getpid()) 38 | self.lock_path = join(self.path, LOCKFN + self.end) 39 | self.pattern = join(self.path, LOCKFN + '-*') 40 | self.remove = True 41 | 42 | def __enter__(self): 43 | retries = 10 44 | # Keep the string "LOCKERROR" in this string so that external 45 | # programs can look for it. 46 | lockstr = ("""\ 47 | LOCKERROR: It looks like conda is already doing something. 48 | The lock %s was found. Wait for it to finish before continuing. 49 | If you are sure that conda is not running, remove it and try again. 50 | You can also use: $ conda clean --lock\n""" % self.lock_path) 51 | sleeptime = 1 52 | while retries: 53 | files = glob.glob(self.pattern) 54 | if files and not files[0].endswith(self.end): 55 | stdoutlog.info(lockstr) 56 | stdoutlog.info("Sleeping for %s seconds\n" % sleeptime) 57 | sleep(sleeptime) 58 | sleeptime *= 2 59 | retries -= 1 60 | else: 61 | break 62 | else: 63 | stdoutlog.error("Exceeded max retries, giving up") 64 | raise RuntimeError(lockstr) 65 | 66 | if not files: 67 | try: 68 | os.makedirs(self.lock_path) 69 | except OSError: 70 | pass 71 | else: # PID lock already here --- someone else will remove it. 72 | self.remove = False 73 | 74 | def __exit__(self, exc_type, exc_value, traceback): 75 | if self.remove: 76 | for path in self.lock_path, self.path: 77 | try: 78 | os.rmdir(path) 79 | except OSError: 80 | pass 81 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Tell appveyor to not use msbuild 2 | build: false 3 | 4 | environment: 5 | matrix: 6 | - PYTHON: 2.7 7 | PYCOSAT: 0.6.0 8 | - PYTHON: 2.7 9 | PYCOSAT: 10 | - PYTHON: 3.4 11 | PYCOSAT: 0.6.0 12 | - PYTHON: 3.4 13 | PYCOSAT: 14 | 15 | platform: 16 | - x86 17 | - x64 18 | 19 | init: 20 | - "ECHO %PYTHON%" 21 | - "ECHO %PYCOSAT%" 22 | - ps: if($env:PYTHON -eq "2.7" -and $env:Platform -eq "x64" -and $env:PYCOSAT -ne "0.6.0"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda-3.5.5-Windows-x86_64.exe' C:\Miniconda.exe; echo "Done"} 23 | - ps: if($env:PYTHON -eq "2.7" -and $env:Platform -eq "x64" -and $env:PYCOSAT -eq "0.6.0"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda-3.4.2-Windows-x86_64.exe' C:\Miniconda.exe; echo "Done"} 24 | - ps: if($env:PYTHON -eq "2.7" -and $env:Platform -eq "x86" -and $env:PYCOSAT -ne "0.6.0"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda-3.5.5-Windows-x86.exe' C:\Miniconda.exe; echo "Done"} 25 | - ps: if($env:PYTHON -eq "2.7" -and $env:Platform -eq "x86" -and $env:PYCOSAT -eq "0.6.0"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda-3.4.2-Windows-x86.exe' C:\Miniconda.exe; echo "Done"} 26 | - ps: if($env:PYTHON -eq "3.4" -and $env:Platform -eq "x64" -and $env:PYCOSAT -ne "0.6.0"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda3-3.5.5-Windows-x86_64.exe' C:\Miniconda.exe; echo "Done"} 27 | - ps: if($env:PYTHON -eq "3.4" -and $env:Platform -eq "x64" -and $env:PYCOSAT -eq "0.6.0"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda3-3.4.2-Windows-x86_64.exe' C:\Miniconda.exe; echo "Done"} 28 | - ps: if($env:PYTHON -eq "3.4" -and $env:Platform -eq "x86" -and $env:PYCOSAT -ne "0.6.0"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda3-3.5.5-Windows-x86.exe' C:\Miniconda.exe; echo "Done"} 29 | - ps: if($env:PYTHON -eq "3.4" -and $env:Platform -eq "x86" -and $env:PYCOSAT -eq "0.6.0"){Start-FileDownload 'http://repo.continuum.io/miniconda/Miniconda3-3.4.2-Windows-x86.exe' C:\Miniconda.exe; echo "Done"} 30 | - cmd: C:\Miniconda.exe /S /D=C:\Miniconda 31 | - ps: ls C:\Miniconda/Scripts 32 | - C:\Miniconda\Scripts\conda config --set always_yes yes 33 | # We need to do this first as other commands may not work with older versions of conda. 34 | - C:\Miniconda\Scripts\conda update conda 35 | - C:\Miniconda\Scripts\conda install -c conda pytest requests pycrypto mock conda-env --quiet 36 | - cmd: if "%PYTHON%"=="2.7" C:\Miniconda\Scripts\conda install ndg-httpsclient 37 | 38 | install: 39 | - C:\Miniconda\python.exe setup.py install 40 | - C:\Miniconda\Scripts\conda info -a 41 | - C:\Miniconda\Scripts\conda list 42 | 43 | test_script: 44 | - C:\Miniconda\Scripts\py.test -vvv 45 | - C:\Miniconda\Scripts\conda --help 46 | - IF %PYTHON%==2.7 IF DEFINED PYCOSAT C:\Miniconda\Scripts\conda install -f menuinst ipython-notebook & echo "Finished menuinst test" 47 | -------------------------------------------------------------------------------- /conda/cli/find_commands.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import re 4 | import os 5 | import sys 6 | import subprocess 7 | from os.path import isdir, isfile, join, expanduser 8 | 9 | from conda.utils import memoized 10 | 11 | def find_executable(executable, include_others=True): 12 | # backwards compatibility 13 | global dir_paths 14 | 15 | if include_others: 16 | if sys.platform == 'win32': 17 | dir_paths = [join(sys.prefix, 'Scripts'), 18 | 'C:\\cygwin\\bin'] 19 | else: 20 | dir_paths = [join(sys.prefix, 'bin')] 21 | else: 22 | dir_paths = [] 23 | 24 | dir_paths.extend(os.environ['PATH'].split(os.pathsep)) 25 | 26 | for dir_path in dir_paths: 27 | if sys.platform == 'win32': 28 | for ext in '.exe', '.bat', '': 29 | path = join(dir_path, executable + ext) 30 | if isfile(path): 31 | return path 32 | else: 33 | path = join(dir_path, executable) 34 | if isfile(expanduser(path)): 35 | return expanduser(path) 36 | return None 37 | 38 | @memoized 39 | def find_commands(include_others=True): 40 | if include_others: 41 | if sys.platform == 'win32': 42 | dir_paths = [join(sys.prefix, 'Scripts'), 43 | 'C:\\cygwin\\bin'] 44 | else: 45 | dir_paths = [join(sys.prefix, 'bin')] 46 | else: 47 | dir_paths = [] 48 | 49 | if sys.platform == 'win32': 50 | pat = re.compile(r'conda-(\w+)\.(exe|bat)$') 51 | else: 52 | pat = re.compile(r'conda-(\w+)$') 53 | 54 | res = set() 55 | for dir_path in dir_paths: 56 | if not isdir(dir_path): 57 | continue 58 | for fn in os.listdir(dir_path): 59 | if not isfile(join(dir_path, fn)): 60 | continue 61 | m = pat.match(fn) 62 | if m: 63 | res.add(m.group(1)) 64 | return sorted(res) 65 | 66 | 67 | def filter_descr(cmd): 68 | args = [find_executable('conda-' + cmd), '--help'] 69 | if not args[0]: 70 | print('failed: %s (could not find executable)' % (cmd)) 71 | return 72 | try: 73 | output = subprocess.check_output(args) 74 | except (OSError, subprocess.CalledProcessError): 75 | print('failed: %s' % (' '.join(args))) 76 | return 77 | pat = re.compile(r'(\r?\n){2}(.*?)(\r?\n){2}', re.DOTALL) 78 | m = pat.search(output.decode('utf-8')) 79 | descr = [''] if m is None else m.group(2).split('\n') 80 | # XXX: using some stuff from textwrap would be better here, as it gets 81 | # longer than 80 characters 82 | print(' %-12s %s' % (cmd, descr[0])) 83 | for d in descr[1:]: 84 | print(' %s' % d) 85 | 86 | 87 | def help(): 88 | print("\nexternal commands:") 89 | for cmd in find_commands(): 90 | filter_descr(cmd) 91 | 92 | 93 | if __name__ == '__main__': 94 | help() 95 | -------------------------------------------------------------------------------- /conda/toposort.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | from functools import reduce as _reduce 4 | import logging 5 | 6 | log = logging.getLogger(__name__) 7 | 8 | def _toposort(data): 9 | """Dependencies are expressed as a dictionary whose keys are items 10 | and whose values are a set of dependent items. Output is a list of 11 | sets in topological order. The first set consists of items with no 12 | dependences, each subsequent set consists of items that depend upon 13 | items in the preceding sets. 14 | """ 15 | 16 | # Special case empty input. 17 | if len(data) == 0: 18 | return 19 | 20 | # Ignore self dependencies. 21 | for k, v in data.items(): 22 | v.discard(k) 23 | # Find all items that don't depend on anything. 24 | extra_items_in_deps = _reduce(set.union, data.values()) - set(data.keys()) 25 | # Add empty dependences where needed. 26 | data.update({item:set() for item in extra_items_in_deps}) 27 | while True: 28 | 29 | ordered = sorted(set(item for item, dep in data.items() if len(dep) == 0)) 30 | if not ordered: 31 | break 32 | 33 | for item in ordered: 34 | yield item 35 | data.pop(item, None) 36 | 37 | for dep in sorted(data.values()): 38 | dep -= set(ordered) 39 | # data = {item: (dep - ordered) 40 | # for item, dep in data.items() 41 | # if item not in ordered} 42 | 43 | if len(data) != 0: 44 | msg = 'Cyclic dependencies exist among these items: {}' 45 | raise ValueError(msg.format(' -> '.join(repr(x) for x in data.keys()))) 46 | 47 | def pop_key(data): 48 | ''' 49 | Pop an item from the graph that has the fewest dependencies in the case of a tie 50 | The winners will be sorted alphabetically 51 | ''' 52 | items = sorted(data.items(), key=lambda item: (len(item[1]), item[0])) 53 | key = items[0][0] 54 | 55 | data.pop(key) 56 | 57 | for dep in data.values(): 58 | dep.discard(key) 59 | 60 | return key 61 | 62 | def _safe_toposort(data): 63 | """Dependencies are expressed as a dictionary whose keys are items 64 | and whose values are a set of dependent items. Output is a list of 65 | sets in topological order. The first set consists of items with no 66 | dependencies, each subsequent set consists of items that depend upon 67 | items in the preceding sets. 68 | """ 69 | 70 | # Special case empty input. 71 | if len(data) == 0: 72 | return 73 | 74 | t = _toposort(data) 75 | 76 | while True: 77 | try: 78 | value = next(t) 79 | yield value 80 | except ValueError as err: 81 | log.debug(err.args[0]) 82 | 83 | if not data: 84 | return 85 | 86 | yield pop_key(data) 87 | 88 | t = _toposort(data) 89 | 90 | continue 91 | except StopIteration: 92 | return 93 | 94 | 95 | def toposort(data, safe=True): 96 | 97 | data = {k:set(v) for k, v in data.items()} 98 | 99 | if safe: 100 | return list(_safe_toposort(data)) 101 | else: 102 | return list(_toposort(data)) 103 | -------------------------------------------------------------------------------- /utils/smoketest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess as sp 3 | import sys 4 | import time 5 | 6 | from os.path import exists, join 7 | from shutil import rmtree 8 | from tempfile import mkdtemp 9 | 10 | base = mkdtemp() 11 | myenv = join(base, "env") 12 | 13 | if 'win' in sys.platform and 'dar' not in sys.platform: 14 | status = True 15 | pandas = numba = '0.10.1' 16 | cython = '0.18' 17 | else: 18 | status = False 19 | pandas = '0.8.1' 20 | numba = '0.1.1' 21 | cython = '0.16' 22 | 23 | 24 | # CIO_TEST needs to be set to 2 if any of the packages tested below are only found in the test repo. 25 | 26 | # os.environ['CIO_TEST'] = 2 27 | 28 | cmds = [ 29 | "info", 30 | "list ^m.*lib$", 31 | "search ^m.*lib$", 32 | "search -v numpy", 33 | "search -c numpy", 34 | "info -e", 35 | "create --yes -p %s sqlite python=2.6" % myenv, 36 | "install --yes -p %s pandas=%s" % (myenv, pandas), 37 | "remove --yes -p %s pandas" % myenv, 38 | "install --yes -p %s numba=%s" % (myenv, numba), 39 | "install --yes -p %s cython=%s" % (myenv, cython), 40 | "remove --yes -p %s cython" % myenv, 41 | "install --yes -p %s accelerate" % myenv, 42 | "remove --yes -p %s accelerate" % myenv, 43 | "install --yes -p %s mkl" % myenv, 44 | "remove --yes -p %s mkl" % myenv, 45 | "update --yes -p %s numba" % myenv, 46 | "remove --yes -p %s numba" % myenv, 47 | "install --yes -p %s iopro" % myenv, 48 | "remove --yes -p %s iopro" % myenv, 49 | "info -e", 50 | "info -a", 51 | "info --license", 52 | "info -s", 53 | ] 54 | 55 | def tester(commands): 56 | cmds = commands 57 | errs = [] 58 | fails = [] 59 | for cmd in cmds: 60 | cmd = "conda %s" % cmd 61 | print("-"*len(cmd)) 62 | print("%s" % cmd) 63 | print("-"*len(cmd)) 64 | try: 65 | child = sp.Popen(cmd.split(), stdout=sp.PIPE, stderr=sp.PIPE, shell=status) 66 | data, err = child.communicate() 67 | ret = child.returncode 68 | if ret != 0: 69 | print("\nFAILED\n") 70 | errs.append("\n%s\n \n%s" % (cmd, err)) 71 | fails.append(cmd) 72 | else: 73 | print("\nPASSED\n") 74 | except Exception as e: 75 | print(e) 76 | errs.append("\nThe script had the following error running %s: %s" % (cmd, e)) 77 | 78 | return (fails, errs) 79 | 80 | 81 | if __name__ == '__main__': 82 | 83 | TESTLOG = 'conda-testlog.txt' 84 | 85 | options = True if len(sys.argv) > 1 else False 86 | 87 | if options and 'new' in sys.argv: 88 | if exists(TESTLOG): 89 | os.remove(TESTLOG) 90 | 91 | fails, errs = tester(cmds) 92 | if fails: 93 | print("These commands failed: \n") 94 | for line, fail in enumerate(fails, 1): 95 | print("%d: %s\n" % (line, fail)) 96 | header = 'Test Results For %s' % time.asctime() 97 | if options and 'log' in sys.argv: 98 | print("Writing failed commands to conda-testlog.txt") 99 | with open(TESTLOG, "a") as f: 100 | f.write('%s\n%s\n' % (header, '-'*len(header))) 101 | for error in errs: 102 | f.write(error) 103 | 104 | try: 105 | rmtree(myenv) 106 | except: 107 | pass 108 | -------------------------------------------------------------------------------- /tests/helpers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helpers for the tests 3 | """ 4 | import subprocess 5 | import sys 6 | import os 7 | import json 8 | 9 | try: 10 | from unittest import mock 11 | except ImportError: 12 | try: 13 | import mock 14 | except ImportError: 15 | mock = None 16 | 17 | from contextlib import contextmanager 18 | 19 | import conda.cli as cli 20 | from conda.compat import StringIO 21 | 22 | def raises(exception, func, string=None): 23 | try: 24 | a = func() 25 | except exception as e: 26 | if string: 27 | assert string in e.args[0] 28 | print(e) 29 | return True 30 | raise Exception("did not raise, gave %s" % a) 31 | 32 | def run_in(command, shell='bash'): 33 | p = subprocess.Popen([shell, '-c', command], stdout=subprocess.PIPE, 34 | stderr=subprocess.PIPE) 35 | stdout, stderr = p.communicate() 36 | return (stdout.decode('utf-8').replace('\r\n', '\n'), 37 | stderr.decode('utf-8').replace('\r\n', '\n')) 38 | 39 | python = sys.executable 40 | conda = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'bin', 'conda') 41 | 42 | def run_conda_command(*args): 43 | env = os.environ.copy() 44 | # Make sure bin/conda imports *this* conda. 45 | env['PYTHONPATH'] = os.path.dirname(os.path.dirname(__file__)) 46 | env['CONDARC'] = ' ' 47 | p= subprocess.Popen((python, conda,) + args, stdout=subprocess.PIPE, 48 | stderr=subprocess.PIPE, env=env) 49 | stdout, stderr = p.communicate() 50 | return (stdout.decode('utf-8').replace('\r\n', '\n'), 51 | stderr.decode('utf-8').replace('\r\n', '\n')) 52 | 53 | class CapturedText(object): 54 | pass 55 | 56 | @contextmanager 57 | def captured(disallow_stderr=True): 58 | """ 59 | Context manager to capture the printed output of the code in the with block 60 | 61 | Bind the context manager to a variable using `as` and the result will be 62 | in the stdout property. 63 | 64 | >>> from tests.helpers import capture 65 | >>> with captured() as c: 66 | ... print('hello world!') 67 | ... 68 | >>> c.stdout 69 | 'hello world!\n' 70 | """ 71 | import sys 72 | 73 | stdout = sys.stdout 74 | stderr = sys.stderr 75 | sys.stdout = outfile = StringIO() 76 | sys.stderr = errfile = StringIO() 77 | c = CapturedText() 78 | yield c 79 | c.stdout = outfile.getvalue() 80 | c.stderr = errfile.getvalue() 81 | sys.stdout = stdout 82 | sys.stderr = stderr 83 | if disallow_stderr and c.stderr: 84 | raise Exception("Got stderr output: %s" % c.stderr) 85 | 86 | 87 | def capture_with_argv(*argv): 88 | sys.argv = argv 89 | stdout, stderr = StringIO(), StringIO() 90 | oldstdout, oldstderr = sys.stdout, sys.stderr 91 | sys.stdout = stdout 92 | sys.stderr = stderr 93 | try: 94 | cli.main() 95 | except SystemExit: 96 | pass 97 | sys.stdout = oldstdout 98 | sys.stderr = oldstderr 99 | 100 | stdout.seek(0) 101 | stderr.seek(0) 102 | return stdout.read(), stderr.read() 103 | 104 | 105 | def capture_json_with_argv(*argv): 106 | stdout, stderr = capture_with_argv(*argv) 107 | if stderr: 108 | # TODO should be exception 109 | return stderr 110 | 111 | try: 112 | return json.loads(stdout) 113 | except ValueError: 114 | print(stdout, stderr) 115 | raise 116 | -------------------------------------------------------------------------------- /tests/test_instructions.py: -------------------------------------------------------------------------------- 1 | from logging import getLogger, Handler, DEBUG 2 | import unittest 3 | 4 | 5 | from conda import exceptions 6 | from conda import instructions 7 | from conda.instructions import execute_instructions, commands, PROGRESS_CMD 8 | 9 | 10 | def test_expected_operation_order(): 11 | """Ensure expected order of operations""" 12 | expected = ( 13 | instructions.FETCH, 14 | instructions.EXTRACT, 15 | instructions.UNLINK, 16 | instructions.LINK, 17 | instructions.SYMLINK_CONDA, 18 | instructions.RM_EXTRACTED, 19 | instructions.RM_FETCHED, 20 | ) 21 | assert expected == instructions.action_codes 22 | 23 | 24 | class TestHandler(Handler): 25 | def __init__(self): 26 | Handler.__init__(self) 27 | self.setLevel(DEBUG) 28 | self.records = [] 29 | 30 | def handle(self, record): 31 | self.records.append((record.name, record.msg)) 32 | 33 | 34 | 35 | class TestExecutePlan(unittest.TestCase): 36 | 37 | def test_invalid_instruction(self): 38 | index = {'This is an index': True} 39 | 40 | plan = [('DOES_NOT_EXIST', ())] 41 | 42 | with self.assertRaises(exceptions.InvalidInstruction): 43 | execute_instructions(plan, index, verbose=False) 44 | 45 | def test_simple_instruction(self): 46 | 47 | index = {'This is an index': True} 48 | 49 | def simple_cmd(state, arg): 50 | simple_cmd.called = True 51 | simple_cmd.call_args = (arg,) 52 | 53 | commands['SIMPLE'] = simple_cmd 54 | 55 | plan = [('SIMPLE', ('arg1',))] 56 | 57 | execute_instructions(plan, index, verbose=False) 58 | 59 | self.assertTrue(simple_cmd.called) 60 | self.assertTrue(simple_cmd.call_args, ('arg1',)) 61 | 62 | def test_state(self): 63 | 64 | index = {'This is an index': True} 65 | 66 | def simple_cmd(state, arg): 67 | expect, x = arg 68 | state.setdefault('x', 1) 69 | self.assertEqual(state['x'], expect) 70 | state['x'] = x 71 | simple_cmd.called = True 72 | 73 | commands['SIMPLE'] = simple_cmd 74 | 75 | plan = [('SIMPLE', (1, 5)), 76 | ('SIMPLE', (5, None)), 77 | ] 78 | 79 | execute_instructions(plan, index, verbose=False) 80 | self.assertTrue(simple_cmd.called) 81 | 82 | def test_progess(self): 83 | 84 | index = {'This is an index': True} 85 | 86 | plan = [ 87 | ('PROGRESS', '2'), 88 | ('LINK', 'ipython'), 89 | ('LINK', 'menuinst'), 90 | ] 91 | 92 | def cmd(state, arg): 93 | pass # NO-OP 94 | 95 | _commands = {'PROGRESS': PROGRESS_CMD, 'LINK': cmd} 96 | h = TestHandler() 97 | 98 | update_logger = getLogger('progress.update') 99 | update_logger.setLevel(DEBUG) 100 | update_logger.addHandler(h) 101 | 102 | stop_logger = getLogger('progress.stop') 103 | stop_logger.setLevel(DEBUG) 104 | stop_logger.addHandler(h) 105 | 106 | execute_instructions(plan, index, _commands=_commands) 107 | 108 | update_logger.removeHandler(h) 109 | stop_logger.removeHandler(h) 110 | 111 | expected = [('progress.update', ('ipython', 0)), 112 | ('progress.update', ('menuinst', 1)), 113 | ('progress.stop', None) 114 | ] 115 | 116 | self.assertEqual(h.records, expected) 117 | 118 | if __name__ == '__main__': 119 | unittest.main() 120 | -------------------------------------------------------------------------------- /conda/pip.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions related to core conda functionality that relates to pip 3 | """ 4 | from __future__ import absolute_import, print_function 5 | from os.path import isfile, join 6 | import re 7 | import subprocess 8 | import sys 9 | 10 | 11 | def pip_args(prefix): 12 | """ 13 | return the arguments required to invoke pip (in prefix), or None if pip 14 | is not installed 15 | """ 16 | if sys.platform == 'win32': 17 | pip_path = join(prefix, 'Scripts', 'pip-script.py') 18 | py_path = join(prefix, 'python.exe') 19 | else: 20 | pip_path = join(prefix, 'bin', 'pip') 21 | py_path = join(prefix, 'bin', 'python') 22 | if isfile(pip_path) and isfile(py_path): 23 | ret = [py_path, pip_path] 24 | 25 | # Check the version of pip 26 | # --disable-pip-version-check was introduced in pip 6.0 27 | # If older than that, they should probably get the warning anyway. 28 | pip_version = subprocess.check_output(ret + ['-V']).decode('utf-8').split()[1] 29 | major_ver = pip_version.split('.')[0] 30 | if int(major_ver) >= 6: 31 | ret.append('--disable-pip-version-check') 32 | return ret 33 | else: 34 | return None 35 | 36 | 37 | class PipPackage(dict): 38 | def __str__(self): 39 | if 'path' in self: 40 | return '%s (%s)-%s-' % ( 41 | self['name'], 42 | self['path'], 43 | self['version'] 44 | ) 45 | return '%s-%s-' % (self['name'], self['version']) 46 | 47 | 48 | def installed(prefix, output=True): 49 | args = pip_args(prefix) 50 | if args is None: 51 | return 52 | args.append('list') 53 | try: 54 | pipinst = subprocess.check_output( 55 | args, universal_newlines=True 56 | ).split('\n') 57 | except Exception: 58 | # Any error should just be ignored 59 | if output: 60 | print("# Warning: subprocess call to pip failed") 61 | return 62 | 63 | # For every package in pipinst that is not already represented 64 | # in installed append a fake name to installed with 'pip' 65 | # as the build string 66 | pat = re.compile('([\w.-]+)\s+\((.+)\)') 67 | for line in pipinst: 68 | line = line.strip() 69 | if not line: 70 | continue 71 | m = pat.match(line) 72 | if m is None: 73 | if output: 74 | print('Could not extract name and version from: %r' % line) 75 | continue 76 | name, version = m.groups() 77 | name = name.lower() 78 | kwargs = { 79 | 'name': name, 80 | 'version': version, 81 | } 82 | if ', ' in version: 83 | # Packages installed with setup.py develop will include a path in 84 | # the version. They should be included here, even if they are 85 | # installed with conda, as they are preferred over the conda 86 | # version. We still include the conda version, though, because it 87 | # is still installed. 88 | 89 | version, path = version.split(', ') 90 | # We do this because the code below uses rsplit('-', 2) 91 | version = version.replace('-', ' ') 92 | kwargs.update({ 93 | 'path': path, 94 | 'version': version, 95 | }) 96 | yield PipPackage(**kwargs) 97 | 98 | 99 | def add_pip_installed(prefix, installed_pkgs, json=None, output=True): 100 | # Defer to json for backwards compatibility 101 | if type(json) is bool: 102 | output = not json 103 | 104 | # TODO Refactor so installed is a real list of objects/dicts 105 | # instead of strings allowing for direct comparison 106 | conda_names = {d.rsplit('-', 2)[0] for d in installed_pkgs} 107 | 108 | for pip_pkg in installed(prefix, output=output): 109 | if pip_pkg['name'] in conda_names and not 'path' in pip_pkg: 110 | continue 111 | installed_pkgs.add(str(pip_pkg)) 112 | -------------------------------------------------------------------------------- /conda/cli/main_bundle.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | from conda.cli import common 4 | 5 | descr = 'Create or extract a "bundle package" (EXPERIMENTAL)' 6 | 7 | 8 | def configure_parser(sub_parsers): 9 | p = sub_parsers.add_parser( 10 | 'bundle', 11 | description = descr, 12 | help = descr, 13 | ) 14 | cxgroup=p.add_mutually_exclusive_group() 15 | cxgroup.add_argument('-c', "--create", 16 | action="store_true", 17 | help="Create bundle.") 18 | cxgroup.add_argument('-x', "--extract", 19 | action="store", 20 | help="Extact bundle located at PATH.", 21 | metavar="PATH") 22 | cxgroup.add_argument("--metadump", 23 | action="store", 24 | help="Dump metadata of bundle at PATH.", 25 | metavar="PATH") 26 | 27 | common.add_parser_prefix(p) 28 | common.add_parser_quiet(p) 29 | p.add_argument("--bundle-name", 30 | action="store", 31 | help="Name of bundle.", 32 | metavar='NAME', 33 | ) 34 | p.add_argument("--data-path", 35 | action="store", 36 | help="Path to data to be included in bundle.", 37 | metavar="PATH" 38 | ) 39 | p.add_argument("--extra-meta", 40 | action="store", 41 | help="Path to json file with additional meta-data.", 42 | metavar="PATH", 43 | ) 44 | p.add_argument("--no-env", 45 | action="store_true", 46 | help="No environment.", 47 | ) 48 | common.add_parser_json(p) 49 | p.set_defaults(func=execute) 50 | 51 | 52 | def execute(args, parser): 53 | import sys 54 | import json 55 | 56 | import conda.bundle as bundle 57 | from conda.fetch import TmpDownload 58 | 59 | 60 | if not (args.create or args.extract or args.metadump): 61 | sys.exit("""Error: 62 | either one of the following options is required: 63 | -c/--create -x/--extract --metadump 64 | (see -h for more details)""") 65 | prefix = common.get_prefix(args) 66 | if args.no_env: 67 | prefix = None 68 | 69 | if args.create: 70 | if args.extra_meta: 71 | with open(args.extra_meta) as fi: 72 | extra = json.load(fi) 73 | if not isinstance(extra, dict): 74 | sys.exit('Error: no dictionary in: %s' % args.extra_meta) 75 | else: 76 | extra = None 77 | 78 | bundle.warn = [] 79 | out_path = bundle.create_bundle(prefix, args.data_path, 80 | args.bundle_name, extra) 81 | if args.json: 82 | d = dict(path=out_path, warnings=bundle.warn) 83 | json.dump(d, sys.stdout, indent=2, sort_keys=True) 84 | else: 85 | print(out_path) 86 | 87 | 88 | if args.extract: 89 | if args.data_path or args.extra_meta: 90 | sys.exit("""\ 91 | Error: -x/--extract does not allow --data-path or --extra-meta""") 92 | 93 | with TmpDownload(args.extract, verbose=not args.quiet) as path: 94 | bundle.clone_bundle(path, prefix, args.bundle_name) 95 | 96 | 97 | if args.metadump: 98 | import tarfile 99 | 100 | with TmpDownload(args.metadump, verbose=not args.quiet) as path: 101 | try: 102 | t = tarfile.open(path, 'r:*') 103 | f = t.extractfile('info/index.json') 104 | sys.stdout.write(f.read()) 105 | sys.stdout.write('\n') 106 | except IOError: 107 | sys.exit("Error: no such file: %s" % path) 108 | except tarfile.ReadError: 109 | sys.exit("Error: bad tar archive: %s" % path) 110 | except KeyError: 111 | sys.exit("Error: no archive '%s' in: %s" % (bundle.BMJ, path)) 112 | t.close() 113 | -------------------------------------------------------------------------------- /conda/share.py: -------------------------------------------------------------------------------- 1 | # NOTE: 2 | # This module is deprecated. Don't import from this here when writing 3 | # new code. 4 | from __future__ import print_function, division, absolute_import 5 | 6 | import os 7 | import re 8 | import json 9 | import hashlib 10 | import tempfile 11 | import shutil 12 | from os.path import abspath, basename, isdir, join 13 | 14 | import conda.config as config 15 | from conda.api import get_index 16 | from conda.misc import untracked 17 | from conda.resolve import MatchSpec 18 | import conda.install as install 19 | import conda.plan as plan 20 | import conda.instructions as inst 21 | 22 | from conda.packup import create_conda_pkg 23 | 24 | 25 | def get_requires(prefix): 26 | res = [] 27 | for dist in install.linked(prefix): 28 | meta = install.is_linked(prefix, dist) 29 | assert meta 30 | if 'file_hash' not in meta: 31 | res.append('%(name)s %(version)s %(build)s' % meta) 32 | res.sort() 33 | return res 34 | 35 | def update_info(info): 36 | h = hashlib.new('sha1') 37 | for spec in info['depends']: 38 | assert MatchSpec(spec).strictness == 3 39 | h.update(spec.encode('utf-8')) 40 | h.update(b'\x00') 41 | h.update(info['file_hash'].encode('utf-8')) 42 | info['version'] = h.hexdigest() 43 | 44 | def old_create_bundle(prefix): 45 | """ 46 | Create a "bundle package" of the environment located in `prefix`, 47 | and return the full path to the created package. This file is 48 | created in a temp directory, and it is the callers responsibility 49 | to remove this directory (after the file has been handled in some way). 50 | 51 | This bundle is a regular meta-package which lists (in its requirements) 52 | all Anaconda packages installed (not packages the user created manually), 53 | and all files in the prefix which are not installed from Anaconda 54 | packages. When putting this packages into a conda repository, 55 | it can be used to created a new environment using the conda create 56 | command. 57 | """ 58 | info = dict( 59 | name='share', 60 | build='0', 61 | build_number=0, 62 | platform=config.platform, 63 | arch=config.arch_name, 64 | depends=get_requires(prefix), 65 | ) 66 | tmp_dir = tempfile.mkdtemp() 67 | tmp_path = join(tmp_dir, 'share.tar.bz2') 68 | warnings = create_conda_pkg(prefix, 69 | untracked(prefix, exclude_self_build=True), 70 | info, tmp_path, update_info) 71 | 72 | path = join(tmp_dir, '%(name)s-%(version)s-%(build)s.tar.bz2' % info) 73 | os.rename(tmp_path, path) 74 | return path, warnings 75 | 76 | 77 | def old_clone_bundle(path, prefix): 78 | """ 79 | Clone the bundle (located at `path`) by creating a new environment at 80 | `prefix`. 81 | The directory `path` is located in should be some temp directory or 82 | some other directory OUTSITE /opt/anaconda (this function handles 83 | copying the of the file if necessary for you). After calling this 84 | funtion, the original file (at `path`) may be removed. 85 | """ 86 | assert not abspath(path).startswith(abspath(config.root_dir)) 87 | assert not isdir(prefix) 88 | fn = basename(path) 89 | assert re.match(r'share-[0-9a-f]{40}-\d+\.tar\.bz2$', fn), fn 90 | dist = fn[:-8] 91 | 92 | pkgs_dir = config.pkgs_dirs[0] 93 | if not install.is_extracted(pkgs_dir, dist): 94 | shutil.copyfile(path, join(pkgs_dir, dist + '.tar.bz2')) 95 | inst.execute_instructions([(inst.EXTRACT, (dist,))]) 96 | assert install.is_extracted(pkgs_dir, dist) 97 | 98 | with open(join(pkgs_dir, dist, 'info', 'index.json')) as fi: 99 | meta = json.load(fi) 100 | 101 | # for backwards compatibility, use "requires" when "depends" is not there 102 | dists = ['-'.join(r.split()) 103 | for r in meta.get('depends', meta.get('requires', [])) 104 | if not r.startswith('conda ')] 105 | dists.append(dist) 106 | 107 | actions = plan.ensure_linked_actions(dists, prefix) 108 | index = get_index() 109 | plan.execute_actions(actions, index, verbose=False) 110 | 111 | os.unlink(join(prefix, 'conda-meta', dist + '.json')) 112 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | import random 2 | import unittest 3 | 4 | from .decorators import skip_if_no_mock 5 | from .helpers import mock 6 | 7 | from conda import utils 8 | 9 | SOME_PREFIX = "/some/prefix" 10 | SOME_FILES = ["a", "b", "c"] 11 | 12 | 13 | def create_mock_open(): 14 | return mock.patch.object(utils, "open", create=True) 15 | 16 | 17 | def create_mock_can_open(): 18 | can_open = mock.patch.object(utils, "can_open") 19 | can_open.return_value = True 20 | return can_open 21 | 22 | 23 | class can_open_TestCase(unittest.TestCase): 24 | @skip_if_no_mock 25 | def test_returns_true_if_can_open(self): 26 | with create_mock_open(): 27 | self.assertTrue(utils.can_open("/some/path/some/file")) 28 | 29 | @skip_if_no_mock 30 | def test_returns_false_if_unable_to_open(self): 31 | with create_mock_open() as o: 32 | o.side_effect = IOError 33 | self.assertFalse(utils.can_open("/some/path/some/file")) 34 | 35 | @skip_if_no_mock 36 | def test_logs_file_to_debug_log(self): 37 | random_file = "/some/path/to/a/file/%s" % random.randint(100, 200) 38 | with create_mock_open() as o: 39 | o.side_effect = IOError 40 | with mock.patch.object(utils, "stderrlog") as log: 41 | utils.can_open(random_file) 42 | log.info.assert_called_with("Unable to open %s\n" % random_file) 43 | 44 | @skip_if_no_mock 45 | def test_closes_file_handler_if_successful(self): 46 | with create_mock_open() as o: 47 | utils.can_open("/some/file") 48 | o.assert_has_calls([ 49 | mock.call("/some/file", "ab"), 50 | mock.call().close(), 51 | ]) 52 | 53 | 54 | class can_open_all_TestCase(unittest.TestCase): 55 | @skip_if_no_mock 56 | def test_returns_true_if_all_files_are_openable(self): 57 | with create_mock_can_open(): 58 | self.assertTrue(utils.can_open_all([ 59 | "/some/path/a", 60 | "/some/path/b", 61 | ])) 62 | 63 | @skip_if_no_mock 64 | def test_returns_false_if_not_all_files_are_opened(self): 65 | with create_mock_can_open() as can_open: 66 | can_open.return_value = False 67 | self.assertFalse(utils.can_open_all([ 68 | "/some/path/a", 69 | "/some/path/b", 70 | ])) 71 | 72 | @skip_if_no_mock 73 | def test_only_call_can_open_as_many_times_as_needed(self): 74 | with create_mock_can_open() as can_open: 75 | can_open.side_effect = [True, False, True] 76 | self.assertFalse(utils.can_open_all([ 77 | "/can/open", 78 | "/cannot/open", 79 | "/can/open", 80 | ])) 81 | self.assertEqual(can_open.call_count, 2) 82 | 83 | 84 | class can_open_all_files_in_prefix_TestCase(unittest.TestCase): 85 | @skip_if_no_mock 86 | def test_returns_true_on_success(self): 87 | with create_mock_open(): 88 | self.assertTrue(utils.can_open_all_files_in_prefix(SOME_PREFIX, SOME_FILES)) 89 | 90 | @skip_if_no_mock 91 | def test_returns_false_if_unable_to_open_file_for_writing(self): 92 | with create_mock_open() as o: 93 | o.side_effect = IOError 94 | self.assertFalse(utils.can_open_all_files_in_prefix(SOME_PREFIX, SOME_FILES)) 95 | 96 | @skip_if_no_mock 97 | def test_dispatches_to_can_can_call(self): 98 | with mock.patch.object(utils, "can_open_all") as can_open_all: 99 | utils.can_open_all_files_in_prefix(SOME_PREFIX, SOME_FILES) 100 | self.assertTrue(can_open_all.called) 101 | 102 | @skip_if_no_mock 103 | def test_tries_to_open_all_files(self): 104 | random_files = ['%s' % i for i in range(random.randint(10, 20))] 105 | with create_mock_can_open(): 106 | utils.can_open_all_files_in_prefix(SOME_PREFIX, random_files) 107 | 108 | @skip_if_no_mock 109 | def test_stops_checking_as_soon_as_the_first_file_fails(self): 110 | with create_mock_can_open() as can_open: 111 | can_open.side_effect = [True, False, True] 112 | self.assertFalse( 113 | utils.can_open_all_files_in_prefix(SOME_PREFIX, SOME_FILES) 114 | ) 115 | self.assertEqual(can_open.call_count, 2) 116 | -------------------------------------------------------------------------------- /conda/instructions.py: -------------------------------------------------------------------------------- 1 | from logging import getLogger 2 | import re 3 | 4 | from conda import config 5 | from conda import install 6 | from conda.exceptions import InvalidInstruction 7 | from conda.fetch import fetch_pkg 8 | 9 | 10 | log = getLogger(__name__) 11 | 12 | # op codes 13 | FETCH = 'FETCH' 14 | EXTRACT = 'EXTRACT' 15 | UNLINK = 'UNLINK' 16 | LINK = 'LINK' 17 | RM_EXTRACTED = 'RM_EXTRACTED' 18 | RM_FETCHED = 'RM_FETCHED' 19 | PREFIX = 'PREFIX' 20 | PRINT = 'PRINT' 21 | PROGRESS = 'PROGRESS' 22 | SYMLINK_CONDA = 'SYMLINK_CONDA' 23 | 24 | 25 | progress_cmds = set([EXTRACT, RM_EXTRACTED, LINK, UNLINK]) 26 | action_codes = ( 27 | FETCH, 28 | EXTRACT, 29 | UNLINK, 30 | LINK, 31 | SYMLINK_CONDA, 32 | RM_EXTRACTED, 33 | RM_FETCHED, 34 | ) 35 | 36 | 37 | def PREFIX_CMD(state, arg): 38 | state['prefix'] = arg 39 | 40 | 41 | def PRINT_CMD(state, arg): 42 | getLogger('print').info(arg) 43 | 44 | 45 | def fetch(index, dist): 46 | assert index is not None 47 | fn = dist + '.tar.bz2' 48 | fetch_pkg(index[fn]) 49 | 50 | 51 | def FETCH_CMD(state, arg): 52 | fetch(state['index'], arg) 53 | 54 | 55 | def PROGRESS_CMD(state, arg): 56 | state['i'] = 0 57 | state['maxval'] = int(arg) 58 | getLogger('progress.start').info(state['maxval']) 59 | 60 | 61 | def EXTRACT_CMD(state, arg): 62 | install.extract(config.pkgs_dirs[0], arg) 63 | 64 | 65 | def RM_EXTRACTED_CMD(state, arg): 66 | install.rm_extracted(config.pkgs_dirs[0], arg) 67 | 68 | 69 | def RM_FETCHED_CMD(state, arg): 70 | install.rm_fetched(config.pkgs_dirs[0], arg) 71 | 72 | 73 | def split_linkarg(arg): 74 | "Return tuple(dist, pkgs_dir, linktype)" 75 | pat = re.compile(r'\s*(\S+)(?:\s+(.+?)\s+(\d+))?\s*$') 76 | m = pat.match(arg) 77 | dist, pkgs_dir, linktype = m.groups() 78 | if pkgs_dir is None: 79 | pkgs_dir = config.pkgs_dirs[0] 80 | if linktype is None: 81 | linktype = install.LINK_HARD 82 | return dist, pkgs_dir, int(linktype) 83 | 84 | 85 | def link(prefix, arg, index=None): 86 | dist, pkgs_dir, lt = split_linkarg(arg) 87 | install.link(pkgs_dir, prefix, dist, lt, index=index) 88 | 89 | 90 | def LINK_CMD(state, arg): 91 | link(state['prefix'], arg, index=state['index']) 92 | 93 | 94 | def UNLINK_CMD(state, arg): 95 | install.unlink(state['prefix'], arg) 96 | 97 | 98 | def SYMLINK_CONDA_CMD(state, arg): 99 | install.symlink_conda(state['prefix'], arg) 100 | 101 | # Map instruction to command (a python function) 102 | commands = { 103 | PREFIX: PREFIX_CMD, 104 | PRINT: PRINT_CMD, 105 | FETCH: FETCH_CMD, 106 | PROGRESS: PROGRESS_CMD, 107 | EXTRACT: EXTRACT_CMD, 108 | RM_EXTRACTED: RM_EXTRACTED_CMD, 109 | RM_FETCHED: RM_FETCHED_CMD, 110 | LINK: LINK_CMD, 111 | UNLINK: UNLINK_CMD, 112 | SYMLINK_CONDA: SYMLINK_CONDA_CMD, 113 | } 114 | 115 | 116 | def execute_instructions(plan, index=None, verbose=False, _commands=None): 117 | """ 118 | Execute the instructions in the plan 119 | 120 | :param plan: A list of (instruction, arg) tuples 121 | :param index: The meta-data index 122 | :param verbose: verbose output 123 | :param _commands: (For testing only) dict mapping an instruction to executable if None 124 | then the default commands will be used 125 | """ 126 | if _commands is None: 127 | _commands = commands 128 | 129 | if verbose: 130 | from conda.console import setup_verbose_handlers 131 | setup_verbose_handlers() 132 | 133 | state = {'i': None, 'prefix': config.root_dir, 'index': index} 134 | 135 | for instruction, arg in plan: 136 | 137 | log.debug(' %s(%r)' % (instruction, arg)) 138 | 139 | if state['i'] is not None and instruction in progress_cmds: 140 | state['i'] += 1 141 | getLogger('progress.update').info((install.name_dist(arg), 142 | state['i']-1)) 143 | cmd = _commands.get(instruction) 144 | 145 | if cmd is None: 146 | raise InvalidInstruction(instruction) 147 | 148 | cmd(state, arg) 149 | 150 | if (state['i'] is not None and instruction in progress_cmds 151 | and state['maxval'] == state['i']): 152 | state['i'] = None 153 | getLogger('progress.stop').info(None) 154 | 155 | install.messages(state['prefix']) 156 | -------------------------------------------------------------------------------- /conda/cli/main_run.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | import sys 10 | import logging 11 | 12 | from conda.cli import common 13 | 14 | descr = """ 15 | Launches an application installed with conda. 16 | 17 | To include command line options in a command, separate the command from the 18 | other options with --, like 19 | 20 | conda run -- ipython --matplotlib 21 | """ 22 | 23 | examples = """ 24 | Examples: 25 | 26 | conda run ipython-notebook 27 | """ 28 | 29 | def configure_parser(sub_parsers): 30 | p = sub_parsers.add_parser( 31 | 'run', 32 | description=descr, 33 | help=descr, 34 | epilog=examples, 35 | ) 36 | common.add_parser_prefix(p) 37 | common.add_parser_quiet(p) 38 | common.add_parser_json(p) 39 | p.add_argument( 40 | 'package', 41 | metavar='COMMAND', 42 | action="store", 43 | nargs='?', 44 | help="Package to launch." 45 | ) 46 | p.add_argument( 47 | 'arguments', 48 | metavar='ARGUMENTS', 49 | action='store', 50 | nargs='*', 51 | help="Additional arguments to application." 52 | ) 53 | p.set_defaults(func=execute) 54 | 55 | 56 | def execute(args, parser): 57 | if not args.package: 58 | parser.print_help() 59 | return 60 | 61 | import conda.install 62 | import conda.resolve 63 | from conda.api import get_package_versions, app_is_installed 64 | from conda.misc import launch 65 | 66 | prefix = common.get_prefix(args) 67 | 68 | if args.quiet: 69 | logging.disable(logging.CRITICAL) 70 | 71 | if args.package.endswith('.tar.bz2'): 72 | if app_is_installed(args.package, prefixes=[prefix]): 73 | fn = args.package 74 | else: 75 | error_message = "Package {} not installed.".format(args.package) 76 | common.error_and_exit(error_message, json=args.json, 77 | error_type="PackageNotInstalled") 78 | else: 79 | installed = [] 80 | for pkg in get_package_versions(args.package): 81 | if app_is_installed(pkg.fn, prefixes=[prefix]): 82 | installed.append(pkg) 83 | 84 | for pkg in conda.install.linked(prefix): 85 | name, version, build = pkg.rsplit('-', 2) 86 | if name == args.package: 87 | installed = [conda.resolve.Package(pkg + '.tar.bz2', 88 | conda.install.is_linked(prefix, pkg))] 89 | break 90 | 91 | if installed: 92 | package = max(installed) 93 | fn = package.fn 94 | 95 | try: 96 | subprocess = launch(fn, prefix=prefix, 97 | additional_args=args.arguments, 98 | background=args.json) 99 | if args.json: 100 | common.stdout_json(dict(fn=fn, pid=subprocess.pid)) 101 | elif not args.quiet: 102 | print("Started app. Some apps may take a while to finish loading.") 103 | except TypeError: 104 | execute_command(args.package, prefix, args.arguments, args.json) 105 | except Exception as e: 106 | common.exception_and_exit(e, json=args.json) 107 | else: 108 | # Try interpreting it as a command 109 | execute_command(args.package, prefix, args.arguments, args.json) 110 | 111 | def execute_command(cmd, prefix, additional_args, json=False): 112 | from conda.misc import execute_in_environment 113 | try: 114 | process = execute_in_environment( 115 | cmd, prefix=prefix, additional_args=additional_args, inherit=not json) 116 | if not json: 117 | sys.exit(process.wait()) 118 | else: 119 | common.stdout_json(dict(cmd=cmd, pid=process.pid)) 120 | except OSError: 121 | error_message = "App {} not installed.".format(cmd) 122 | common.error_and_exit(error_message, json=json, 123 | error_type="AppNotInstalled") 124 | -------------------------------------------------------------------------------- /conda/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import logging 4 | import sys 5 | import hashlib 6 | import collections 7 | from functools import partial 8 | from os.path import abspath, isdir, join 9 | import os 10 | 11 | log = logging.getLogger(__name__) 12 | stderrlog = logging.getLogger('stderrlog') 13 | 14 | def can_open(file): 15 | """ 16 | Return True if the given ``file`` can be opened for writing 17 | """ 18 | try: 19 | fp = open(file, "ab") 20 | fp.close() 21 | return True 22 | except IOError: 23 | stderrlog.info("Unable to open %s\n" % file) 24 | return False 25 | 26 | 27 | def can_open_all(files): 28 | """ 29 | Return True if all of the provided ``files`` can be opened 30 | """ 31 | for f in files: 32 | if not can_open(f): 33 | return False 34 | return True 35 | 36 | 37 | def can_open_all_files_in_prefix(prefix, files): 38 | """ 39 | Returns True if all ``files`` at a given ``prefix`` can be opened 40 | """ 41 | return can_open_all((os.path.join(prefix, f) for f in files)) 42 | 43 | def try_write(dir_path): 44 | assert isdir(dir_path) 45 | try: 46 | try: 47 | with open(join(dir_path, '.conda-try-write'), mode='wb') as fo: 48 | fo.write(b'This is a test file.\n') 49 | return True 50 | finally: 51 | # XXX: If this raises an exception it will also return False 52 | os.unlink(join(dir_path, '.conda-try-write')) 53 | except (IOError, OSError): 54 | return False 55 | 56 | 57 | def hashsum_file(path, mode='md5'): 58 | h = hashlib.new(mode) 59 | with open(path, 'rb') as fi: 60 | while True: 61 | chunk = fi.read(262144) # process chunks of 256KB 62 | if not chunk: 63 | break 64 | h.update(chunk) 65 | return h.hexdigest() 66 | 67 | 68 | def md5_file(path): 69 | return hashsum_file(path, 'md5') 70 | 71 | 72 | def url_path(path): 73 | path = abspath(path) 74 | if sys.platform == 'win32': 75 | path = '/' + path.replace(':', '|').replace('\\', '/') 76 | return 'file://%s' % path 77 | 78 | 79 | def human_bytes(n): 80 | """ 81 | Return the number of bytes n in more human readable form. 82 | """ 83 | if n < 1024: 84 | return '%d B' % n 85 | k = n/1024 86 | if k < 1024: 87 | return '%d KB' % round(k) 88 | m = k/1024 89 | if m < 1024: 90 | return '%.1f MB' % m 91 | g = m/1024 92 | return '%.2f GB' % g 93 | 94 | 95 | class memoized(object): 96 | """Decorator. Caches a function's return value each time it is called. 97 | If called later with the same arguments, the cached value is returned 98 | (not reevaluated). 99 | """ 100 | def __init__(self, func): 101 | self.func = func 102 | self.cache = {} 103 | def __call__(self, *args, **kw): 104 | newargs = [] 105 | for arg in args: 106 | if isinstance(arg, list): 107 | newargs.append(tuple(arg)) 108 | elif not isinstance(arg, collections.Hashable): 109 | # uncacheable. a list, for instance. 110 | # better to not cache than blow up. 111 | return self.func(*args, **kw) 112 | else: 113 | newargs.append(arg) 114 | newargs = tuple(newargs) 115 | key = (newargs, frozenset(kw.items())) 116 | if key in self.cache: 117 | return self.cache[key] 118 | else: 119 | value = self.func(*args, **kw) 120 | self.cache[key] = value 121 | return value 122 | 123 | 124 | # For instance methods only 125 | class memoize(object): # 577452 126 | def __init__(self, func): 127 | self.func = func 128 | def __get__(self, obj, objtype=None): 129 | if obj is None: 130 | return self.func 131 | return partial(self, obj) 132 | def __call__(self, *args, **kw): 133 | obj = args[0] 134 | try: 135 | cache = obj.__cache 136 | except AttributeError: 137 | cache = obj.__cache = {} 138 | key = (self.func, args[1:], frozenset(kw.items())) 139 | try: 140 | res = cache[key] 141 | except KeyError: 142 | res = cache[key] = self.func(*args, **kw) 143 | return res 144 | -------------------------------------------------------------------------------- /conda/cli/activate.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import os 4 | import sys 5 | from os.path import isdir, join, abspath 6 | 7 | from conda.cli.common import find_prefix_name 8 | 9 | def help(): 10 | # sys.argv[1] will be ..checkenv in activate if an environment is already 11 | # activated 12 | if sys.argv[1] in ('..activate', '..checkenv'): 13 | sys.exit("""Usage: source activate ENV 14 | 15 | adds the 'bin' directory of the environment ENV to the front of PATH. 16 | ENV may either refer to just the name of the environment, or the full 17 | prefix path.""") 18 | else: # ..deactivate 19 | sys.exit("""Usage: source deactivate 20 | 21 | removes the 'bin' directory of the environment activated with 'source 22 | activate' from PATH. """) 23 | 24 | 25 | def prefix_from_arg(arg): 26 | if os.sep in arg: 27 | return abspath(arg) 28 | prefix = find_prefix_name(arg) 29 | if prefix is None: 30 | sys.exit('Error: could not find environment: %s' % arg) 31 | return prefix 32 | 33 | 34 | def binpath_from_arg(arg): 35 | path = join(prefix_from_arg(arg), 'bin') 36 | if not isdir(path): 37 | sys.exit("Error: no such directory: %s" % path) 38 | return path 39 | 40 | def main(): 41 | if '-h' in sys.argv or '--help' in sys.argv: 42 | help() 43 | 44 | if sys.argv[1] == '..activate': 45 | if len(sys.argv) == 2: 46 | sys.exit("Error: no environment provided.") 47 | elif len(sys.argv) == 3: 48 | binpath = binpath_from_arg(sys.argv[2]) 49 | else: 50 | sys.exit("Error: did not expect more than one argument") 51 | 52 | paths = [binpath] 53 | sys.stderr.write("prepending %s to PATH\n" % binpath) 54 | 55 | elif sys.argv[1] == '..deactivate': 56 | if len(sys.argv) != 2: 57 | sys.exit("Error: too many arguments.") 58 | 59 | try: 60 | binpath = binpath_from_arg(os.getenv('CONDA_DEFAULT_ENV', 'root')) 61 | except SystemExit: 62 | print(os.environ['PATH']) 63 | raise 64 | paths = [] 65 | sys.stderr.write("discarding %s from PATH\n" % binpath) 66 | 67 | elif sys.argv[1] == '..activateroot': 68 | if len(sys.argv) != 2: 69 | sys.exit("Error: too many arguments.") 70 | 71 | if 'CONDA_DEFAULT_ENV' not in os.environ: 72 | sys.exit("Error: No environment to deactivate") 73 | try: 74 | import conda.config 75 | binpath = binpath_from_arg(os.getenv('CONDA_DEFAULT_ENV')) 76 | rootpath = binpath_from_arg(conda.config.root_env_name) 77 | except SystemExit: 78 | print(os.environ['PATH']) 79 | raise 80 | # deactivate is the same as activate root (except without setting 81 | # CONDA_DEFAULT_ENV or PS1). XXX: The user might want to put the root 82 | # env back somewhere in the middle of the PATH, not at the beginning. 83 | if rootpath not in os.getenv('PATH').split(os.pathsep): 84 | paths = [rootpath] 85 | else: 86 | paths = [] 87 | sys.stderr.write("discarding %s from PATH\n" % binpath) 88 | 89 | elif sys.argv[1] == '..checkenv': 90 | if len(sys.argv) < 3: 91 | sys.exit("Error: no environment provided.") 92 | if len(sys.argv) > 3: 93 | sys.exit("Error: did not expect more than one argument.") 94 | binpath = binpath_from_arg(sys.argv[2]) 95 | # Make sure an env always has the conda symlink 96 | try: 97 | import conda.config 98 | if binpath != join(conda.config.root_dir, 'bin'): 99 | import conda.install 100 | conda.install.symlink_conda(join(binpath, '..'), conda.config.root_dir) 101 | except (IOError, OSError) as e: 102 | import errno 103 | if e.errno == errno.EPERM or e.errno == errno.EACCES: 104 | sys.exit("Cannot activate environment {}, do not have write access to write conda symlink".format(sys.argv[2])) 105 | raise 106 | sys.exit(0) 107 | 108 | else: 109 | # This means there is a bug in main.py 110 | raise ValueError("unexpected command") 111 | 112 | for path in os.getenv('PATH').split(os.pathsep): 113 | if path != binpath: 114 | paths.append(path) 115 | print(os.pathsep.join(paths)) 116 | 117 | 118 | if __name__ == '__main__': 119 | main() 120 | -------------------------------------------------------------------------------- /conda/cli/main_package.py: -------------------------------------------------------------------------------- 1 | # (c) Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | from argparse import RawTextHelpFormatter 10 | 11 | from conda.cli import common 12 | 13 | 14 | descr = "Low-level conda package utility. (EXPERIMENTAL)" 15 | 16 | 17 | def configure_parser(sub_parsers): 18 | p = sub_parsers.add_parser( 19 | 'package', 20 | description=descr, 21 | help=descr, 22 | ) 23 | common.add_parser_prefix(p) 24 | p.add_argument( 25 | '-w', "--which", 26 | metavar="PATH", 27 | nargs='+', 28 | action="store", 29 | help="Given some PATH print which conda package the file came from.", 30 | ) 31 | p.add_argument( 32 | '-L', "--ls-files", 33 | metavar='PKG-NAME', 34 | action="store", 35 | help="List all files belonging to specified package.", 36 | ) 37 | p.add_argument( 38 | '-r', "--reset", 39 | action="store_true", 40 | help="Remove all untracked files and exit.", 41 | ) 42 | p.add_argument( 43 | '-u', "--untracked", 44 | action="store_true", 45 | help="Display all untracked files and exit.", 46 | ) 47 | p.add_argument( 48 | "--pkg-name", 49 | action="store", 50 | default="unknown", 51 | help="Package name of the created package.", 52 | ) 53 | p.add_argument( 54 | "--pkg-version", 55 | action="store", 56 | default="0.0", 57 | help="Package version of the created package.", 58 | ) 59 | p.add_argument( 60 | "--pkg-build", 61 | action="store", 62 | default=0, 63 | help="Package build number of the created package.", 64 | ) 65 | p.set_defaults(func=execute) 66 | 67 | def list_package_files(pkg_name=None): 68 | import os 69 | import re 70 | import conda.config as config 71 | from conda.misc import walk_prefix 72 | 73 | pkgs_dirs = config.pkgs_dirs[0] 74 | all_dir_names = [] 75 | pattern = re.compile(pkg_name, re.I) 76 | 77 | print('\nINFO: The location for available packages: %s' % (pkgs_dirs)) 78 | 79 | for dir in os.listdir(pkgs_dirs): 80 | ignore_dirs = [ '_cache-0.0-x0', 'cache' ] 81 | 82 | if dir in ignore_dirs: 83 | continue 84 | 85 | if not os.path.isfile(pkgs_dirs+"/"+dir): 86 | match = pattern.match(dir) 87 | 88 | if match: 89 | all_dir_names.append(dir) 90 | 91 | num_of_all_dir_names = len(all_dir_names) 92 | dir_num_width = len(str(num_of_all_dir_names)) 93 | 94 | if num_of_all_dir_names == 0: 95 | print("\n\tWARN: There is NO '%s' package.\n" % (pkg_name)) 96 | return 1 97 | elif num_of_all_dir_names >= 2: 98 | print("\n\tWARN: Ambiguous package name ('%s'), choose one name from below list:\n" % (pkg_name)) 99 | 100 | num = 0 101 | for dir in all_dir_names: 102 | num += 1 103 | print("\t[ {num:>{width}} / {total} ]: {dir}".format(num=num, width=dir_num_width, total=num_of_all_dir_names, dir=dir)) 104 | print("") 105 | return 1 106 | 107 | full_pkg_name = all_dir_names[0] 108 | 109 | print("INFO: All files belonging to '%s' package:\n" % (full_pkg_name)) 110 | 111 | pkg_dir = pkgs_dirs+"/"+full_pkg_name 112 | 113 | ret = walk_prefix(pkg_dir, ignore_predefined_files=False) 114 | 115 | for item in ret: 116 | print(pkg_dir+"/"+item) 117 | 118 | def execute(args, parser): 119 | import sys 120 | 121 | from conda.misc import untracked 122 | from conda.packup import make_tarbz2, remove 123 | 124 | 125 | prefix = common.get_prefix(args) 126 | 127 | if args.which: 128 | from conda.misc import which_package 129 | 130 | for path in args.which: 131 | for dist in which_package(path): 132 | print('%-50s %s' % (path, dist)) 133 | return 134 | 135 | if args.ls_files: 136 | if list_package_files(args.ls_files) == 1: 137 | sys.exit(1) 138 | else: 139 | return 140 | 141 | print('# prefix:', prefix) 142 | 143 | if args.reset: 144 | remove(prefix, untracked(prefix)) 145 | return 146 | 147 | if args.untracked: 148 | files = sorted(untracked(prefix)) 149 | print('# untracked files: %d' % len(files)) 150 | for fn in files: 151 | print(fn) 152 | return 153 | 154 | make_tarbz2(prefix, 155 | name = args.pkg_name.lower(), 156 | version = args.pkg_version, 157 | build_number = int(args.pkg_build)) 158 | -------------------------------------------------------------------------------- /conda/packup.py: -------------------------------------------------------------------------------- 1 | # NOTE: 2 | # This module is deprecated. Don't import from this here when writing 3 | # new code. 4 | from __future__ import print_function, division, absolute_import 5 | 6 | import os 7 | import re 8 | import sys 9 | import json 10 | import shutil 11 | import hashlib 12 | import tarfile 13 | import tempfile 14 | from os.path import basename, dirname, isfile, islink, join 15 | 16 | import conda.config as config 17 | import conda.install as install 18 | from conda.misc import untracked 19 | from conda.compat import PY3 20 | 21 | 22 | def get_installed_version(prefix, name): 23 | for dist in install.linked(prefix): 24 | n, v, b = dist.rsplit('-', 2) 25 | if n == name: 26 | return v 27 | return None 28 | 29 | 30 | def remove(prefix, files): 31 | """ 32 | Remove files for a given prefix. 33 | """ 34 | dst_dirs = set() 35 | for f in files: 36 | dst = join(prefix, f) 37 | dst_dirs.add(dirname(dst)) 38 | os.unlink(dst) 39 | 40 | for path in sorted(dst_dirs, key=len, reverse=True): 41 | try: 42 | os.rmdir(path) 43 | except OSError: # directory might not be empty 44 | pass 45 | 46 | 47 | def create_info(name, version, build_number, requires_py): 48 | d = dict( 49 | name = name, 50 | version = version, 51 | platform = config.platform, 52 | arch = config.arch_name, 53 | build_number = int(build_number), 54 | build = str(build_number), 55 | depends = [], 56 | ) 57 | if requires_py: 58 | d['build'] = ('py%d%d_' % requires_py) + d['build'] 59 | d['depends'].append('python %d.%d*' % requires_py) 60 | return d 61 | 62 | 63 | shebang_pat = re.compile(r'^#!.+$', re.M) 64 | def fix_shebang(tmp_dir, path): 65 | if open(path, 'rb').read(2) != '#!': 66 | return False 67 | 68 | with open(path) as fi: 69 | data = fi.read() 70 | m = shebang_pat.match(data) 71 | if not (m and 'python' in m.group()): 72 | return False 73 | 74 | data = shebang_pat.sub('#!%s/bin/python' % install.prefix_placeholder, 75 | data, count=1) 76 | tmp_path = join(tmp_dir, basename(path)) 77 | with open(tmp_path, 'w') as fo: 78 | fo.write(data) 79 | os.chmod(tmp_path, int('755', 8)) 80 | return True 81 | 82 | 83 | def _add_info_dir(t, tmp_dir, files, has_prefix, info): 84 | info_dir = join(tmp_dir, 'info') 85 | os.mkdir(info_dir) 86 | with open(join(info_dir, 'files'), 'w') as fo: 87 | for f in files: 88 | fo.write(f + '\n') 89 | 90 | with open(join(info_dir, 'index.json'), 'w') as fo: 91 | json.dump(info, fo, indent=2, sort_keys=True) 92 | 93 | if has_prefix: 94 | with open(join(info_dir, 'has_prefix'), 'w') as fo: 95 | for f in has_prefix: 96 | fo.write(f + '\n') 97 | 98 | for fn in os.listdir(info_dir): 99 | t.add(join(info_dir, fn), 'info/' + fn) 100 | 101 | 102 | def create_conda_pkg(prefix, files, info, tar_path, update_info=None): 103 | """ 104 | create a conda package with `files` (in `prefix` and `info` metadata) 105 | at `tar_path`, and return a list of warning strings 106 | """ 107 | files = sorted(files) 108 | warnings = [] 109 | has_prefix = [] 110 | tmp_dir = tempfile.mkdtemp() 111 | t = tarfile.open(tar_path, 'w:bz2') 112 | h = hashlib.new('sha1') 113 | for f in files: 114 | assert not (f.startswith('/') or f.endswith('/') or 115 | '\\' in f or f == ''), f 116 | path = join(prefix, f) 117 | if f.startswith('bin/') and fix_shebang(tmp_dir, path): 118 | path = join(tmp_dir, basename(path)) 119 | has_prefix.append(f) 120 | t.add(path, f) 121 | h.update(f.encode('utf-8')) 122 | h.update(b'\x00') 123 | if islink(path): 124 | link = os.readlink(path) 125 | if PY3 and isinstance(link, str): 126 | h.update(bytes(link, 'utf-8')) 127 | else: 128 | h.update(link) 129 | if link.startswith('/'): 130 | warnings.append('found symlink to absolute path: %s -> %s' % 131 | (f, link)) 132 | elif isfile(path): 133 | h.update(open(path, 'rb').read()) 134 | if path.endswith('.egg-link'): 135 | warnings.append('found egg link: %s' % f) 136 | 137 | info['file_hash'] = h.hexdigest() 138 | if update_info: 139 | update_info(info) 140 | _add_info_dir(t, tmp_dir, files, has_prefix, info) 141 | t.close() 142 | shutil.rmtree(tmp_dir) 143 | return warnings 144 | 145 | 146 | def make_tarbz2(prefix, name='unknown', version='0.0', build_number=0, 147 | files=None): 148 | if files is None: 149 | files = untracked(prefix) 150 | print("# files: %d" % len(files)) 151 | if len(files) == 0: 152 | print("# failed: nothing to do") 153 | return None 154 | 155 | if any('/site-packages/' in f for f in files): 156 | python_version = get_installed_version(prefix, 'python') 157 | assert python_version is not None 158 | requires_py = tuple(int(x) for x in python_version[:3].split('.')) 159 | else: 160 | requires_py = False 161 | 162 | info = create_info(name, version, build_number, requires_py) 163 | tarbz2_fn = '%(name)s-%(version)s-%(build)s.tar.bz2' % info 164 | create_conda_pkg(prefix, files, info, tarbz2_fn) 165 | print('# success') 166 | print(tarbz2_fn) 167 | return tarbz2_fn 168 | 169 | 170 | if __name__ == '__main__': 171 | make_tarbz2(sys.prefix) 172 | -------------------------------------------------------------------------------- /conda/bundle.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import os 4 | import sys 5 | import json 6 | import time 7 | import shutil 8 | import hashlib 9 | import tarfile 10 | import tempfile 11 | from os.path import abspath, expanduser, basename, isdir, isfile, islink, join 12 | 13 | import conda.config as config 14 | from conda.api import get_index 15 | from conda.misc import untracked, discard_conda 16 | import conda.install as install 17 | import conda.plan as plan 18 | 19 | 20 | ISO8601 = "%Y-%m-%d %H:%M:%S %z" 21 | BDP = 'bundle-data/' 22 | 23 | warn = [] 24 | 25 | 26 | def add_file(t, path, f): 27 | t.add(path, f) 28 | if islink(path): 29 | link = os.readlink(path) 30 | if link.startswith('/'): 31 | warn.append('found symlink to absolute path: %s -> %s' % (f, link)) 32 | elif isfile(path): 33 | if path.endswith('.egg-link'): 34 | warn.append('found egg link: %s' % f) 35 | 36 | def add_data(t, data_path): 37 | data_path = abspath(data_path) 38 | if isfile(data_path): 39 | f = BDP + basename(data_path) 40 | add_file(t, data_path, f) 41 | elif isdir(data_path): 42 | for root, dirs, files in os.walk(data_path): 43 | for fn in files: 44 | if fn.endswith(('~', '.pyc')): 45 | continue 46 | path = join(root, fn) 47 | f = path[len(data_path) + 1:] 48 | if f.startswith('.git'): 49 | continue 50 | add_file(t, path, BDP + f) 51 | else: 52 | raise RuntimeError('no such file or directory: %s' % data_path) 53 | 54 | 55 | def add_info_files(t, meta): 56 | tmp_dir = tempfile.mkdtemp() 57 | with open(join(tmp_dir, 'index.json'), 'w') as fo: 58 | json.dump(meta, fo, indent=2, sort_keys=True) 59 | with open(join(tmp_dir, 'files'), 'w') as fo: 60 | for m in t.getmembers(): 61 | fo.write(m.path + '\n') 62 | for fn in 'index.json', 'files': 63 | add_file(t, join(tmp_dir, fn), 'info/' + fn) 64 | shutil.rmtree(tmp_dir) 65 | 66 | 67 | def get_version(meta): 68 | s = '%(creator)s:%(bundle_name)s' % meta 69 | h = hashlib.new('sha1') 70 | h.update(s.encode('utf-8')) 71 | return h.hexdigest() 72 | 73 | 74 | def create_bundle(prefix=None, data_path=None, bundle_name=None, 75 | extra_meta=None): 76 | """ 77 | Create a "bundle" of the environment located in `prefix`, 78 | and return the full path to the created package, which is going to be 79 | located in the current working directory, unless specified otherwise. 80 | """ 81 | meta = dict( 82 | name = 'bundle', 83 | build = '0', build_number = 0, 84 | type = 'bundle', 85 | bundle_name = bundle_name, 86 | creator = os.getenv('USER'), 87 | platform = config.platform, 88 | arch = config.arch_name, 89 | ctime = time.strftime(ISO8601), 90 | depends = [], 91 | ) 92 | meta['version'] = get_version(meta) 93 | 94 | tar_path = join('bundle-%(version)s-0.tar.bz2' % meta) 95 | t = tarfile.open(tar_path, 'w:bz2') 96 | if prefix: 97 | prefix = abspath(prefix) 98 | if not prefix.startswith('/opt/anaconda'): 99 | for f in sorted(untracked(prefix, exclude_self_build=True)): 100 | if f.startswith(BDP): 101 | raise RuntimeError('bad untracked file: %s' % f) 102 | if f.startswith('info/'): 103 | continue 104 | path = join(prefix, f) 105 | add_file(t, path, f) 106 | meta['bundle_prefix'] = prefix 107 | meta['depends'] = [' '.join(dist.rsplit('-', 2)) for dist in 108 | sorted(install.linked(prefix))] 109 | 110 | if data_path: 111 | add_data(t, data_path) 112 | 113 | if extra_meta: 114 | meta.update(extra_meta) 115 | 116 | add_info_files(t, meta) 117 | t.close() 118 | return tar_path 119 | 120 | 121 | def clone_bundle(path, prefix=None, bundle_name=None): 122 | """ 123 | Clone the bundle (located at `path`) by creating a new environment at 124 | `prefix` (unless prefix is None or the prefix directory already exists) 125 | """ 126 | try: 127 | t = tarfile.open(path, 'r:*') 128 | meta = json.load(t.extractfile('info/index.json')) 129 | except tarfile.ReadError: 130 | raise RuntimeError('bad tar archive: %s' % path) 131 | except KeyError: 132 | raise RuntimeError("no archive 'info/index.json' in: %s" % (path)) 133 | 134 | if prefix and not isdir(prefix): 135 | for m in t.getmembers(): 136 | if m.path.startswith((BDP, 'info/')): 137 | continue 138 | t.extract(m, path=prefix) 139 | dists = discard_conda('-'.join(s.split()) 140 | for s in meta.get('depends', [])) 141 | actions = plan.ensure_linked_actions(dists, prefix) 142 | index = get_index() 143 | plan.display_actions(actions, index) 144 | plan.execute_actions(actions, index, verbose=True) 145 | 146 | bundle_dir = abspath(expanduser('~/bundles/%s' % 147 | (bundle_name or meta.get('bundle_name')))) 148 | for m in t.getmembers(): 149 | if m.path.startswith(BDP): 150 | targetpath = join(bundle_dir, m.path[len(BDP):]) 151 | t._extract_member(m, targetpath) 152 | 153 | t.close() 154 | 155 | 156 | if __name__ == '__main__': 157 | try: 158 | path = sys.argv[1] 159 | except IndexError: 160 | path = 'bundle-90809033a16372615e953f6961a6a272a4b35a1a.tar.bz2' 161 | clone_bundle(path, 162 | join(config.envs_dirs[0], 'tc001')) 163 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. NOTE: This file serves both as the README on GitHub and the index.html for 2 | conda.pydata.org. If you update this file, be sure to cd to the web 3 | directory and run ``make html; make live`` 4 | 5 | ===== 6 | Conda 7 | ===== 8 | 9 | .. image:: https://travis-ci.org/conda/conda.svg?branch=master 10 | :target: https://travis-ci.org/conda/conda 11 | 12 | .. image:: https://ci.appveyor.com/api/projects/status/6btwguwga3959bkc 13 | :target: https://ci.appveyor.com/project/asmeurer/conda-973 14 | 15 | Conda is a cross-platform, Python-agnostic binary package manager. It is the 16 | package manager used by `Anaconda 17 | `_ installations, but it may be 18 | used for other systems as well. Conda makes environments first-class 19 | citizens, making it easy to create independent environments even for C 20 | libraries. Conda is written entirely in Python, and is BSD licensed open 21 | source. 22 | 23 | 24 | Installation 25 | ------------ 26 | 27 | Conda is a part of the `Anaconda distribution `_. You can also download a 28 | minimal installation that only includes conda and its dependencies, called 29 | `Miniconda `_. 30 | 31 | 32 | Getting Started 33 | --------------- 34 | 35 | If you install Anaconda, you will already have hundreds of packages 36 | installed. You can see what packages are installed by running 37 | 38 | .. code-block:: bash 39 | 40 | $ conda list 41 | 42 | to see all the packages that are available, use 43 | 44 | .. code-block:: bash 45 | 46 | $ conda search 47 | 48 | and to install a package, use 49 | 50 | .. code-block:: bash 51 | 52 | $ conda install 53 | 54 | 55 | The real power of conda comes from its ability to manage environments. In 56 | conda, an environment can be thought of as a completely separate installation. 57 | Conda installs packages into environments efficiently using `hard links 58 | `_ by default when it is possible, so 59 | environments are space efficient, and take seconds to create. 60 | 61 | The default environment, which ``conda`` itself is installed into is called 62 | ``root``. To create another environment, use the ``conda create`` 63 | command. For instance, to create an environment with the IPython notebook and 64 | NumPy 1.6, which is older than the version that comes with Anaconda by 65 | default, you would run 66 | 67 | .. code-block:: bash 68 | 69 | $ conda create -n numpy16 ipython-notebook numpy=1.6 70 | 71 | This creates an environment called ``numpy16`` with the latest version of 72 | the IPython notebook, NumPy 1.6, and their dependencies. 73 | 74 | We can now activate this environment, use 75 | 76 | .. code-block:: bash 77 | 78 | # On Linux and Mac OS X 79 | $ source activate numpy16 80 | 81 | # On Windows 82 | > activate numpy16 83 | 84 | This puts the bin directory of the ``numpy16`` environment in the front of the 85 | ``PATH``, and sets it as the default environment for all subsequent conda commands. 86 | 87 | To go back to the root environment, use 88 | 89 | .. code-block:: bash 90 | 91 | # On Linux and Mac OS X 92 | $ source deactivate 93 | 94 | # On Windows 95 | > deactivate 96 | 97 | 98 | Building Your Own Packages 99 | -------------------------- 100 | 101 | You can easily build your own packages for conda, and upload them 102 | to `anaconda.org `_, a free service for hosting 103 | packages for conda, as well as other package managers. 104 | To build a package, create a recipe. 105 | See http://github.com/conda/conda-recipes for many example recipes, and 106 | http://docs.continuum.io/conda/build.html for documentation on how to build 107 | recipes. 108 | 109 | To upload to anaconda.org, create an account. Then, install the 110 | anaconda-client and login 111 | 112 | .. code-block:: bash 113 | 114 | $ conda install anaconda-client 115 | $ anaconda login 116 | 117 | Then, after you build your recipe 118 | 119 | .. code-block:: bash 120 | 121 | $ conda build 122 | 123 | you will be prompted to upload to anaconda.org. 124 | 125 | To add your anaconda.org channel, or the channel of others to conda so 126 | that ``conda install`` will find and install their packages, run 127 | 128 | .. code-block:: bash 129 | 130 | $ conda config --add channels https://conda.anaconda.org/username 131 | 132 | (replacing ``username`` with the user name of the person whose channel you want 133 | to add). 134 | 135 | Getting Help 136 | ------------ 137 | 138 | The documentation for conda is at http://conda.pydata.org/docs/. You can 139 | subscribe to the `conda mailing list 140 | `_. The source 141 | code and issue tracker for conda are on `GitHub `_. 142 | 143 | Contributing 144 | ------------ 145 | 146 | Contributions to conda are welcome. Just fork the GitHub repository and send a 147 | pull request. 148 | 149 | To develop on conda, the easiest way is to use ``python setup.py develop`` in your 150 | root conda environment. This will install a link to the local conda source 151 | code, so that any change you make to conda will be instantly available. To undo 152 | this, run ``python setup.py develop -u``. If you are worried about breaking 153 | your conda installation, you can install a separate instance of `Miniconda 154 | `_ and work off it. This is also the 155 | only way to test conda in both Python 2 and Python 3, as conda can only be 156 | installed into a root environment. 157 | 158 | Run the conda tests by ``conda install pytest`` and then running ``py.test`` 159 | in the conda directory. The tests are also run by Travis CI when you make a 160 | pull request. 161 | -------------------------------------------------------------------------------- /conda/api.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import os 4 | from collections import defaultdict 5 | from os.path import isdir, join 6 | 7 | from conda import config 8 | from conda import install 9 | #from conda.utils import url_path 10 | from conda.fetch import fetch_index 11 | from conda.compat import iteritems, itervalues 12 | from conda.resolve import MatchSpec, Package, Resolve 13 | 14 | 15 | def _name_fn(fn): 16 | assert fn.endswith('.tar.bz2') 17 | return install.name_dist(fn[:-8]) 18 | 19 | def _fn2spec(fn): 20 | assert fn.endswith('.tar.bz2') 21 | return ' '.join(fn[:-8].rsplit('-', 2)[:2]) 22 | 23 | def _fn2fullspec(fn): 24 | assert fn.endswith('.tar.bz2') 25 | return ' '.join(fn[:-8].rsplit('-', 2)) 26 | 27 | 28 | def get_index(channel_urls=(), prepend=True, platform=None, 29 | use_cache=False, unknown=False, offline=False): 30 | """ 31 | Return the index of packages available on the channels 32 | 33 | If prepend=False, only the channels passed in as arguments are used. 34 | If platform=None, then the current platform is used. 35 | """ 36 | channel_urls = config.normalize_urls(channel_urls, platform=platform) 37 | if prepend: 38 | channel_urls += config.get_channel_urls(platform=platform) 39 | if offline: 40 | channel_urls = [url for url in channel_urls if url.startswith('file:')] 41 | return fetch_index(tuple(channel_urls), use_cache=use_cache, 42 | unknown=unknown) 43 | 44 | 45 | def app_get_index(all_version=False): 46 | """ 47 | return the index of available applications on the channels 48 | 49 | By default only the latest version of each app is included in the result, 50 | unless all_version is set to True. 51 | """ 52 | index = {fn: info for fn, info in iteritems(get_index()) 53 | if info.get('type') == 'app'} 54 | if all_version: 55 | return index 56 | 57 | d = defaultdict(list) # name -> list of Package objects 58 | for fn, info in iteritems(index): 59 | d[_name_fn(fn)].append(Package(fn, info)) 60 | 61 | res = {} 62 | for pkgs in itervalues(d): 63 | pkg = max(pkgs) 64 | res[pkg.fn] = index[pkg.fn] 65 | return res 66 | 67 | 68 | def app_get_icon_url(fn): 69 | """ 70 | return the URL belonging to the icon for application `fn`. 71 | """ 72 | from conda.misc import make_icon_url 73 | index = get_index() 74 | info = index[fn] 75 | return make_icon_url(info) 76 | 77 | 78 | def app_info_packages(fn): 79 | """ 80 | given the filename of a package, return which packages (and their sizes) 81 | still need to be downloaded, in order to install the package. That is, 82 | the package itself and it's dependencies. 83 | Returns a list of tuples (pkg_name, pkg_version, size, 84 | fetched? True or False). 85 | """ 86 | from conda.resolve import Resolve 87 | 88 | index = get_index() 89 | r = Resolve(index) 90 | res = [] 91 | for fn2 in r.solve([_fn2fullspec(fn)]): 92 | info = index[fn2] 93 | res.append((info['name'], info['version'], info['size'], 94 | any(install.is_fetched(pkgs_dir, fn2[:-8]) 95 | for pkgs_dir in config.pkgs_dirs))) 96 | return res 97 | 98 | 99 | def app_is_installed(fn, prefixes=None): 100 | """ 101 | Return the list of prefix directories in which `fn` in installed into, 102 | which might be an empty list. 103 | """ 104 | if prefixes is None: 105 | prefixes = [config.root_dir] 106 | for envs_dir in config.envs_dirs: 107 | for fn2 in os.listdir(envs_dir): 108 | prefix = join(envs_dir, fn2) 109 | if isdir(prefix): 110 | prefixes.append(prefix) 111 | dist = fn[:-8] 112 | return [prefix for prefix in prefixes if install.is_linked(prefix, dist)] 113 | 114 | # It seems to me that we need different types of apps, i.e. apps which 115 | # are preferably installed (or already exist) in existing environments, 116 | # and apps which are more "standalone" (such as firefox). 117 | 118 | def app_install(fn, prefix=config.root_dir): 119 | """ 120 | Install the application `fn` into prefix (which defauts to the root 121 | environment). 122 | """ 123 | import conda.plan as plan 124 | 125 | index = get_index() 126 | actions = plan.install_actions(prefix, index, [_fn2spec(fn)]) 127 | plan.execute_actions(actions, index) 128 | 129 | 130 | def app_launch(fn, prefix=config.root_dir, additional_args=None): 131 | """ 132 | Launch the application `fn` (with optional additional command line 133 | arguments), in the prefix (which defaults to the root environment). 134 | Returned is the process object (the one returned by subprocess.Popen), 135 | or None if the application `fn` is not installed in the prefix. 136 | """ 137 | from conda.misc import launch 138 | 139 | return launch(fn, prefix, additional_args) 140 | 141 | 142 | def app_uninstall(fn, prefix=config.root_dir): 143 | """ 144 | Uninstall application `fn` (but not its dependencies). 145 | 146 | Like `conda remove fn`. 147 | 148 | """ 149 | import conda.cli.common as common 150 | import conda.plan as plan 151 | 152 | index = None 153 | specs = [_fn2spec(fn)] 154 | if (plan.is_root_prefix(prefix) and 155 | common.names_in_specs(common.root_no_rm, specs)): 156 | raise ValueError("Cannot remove %s from the root environment" % 157 | ', '.join(common.root_no_rm)) 158 | 159 | actions = plan.remove_actions(prefix, specs, index=index) 160 | 161 | if plan.nothing_to_do(actions): 162 | raise ValueError("Nothing to do") 163 | 164 | plan.execute_actions(actions, index) 165 | 166 | 167 | def get_package_versions(package): 168 | index = get_index() 169 | r = Resolve(index) 170 | if package in r.groups: 171 | return r.get_pkgs(MatchSpec(package)) 172 | else: 173 | return [] 174 | 175 | 176 | if __name__ == '__main__': 177 | #from pprint import pprint 178 | for fn in app_get_index(): 179 | print('%s: %s' % (fn, app_is_installed(fn))) 180 | #pprint(missing_packages('twisted-12.3.0-py27_0.tar.bz2')) 181 | #print(app_install('twisted-12.3.0-py27_0.tar.bz2')) 182 | #pprint(get_index()) 183 | #print(app_get_icon_url('spyder-app-2.2.0-py27_0.tar.bz2')) 184 | -------------------------------------------------------------------------------- /conda/cli/main_list.py: -------------------------------------------------------------------------------- 1 | # (c) Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | import re 10 | import sys 11 | import subprocess 12 | from os.path import isdir, isfile 13 | import logging 14 | from argparse import RawDescriptionHelpFormatter 15 | 16 | import conda.install as install 17 | import conda.config as config 18 | from conda.cli import common 19 | 20 | # pip_args is here for BC 21 | from conda.pip import pip_args, add_pip_installed 22 | 23 | 24 | descr = "List linked packages in a conda environment." 25 | 26 | # Note, the formatting of this is designed to work well with help2man 27 | examples = """ 28 | Examples: 29 | 30 | List all packages in the current environment: 31 | 32 | conda list 33 | 34 | List all packages installed into the environment 'myenv': 35 | 36 | conda list -n myenv 37 | 38 | Save packages for future use: 39 | 40 | conda list --export > package-list.txt 41 | 42 | Reinstall packages from an export file: 43 | 44 | conda create -n myenv --file package-list.txt 45 | 46 | """ 47 | log = logging.getLogger(__name__) 48 | 49 | def configure_parser(sub_parsers): 50 | p = sub_parsers.add_parser( 51 | 'list', 52 | description=descr, 53 | help=descr, 54 | formatter_class=RawDescriptionHelpFormatter, 55 | epilog=examples, 56 | add_help=False, 57 | ) 58 | common.add_parser_help(p) 59 | common.add_parser_prefix(p) 60 | common.add_parser_json(p) 61 | p.add_argument( 62 | '-c', "--canonical", 63 | action="store_true", 64 | help="Output canonical names of packages only.", 65 | ) 66 | p.add_argument( 67 | '-f', "--full-name", 68 | action="store_true", 69 | help="Only search for full names, i.e., ^$.", 70 | ) 71 | p.add_argument( 72 | '-e', "--export", 73 | action="store_true", 74 | help="""Output requirement string only (output may be used by conda create 75 | --file).""", 76 | ) 77 | p.add_argument( 78 | '-r', "--revisions", 79 | action="store_true", 80 | help="List the revision history and exit.", 81 | ) 82 | p.add_argument( 83 | "--no-pip", 84 | action="store_false", 85 | default=True, 86 | dest="pip", 87 | help="Do not include pip-only installed packages.") 88 | p.add_argument( 89 | 'regex', 90 | action="store", 91 | nargs="?", 92 | help="List only packages matching this regular expression.", 93 | ) 94 | p.set_defaults(func=execute) 95 | 96 | 97 | def print_export_header(): 98 | print('# This file may be used to create an environment using:') 99 | print('# $ conda create --name --file ') 100 | print('# platform: %s' % config.subdir) 101 | 102 | 103 | def get_packages(installed, regex): 104 | pat = re.compile(regex, re.I) if regex else None 105 | 106 | for dist in sorted(installed): 107 | name = dist.rsplit('-', 2)[0] 108 | if pat and pat.search(name) is None: 109 | continue 110 | 111 | yield dist 112 | 113 | 114 | def list_packages(prefix, installed, regex=None, format='human'): 115 | res = 1 116 | 117 | result = [] 118 | for dist in get_packages(installed, regex): 119 | res = 0 120 | if format == 'canonical': 121 | result.append(dist) 122 | continue 123 | if format == 'export': 124 | result.append('='.join(dist.rsplit('-', 2))) 125 | continue 126 | 127 | try: 128 | # Returns None if no meta-file found (e.g. pip install) 129 | info = install.is_linked(prefix, dist) 130 | features = set(info.get('features', '').split()) 131 | disp = '%(name)-25s %(version)-15s %(build)15s' % info 132 | disp += ' %s' % common.disp_features(features) 133 | if config.show_channel_urls: 134 | disp += ' %s' % config.canonical_channel_name(info.get('url')) 135 | result.append(disp) 136 | except (AttributeError, IOError, KeyError, ValueError) as e: 137 | log.debug(str(e)) 138 | result.append('%-25s %-15s %15s' % tuple(dist.rsplit('-', 2))) 139 | 140 | return res, result 141 | 142 | 143 | def print_packages(prefix, regex=None, format='human', piplist=False, json=False): 144 | if not isdir(prefix): 145 | common.error_and_exit("""\ 146 | Error: environment does not exist: %s 147 | # 148 | # Use 'conda create' to create an environment before listing its packages.""" % prefix, 149 | json=json, 150 | error_type="NoEnvironmentFound") 151 | 152 | if not json: 153 | if format == 'human': 154 | print('# packages in environment at %s:' % prefix) 155 | print('#') 156 | if format == 'export': 157 | print_export_header() 158 | 159 | installed = install.linked(prefix) 160 | if piplist and config.use_pip and format == 'human': 161 | add_pip_installed(prefix, installed, json=json) 162 | 163 | exitcode, output = list_packages(prefix, installed, regex, format=format) 164 | if not json: 165 | print('\n'.join(output)) 166 | else: 167 | common.stdout_json(output) 168 | sys.exit(exitcode) 169 | 170 | 171 | def execute(args, parser): 172 | prefix = common.get_prefix(args) 173 | 174 | regex = args.regex 175 | if args.full_name: 176 | regex = r'^%s$' % regex 177 | 178 | if args.revisions: 179 | from conda.history import History 180 | 181 | h = History(prefix) 182 | if isfile(h.path): 183 | if not args.json: 184 | h.print_log() 185 | else: 186 | common.stdout_json(h.object_log()) 187 | else: 188 | common.error_and_exit("No revision log found: %s\n" % h.path, 189 | json=args.json, 190 | error_type="NoRevisionLog") 191 | return 192 | 193 | if args.canonical: 194 | format = 'canonical' 195 | elif args.export: 196 | format = 'export' 197 | else: 198 | format = 'human' 199 | 200 | if args.json: 201 | format = 'canonical' 202 | 203 | print_packages(prefix, regex, format, piplist=args.pip, json=args.json) 204 | -------------------------------------------------------------------------------- /conda/compat.py: -------------------------------------------------------------------------------- 1 | """ 2 | For compatibility between Python versions. 3 | Taken mostly from six.py by Benjamin Peterson. 4 | """ 5 | 6 | import sys 7 | import types 8 | import os 9 | 10 | # True if we are running on Python 3. 11 | PY3 = sys.version_info[0] == 3 12 | 13 | if PY3: 14 | string_types = str, 15 | integer_types = int, 16 | class_types = type, 17 | text_type = str 18 | binary_type = bytes 19 | input = input 20 | def lchmod(path, mode): 21 | try: 22 | os.chmod(path, mode, follow_symlinks=False) 23 | except (TypeError, NotImplementedError): 24 | # On systems that don't allow permissions on symbolic links, skip 25 | # links entirely. 26 | if not os.path.islink(path): 27 | os.chmod(path, mode) 28 | import configparser 29 | from io import StringIO 30 | import urllib.parse as urlparse 31 | from urllib.parse import quote as urllib_quote 32 | from itertools import zip_longest 33 | from math import log2, ceil 34 | from shlex import quote 35 | from tempfile import TemporaryDirectory 36 | range = range 37 | zip = zip 38 | else: 39 | import ConfigParser as configparser 40 | from cStringIO import StringIO 41 | import urlparse 42 | from urllib import quote as urllib_quote 43 | string_types = basestring, 44 | integer_types = (int, long) 45 | class_types = (type, types.ClassType) 46 | text_type = unicode 47 | binary_type = str 48 | input = raw_input 49 | try: 50 | lchmod = os.lchmod 51 | except AttributeError: 52 | def lchmod(path, mode): 53 | # On systems that don't allow permissions on symbolic links, skip 54 | # links entirely. 55 | if not os.path.islink(path): 56 | os.chmod(path, mode) 57 | from itertools import izip_longest as zip_longest 58 | from math import log 59 | def log2(x): 60 | return log(x, 2) 61 | def ceil(x): 62 | from math import ceil 63 | return int(ceil(x)) 64 | from pipes import quote 65 | 66 | # Modified from http://hg.python.org/cpython/file/3.3/Lib/tempfile.py. Don't 67 | # use the 3.4 one. It uses the new weakref.finalize feature. 68 | import shutil as _shutil 69 | import warnings as _warnings 70 | import os as _os 71 | from tempfile import mkdtemp 72 | range = xrange 73 | from itertools import izip as zip 74 | 75 | class TemporaryDirectory(object): 76 | """Create and return a temporary directory. This has the same 77 | behavior as mkdtemp but can be used as a context manager. For 78 | example: 79 | 80 | with TemporaryDirectory() as tmpdir: 81 | ... 82 | 83 | Upon exiting the context, the directory and everything contained 84 | in it are removed. 85 | """ 86 | 87 | # Handle mkdtemp raising an exception 88 | name = None 89 | _closed = False 90 | 91 | def __init__(self, suffix="", prefix='tmp', dir=None): 92 | self.name = mkdtemp(suffix, prefix, dir) 93 | 94 | def __repr__(self): 95 | return "<{} {!r}>".format(self.__class__.__name__, self.name) 96 | 97 | def __enter__(self): 98 | return self.name 99 | 100 | def cleanup(self, _warn=False, _warnings=_warnings): 101 | if self.name and not self._closed: 102 | try: 103 | _shutil.rmtree(self.name) 104 | except (TypeError, AttributeError) as ex: 105 | if "None" not in '%s' % (ex,): 106 | raise 107 | self._rmtree(self.name) 108 | self._closed = True 109 | if _warn and _warnings.warn: 110 | _warnings.warn("Implicitly cleaning up {!r}".format(self), 111 | ResourceWarning) 112 | 113 | def __exit__(self, exc, value, tb): 114 | self.cleanup() 115 | 116 | def __del__(self): 117 | # Issue a ResourceWarning if implicit cleanup needed 118 | self.cleanup(_warn=True) 119 | 120 | def _rmtree(self, path, _OSError=OSError, _sep=_os.path.sep, 121 | _listdir=_os.listdir, _remove=_os.remove, _rmdir=_os.rmdir): 122 | # Essentially a stripped down version of shutil.rmtree. We can't 123 | # use globals because they may be None'ed out at shutdown. 124 | if not isinstance(path, str): 125 | _sep = _sep.encode() 126 | try: 127 | for name in _listdir(path): 128 | fullname = path + _sep + name 129 | try: 130 | _remove(fullname) 131 | except _OSError: 132 | self._rmtree(fullname) 133 | _rmdir(path) 134 | except _OSError: 135 | pass 136 | 137 | if PY3: 138 | _iterkeys = "keys" 139 | _itervalues = "values" 140 | _iteritems = "items" 141 | else: 142 | _iterkeys = "iterkeys" 143 | _itervalues = "itervalues" 144 | _iteritems = "iteritems" 145 | 146 | 147 | def iterkeys(d): 148 | """Return an iterator over the keys of a dictionary.""" 149 | return iter(getattr(d, _iterkeys)()) 150 | 151 | def itervalues(d): 152 | """Return an iterator over the values of a dictionary.""" 153 | return iter(getattr(d, _itervalues)()) 154 | 155 | def iteritems(d): 156 | """Return an iterator over the (key, value) pairs of a dictionary.""" 157 | return iter(getattr(d, _iteritems)()) 158 | 159 | def get_http_value(u, key): 160 | if PY3: 161 | return u.headers.get(key) 162 | else: 163 | return u.info().getheader(key) 164 | 165 | def with_metaclass(meta, *bases): 166 | """ 167 | Create a base class with a metaclass. 168 | 169 | For example, if you have the metaclass 170 | 171 | >>> class Meta(type): 172 | ... pass 173 | 174 | Use this as the metaclass by doing 175 | 176 | >>> from sympy.core.compatibility import with_metaclass 177 | >>> class MyClass(with_metaclass(Meta, object)): 178 | ... pass 179 | 180 | This is equivalent to the Python 2:: 181 | 182 | class MyClass(object): 183 | __metaclass__ = Meta 184 | 185 | or Python 3:: 186 | 187 | class MyClass(object, metaclass=Meta): 188 | pass 189 | 190 | That is, the first argument is the metaclass, and the remaining arguments 191 | are the base classes. Note that if the base class is just ``object``, you 192 | may omit it. 193 | 194 | >>> MyClass.__mro__ 195 | (, <... 'object'>) 196 | >>> type(MyClass) 197 | 198 | 199 | """ 200 | class metaclass(meta): 201 | __call__ = type.__call__ 202 | __init__ = type.__init__ 203 | def __new__(cls, name, this_bases, d): 204 | if this_bases is None: 205 | return type.__new__(cls, name, (), d) 206 | return meta(name, bases, d) 207 | return metaclass("NewBase", None, {}) 208 | -------------------------------------------------------------------------------- /conda/cli/conda_argparse.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | import sys 10 | import argparse 11 | import os 12 | import subprocess 13 | 14 | from difflib import get_close_matches 15 | 16 | from conda.cli.find_commands import find_commands, find_executable 17 | from conda.cli import common 18 | 19 | build_commands = {'build', 'index', 'skeleton', 'package', 'metapackage', 20 | 'pipbuild', 'develop', 'convert'} 21 | 22 | _ARGCOMPLETE_DEBUG = False 23 | def debug_argcomplete(msg): 24 | # To debug this, replace ttys001 with the fd of the terminal you are using 25 | # (use the `tty` command to find this), and set _ARGCOMPLETE_DEBUG above 26 | # to True. You can also `export _ARC_DEBUG=1` in the shell you are using 27 | # to print debug messages from argcomplete. 28 | if _ARGCOMPLETE_DEBUG: 29 | f = open('/dev/ttys001', 'w') 30 | f.write("\n%s\n" % msg) 31 | f.flush() 32 | 33 | try: 34 | import argcomplete 35 | argcomplete.CompletionFinder 36 | except (ImportError, AttributeError): 37 | # On Python 3.3, argcomplete can be an empty namespace package when 38 | # we are in the conda-recipes directory. 39 | argcomplete = None 40 | 41 | if argcomplete: 42 | class CondaSubprocessCompletionFinder(argcomplete.CompletionFinder): 43 | def __call__(self, argument_parser, **kwargs): 44 | call_super = lambda: super(CondaSubprocessCompletionFinder, self).__call__(argument_parser, **kwargs) 45 | 46 | debug_argcomplete("Working") 47 | 48 | if argument_parser.prog != 'conda': 49 | debug_argcomplete("Argument parser is not conda") 50 | return call_super() 51 | 52 | environ = os.environ.copy() 53 | if 'COMP_LINE' not in environ: 54 | debug_argcomplete("COMP_LINE not in environ") 55 | return call_super() 56 | 57 | subcommands = find_commands() 58 | for subcommand in subcommands: 59 | if 'conda %s' % subcommand in environ['COMP_LINE']: 60 | environ['COMP_LINE'] = environ['COMP_LINE'].replace('conda %s' 61 | % subcommand, 'conda-%s' % subcommand) 62 | debug_argcomplete("Using subprocess") 63 | debug_argcomplete(sys.argv) 64 | import pprint 65 | debug_argcomplete(pprint.pformat(environ)) 66 | args = [find_executable('conda-%s' % subcommand)] 67 | debug_argcomplete(args) 68 | p = subprocess.Popen(args, env=environ, close_fds=False) 69 | p.communicate() 70 | sys.exit() 71 | else: 72 | debug_argcomplete("Not using subprocess") 73 | debug_argcomplete(sys.argv) 74 | debug_argcomplete(argument_parser) 75 | return call_super() 76 | 77 | class ArgumentParser(argparse.ArgumentParser): 78 | def __init__(self, *args, **kwargs): 79 | if not kwargs.get('formatter_class'): 80 | kwargs['formatter_class'] = argparse.RawDescriptionHelpFormatter 81 | if 'add_help' not in kwargs: 82 | add_custom_help = True 83 | kwargs['add_help'] = False 84 | else: 85 | add_custom_help = False 86 | super(ArgumentParser, self).__init__(*args, **kwargs) 87 | 88 | if add_custom_help: 89 | common.add_parser_help(self) 90 | 91 | if self.description: 92 | self.description += "\n\nOptions:\n" 93 | 94 | def _get_action_from_name(self, name): 95 | """Given a name, get the Action instance registered with this parser. 96 | If only it were made available in the ArgumentError object. It is 97 | passed as it's first arg... 98 | """ 99 | container = self._actions 100 | if name is None: 101 | return None 102 | for action in container: 103 | if '/'.join(action.option_strings) == name: 104 | return action 105 | elif action.metavar == name: 106 | return action 107 | elif action.dest == name: 108 | return action 109 | 110 | def error(self, message): 111 | import re 112 | import subprocess 113 | from conda.cli.find_commands import find_executable 114 | 115 | exc = sys.exc_info()[1] 116 | if exc: 117 | # this is incredibly lame, but argparse stupidly does not expose 118 | # reasonable hooks for customizing error handling 119 | if hasattr(exc, 'argument_name'): 120 | argument = self._get_action_from_name(exc.argument_name) 121 | else: 122 | argument = None 123 | if argument and argument.dest == "cmd": 124 | m = re.compile(r"invalid choice: '(\w+)'").match(exc.message) 125 | if m: 126 | cmd = m.group(1) 127 | executable = find_executable('conda-' + cmd) 128 | if not executable: 129 | if cmd in build_commands: 130 | sys.exit("""\ 131 | Error: You need to install conda-build in order to use the 'conda %s' 132 | command. 133 | """ % cmd) 134 | else: 135 | message = "Error: Could not locate 'conda-%s'" % cmd 136 | conda_commands = set(find_commands()) 137 | close = get_close_matches(cmd, 138 | set(argument.choices.keys()) | build_commands | conda_commands) 139 | if close: 140 | message += '\n\nDid you mean one of these?\n' 141 | for s in close: 142 | message += ' %s' % s 143 | sys.exit(message) 144 | args = [find_executable('conda-' + cmd)] 145 | args.extend(sys.argv[2:]) 146 | try: 147 | p = 1 148 | p = subprocess.Popen(args) 149 | p.communicate() 150 | except KeyboardInterrupt: 151 | p.wait() 152 | finally: 153 | sys.exit(p.returncode) 154 | super(ArgumentParser, self).error(message) 155 | 156 | def print_help(self): 157 | super(ArgumentParser, self).print_help() 158 | 159 | if self.prog == 'conda' and sys.argv[1:] in ([], ['help'], ['-h'], ['--help']): 160 | from conda.cli.find_commands import help 161 | help() 162 | 163 | def parse_args(self, *args, **kwargs): 164 | if argcomplete: 165 | CondaSubprocessCompletionFinder()(self) 166 | 167 | return super(ArgumentParser, self).parse_args(*args, **kwargs) 168 | -------------------------------------------------------------------------------- /conda/console.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import sys 4 | import json 5 | import logging 6 | import contextlib 7 | 8 | from conda.utils import memoized 9 | from conda.progressbar import (Bar, ETA, FileTransferSpeed, Percentage, 10 | ProgressBar) 11 | 12 | 13 | fetch_progress = ProgressBar( 14 | widgets=['', ' ', Percentage(), ' ', Bar(), ' ', ETA(), ' ', 15 | FileTransferSpeed()]) 16 | 17 | progress = ProgressBar(widgets=['[%-20s]' % '', '', Bar(), ' ', Percentage()]) 18 | 19 | 20 | class FetchProgressHandler(logging.Handler): 21 | 22 | def emit(self, record): 23 | if record.name == 'fetch.start': 24 | filename, maxval = record.msg 25 | fetch_progress.widgets[0] = filename 26 | fetch_progress.maxval = maxval 27 | fetch_progress.start() 28 | 29 | elif record.name == 'fetch.update': 30 | n = record.msg 31 | fetch_progress.update(n) 32 | 33 | elif record.name == 'fetch.stop': 34 | fetch_progress.finish() 35 | 36 | 37 | class ProgressHandler(logging.Handler): 38 | 39 | def emit(self, record): 40 | if record.name == 'progress.start': 41 | progress.maxval = record.msg 42 | progress.start() 43 | 44 | elif record.name == 'progress.update': 45 | name, n = record.msg 46 | progress.widgets[0] = '[%-20s]' % name 47 | if n == 0: 48 | # Make sure the widget gets updated 49 | progress.start() 50 | progress.update(n) 51 | 52 | elif record.name == 'progress.stop': 53 | progress.widgets[0] = '[ COMPLETE ]' 54 | progress.finish() 55 | 56 | 57 | class JsonFetchProgressHandler(logging.Handler): 58 | def emit(self, record): 59 | if record.name == 'fetch.start': 60 | filename, maxval = record.msg 61 | print(json.dumps({ 62 | 'fetch': filename, 63 | 'maxval': maxval, 64 | 'progress': 0, 65 | 'finished': False 66 | })) 67 | print('\0', end='') 68 | sys.stdout.flush() 69 | self.filename = filename 70 | self.maxval = maxval 71 | 72 | elif record.name == 'fetch.update': 73 | n = record.msg 74 | print(json.dumps({ 75 | 'fetch': self.filename, 76 | 'maxval': self.maxval, 77 | 'progress': n, 78 | 'finished': False 79 | })) 80 | print('\0', end='') 81 | sys.stdout.flush() 82 | 83 | elif record.name == 'fetch.stop': 84 | print(json.dumps({ 85 | 'fetch': self.filename, 86 | 'maxval': self.maxval, 87 | 'progress': self.maxval, 88 | 'finished': True 89 | })) 90 | print('\0', end='') 91 | sys.stdout.flush() 92 | self.filename = None 93 | self.maxval = -1 94 | 95 | 96 | class JsonProgressHandler(logging.Handler): 97 | 98 | def emit(self, record): 99 | if record.name == 'progress.start': 100 | maxval = record.msg 101 | print(json.dumps({ 102 | 'maxval': maxval, 103 | 'progress': 0, 104 | 'finished': False 105 | })) 106 | print('\0', end='') 107 | sys.stdout.flush() 108 | self.maxval = maxval 109 | 110 | elif record.name == 'progress.update': 111 | name, n = record.msg 112 | print(json.dumps({ 113 | 'name': name, 114 | 'maxval': self.maxval, 115 | 'progress': n, 116 | 'finished': False 117 | })) 118 | print('\0', end='') 119 | sys.stdout.flush() 120 | 121 | elif record.name == 'progress.stop': 122 | print(json.dumps({ 123 | 'maxval': self.maxval, 124 | 'progress': self.maxval, 125 | 'finished': True 126 | })) 127 | print('\0', end='') 128 | sys.stdout.flush() 129 | 130 | 131 | class PrintHandler(logging.Handler): 132 | def emit(self, record): 133 | if record.name == 'print': 134 | print(record.msg) 135 | 136 | class DotHandler(logging.Handler): 137 | def emit(self, record): 138 | try: 139 | sys.stdout.write('.') 140 | sys.stdout.flush() 141 | except IOError: 142 | # sys.stdout.flush doesn't work in pythonw 143 | pass 144 | 145 | class SysStdoutWriteHandler(logging.Handler): 146 | def emit(self, record): 147 | try: 148 | sys.stdout.write(record.msg) 149 | sys.stdout.flush() 150 | except IOError: 151 | pass 152 | 153 | 154 | class SysStderrWriteHandler(logging.Handler): 155 | def emit(self, record): 156 | try: 157 | sys.stderr.write(record.msg) 158 | sys.stderr.flush() 159 | except IOError: 160 | pass 161 | 162 | _fetch_prog_handler = FetchProgressHandler() 163 | _prog_handler = ProgressHandler() 164 | 165 | @memoized # to avoid setting up handlers more than once 166 | def setup_verbose_handlers(): 167 | fetch_prog_logger = logging.getLogger('fetch') 168 | fetch_prog_logger.setLevel(logging.INFO) 169 | fetch_prog_logger.addHandler(_fetch_prog_handler) 170 | 171 | prog_logger = logging.getLogger('progress') 172 | prog_logger.setLevel(logging.INFO) 173 | prog_logger.addHandler(_prog_handler) 174 | 175 | print_logger = logging.getLogger('print') 176 | print_logger.setLevel(logging.INFO) 177 | print_logger.addHandler(PrintHandler()) 178 | 179 | @contextlib.contextmanager 180 | def json_progress_bars(): 181 | setup_verbose_handlers() 182 | fetch_prog_logger = logging.getLogger('fetch') 183 | prog_logger = logging.getLogger('progress') 184 | print_logger = logging.getLogger('print') 185 | 186 | # Disable this. Presumably this function is being used in a CLI context 187 | # with --json, so we don't want the logger enabled (but we just 188 | # activated it) 189 | print_logger.setLevel(logging.CRITICAL + 1) 190 | 191 | json_fetch_prog_handler = JsonFetchProgressHandler() 192 | json_prog_handler = JsonProgressHandler() 193 | 194 | fetch_prog_logger.removeHandler(_fetch_prog_handler) 195 | prog_logger.removeHandler(_prog_handler) 196 | fetch_prog_logger.addHandler(json_fetch_prog_handler) 197 | prog_logger.addHandler(json_prog_handler) 198 | 199 | yield 200 | 201 | fetch_prog_logger.removeHandler(json_fetch_prog_handler) 202 | prog_logger.removeHandler(json_prog_handler) 203 | fetch_prog_logger.addHandler(_fetch_prog_handler) 204 | prog_logger.addHandler(_prog_handler) 205 | 206 | @memoized 207 | def setup_handlers(): 208 | dotlogger = logging.getLogger('dotupdate') 209 | dotlogger.setLevel(logging.DEBUG) 210 | dotlogger.addHandler(DotHandler()) 211 | 212 | stdoutlogger = logging.getLogger('stdoutlog') 213 | stdoutlogger.setLevel(logging.DEBUG) 214 | stdoutlogger.addHandler(SysStdoutWriteHandler()) 215 | 216 | stderrlogger = logging.getLogger('stderrlog') 217 | stderrlogger.setLevel(logging.DEBUG) 218 | stderrlogger.addHandler(SysStderrWriteHandler()) 219 | -------------------------------------------------------------------------------- /conda/history.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import os 4 | import re 5 | import sys 6 | import time 7 | from os.path import isdir, isfile, join 8 | import warnings 9 | import errno 10 | import logging 11 | 12 | from conda import install 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | class CondaHistoryException(Exception): 17 | pass 18 | 19 | 20 | class CondaHistoryWarning(Warning): 21 | pass 22 | 23 | 24 | def write_head(fo): 25 | fo.write("==> %s <==\n" % time.strftime('%Y-%m-%d %H:%M:%S')) 26 | fo.write("# cmd: %s\n" % (' '.join(sys.argv))) 27 | 28 | 29 | def is_diff(content): 30 | return any(s.startswith(('-', '+')) for s in content) 31 | 32 | 33 | def pretty_diff(diff): 34 | added = {} 35 | removed = {} 36 | for s in diff: 37 | fn = s[1:] 38 | name, version, unused_build = fn.rsplit('-', 2) 39 | if s.startswith('-'): 40 | removed[name.lower()] = version 41 | elif s.startswith('+'): 42 | added[name.lower()] = version 43 | changed = set(added) & set(removed) 44 | for name in sorted(changed): 45 | yield ' %s {%s -> %s}' % (name, removed[name], added[name]) 46 | for name in sorted(set(removed) - changed): 47 | yield '-%s-%s' % (name, removed[name]) 48 | for name in sorted(set(added) - changed): 49 | yield '+%s-%s' % (name, added[name]) 50 | 51 | 52 | def pretty_content(content): 53 | if is_diff(content): 54 | return pretty_diff(content) 55 | else: 56 | return iter(sorted(content)) 57 | 58 | 59 | class History(object): 60 | 61 | def __init__(self, prefix): 62 | self.prefix = prefix 63 | self.meta_dir = join(prefix, 'conda-meta') 64 | self.path = join(self.meta_dir, 'history') 65 | 66 | def __enter__(self): 67 | self.update() 68 | return self 69 | 70 | def __exit__(self, exc_type, exc_value, traceback): 71 | self.update() 72 | 73 | def init_log_file(self, force=False): 74 | if not force and isfile(self.path): 75 | return 76 | self.write_dists(install.linked(self.prefix)) 77 | 78 | def update(self): 79 | """ 80 | update the history file (creating a new one if necessary) 81 | """ 82 | try: 83 | self.init_log_file() 84 | try: 85 | last = self.get_state() 86 | except CondaHistoryException as e: 87 | warnings.warn("Error in %s: %s" % (self.path, e), 88 | CondaHistoryWarning) 89 | return 90 | curr = set(install.linked(self.prefix)) 91 | if last == curr: 92 | return 93 | self.write_changes(last, curr) 94 | except IOError as e: 95 | if e.errno == errno.EACCES: 96 | log.debug("Can't write the history file") 97 | else: 98 | raise 99 | 100 | def parse(self): 101 | """ 102 | parse the history file and return a list of 103 | tuples(datetime strings, set of distributions/diffs) 104 | """ 105 | res = [] 106 | if not isfile(self.path): 107 | return res 108 | sep_pat = re.compile(r'==>\s*(.+?)\s*<==') 109 | with open(self.path) as f: 110 | lines = f.read().splitlines() 111 | for line in lines: 112 | line = line.strip() 113 | if not line or line.startswith('#'): 114 | continue 115 | m = sep_pat.match(line) 116 | if m: 117 | dt = m.group(1) 118 | res.append((dt, set())) 119 | else: 120 | res[-1][1].add(line) 121 | return res 122 | 123 | def construct_states(self): 124 | """ 125 | return a list of tuples(datetime strings, set of distributions) 126 | """ 127 | res = [] 128 | cur = set([]) 129 | for dt, cont in self.parse(): 130 | if not is_diff(cont): 131 | cur = cont 132 | else: 133 | for s in cont: 134 | if s.startswith('-'): 135 | cur.discard(s[1:]) 136 | elif s.startswith('+'): 137 | cur.add(s[1:]) 138 | else: 139 | raise CondaHistoryException('Did not expect: %s' % s) 140 | res.append((dt, cur.copy())) 141 | return res 142 | 143 | def get_state(self, rev=-1): 144 | """ 145 | return the state, i.e. the set of distributions, for a given revision, 146 | defaults to latest (which is the same as the current state when 147 | the log file is up-to-date) 148 | """ 149 | states = self.construct_states() 150 | if not states: 151 | return set([]) 152 | times, pkgs = zip(*states) 153 | return pkgs[rev] 154 | 155 | def print_log(self): 156 | for i, (date, content) in enumerate(self.parse()): 157 | print('%s (rev %d)' % (date, i)) 158 | for line in pretty_content(content): 159 | print(' %s' % line) 160 | print() 161 | 162 | def object_log(self): 163 | result = [] 164 | for i, (date, content) in enumerate(self.parse()): 165 | # Based on Mateusz's code; provides more details about the 166 | # history event 167 | event = { 168 | 'date': date, 169 | 'rev': i, 170 | 'install': [], 171 | 'remove': [], 172 | 'upgrade': [], 173 | 'downgrade': [] 174 | } 175 | added = {} 176 | removed = {} 177 | if is_diff(content): 178 | for pkg in content: 179 | name, version, build = pkg[1:].rsplit('-', 2) 180 | if pkg.startswith('+'): 181 | added[name.lower()] = (version, build) 182 | elif pkg.startswith('-'): 183 | removed[name.lower()] = (version, build) 184 | 185 | changed = set(added) & set(removed) 186 | for name in sorted(changed): 187 | old = removed[name] 188 | new = added[name] 189 | details = { 190 | 'old': '-'.join((name,) + old), 191 | 'new': '-'.join((name,) + new) 192 | } 193 | 194 | if new > old: 195 | event['upgrade'].append(details) 196 | else: 197 | event['downgrade'].append(details) 198 | 199 | for name in sorted(set(removed) - changed): 200 | event['remove'].append('-'.join((name,) + removed[name])) 201 | 202 | for name in sorted(set(added) - changed): 203 | event['install'].append('-'.join((name,) + added[name])) 204 | else: 205 | for pkg in sorted(content): 206 | event['install'].append(pkg) 207 | result.append(event) 208 | return result 209 | 210 | def write_dists(self, dists): 211 | if not dists: 212 | return 213 | if not isdir(self.meta_dir): 214 | os.makedirs(self.meta_dir) 215 | with open(self.path, 'w') as fo: 216 | write_head(fo) 217 | for dist in sorted(dists): 218 | fo.write('%s\n' % dist) 219 | 220 | def write_changes(self, last_state, current_state): 221 | with open(self.path, 'a') as fo: 222 | write_head(fo) 223 | for fn in sorted(last_state - current_state): 224 | fo.write('-%s\n' % fn) 225 | for fn in sorted(current_state - last_state): 226 | fo.write('+%s\n' % fn) 227 | 228 | if __name__ == '__main__': 229 | with History(sys.prefix) as h: 230 | h.print_log() 231 | -------------------------------------------------------------------------------- /conda/cli/main_remove.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | from os.path import join, exists 10 | 11 | import argparse 12 | from argparse import RawDescriptionHelpFormatter 13 | import errno 14 | import logging 15 | 16 | from conda import config 17 | from conda import plan 18 | from conda.cli import common 19 | from conda.console import json_progress_bars 20 | 21 | 22 | help = "%s a list of packages from a specified conda environment." 23 | descr = help + """ 24 | Normally, only the specified package is removed, and not the packages 25 | which may depend on the package. Hence this command should be used 26 | with caution. Note: conda uninstall is an alias for conda remove. 27 | """ 28 | example = """ 29 | Examples: 30 | 31 | conda %s -n myenv scipy 32 | 33 | """ 34 | 35 | uninstall_help = "Alias for conda remove. See conda remove --help." 36 | log = logging.getLogger(__name__) 37 | 38 | def configure_parser(sub_parsers, name='remove'): 39 | if name == 'remove': 40 | p = sub_parsers.add_parser( 41 | name, 42 | formatter_class=RawDescriptionHelpFormatter, 43 | description=descr % name.capitalize(), 44 | help=help % name.capitalize(), 45 | epilog=example % name, 46 | add_help=False, 47 | ) 48 | else: 49 | p = sub_parsers.add_parser( 50 | name, 51 | formatter_class=RawDescriptionHelpFormatter, 52 | description=uninstall_help, 53 | help=uninstall_help, 54 | epilog=example % name, 55 | add_help=False, 56 | ) 57 | common.add_parser_help(p) 58 | common.add_parser_yes(p) 59 | common.add_parser_json(p) 60 | p.add_argument( 61 | "--all", 62 | action="store_true", 63 | help="%s all packages, i.e., the entire environment." % name.capitalize(), 64 | ) 65 | p.add_argument( 66 | "--features", 67 | action="store_true", 68 | help="%s features (instead of packages)." % name.capitalize(), 69 | ) 70 | common.add_parser_no_pin(p) 71 | common.add_parser_channels(p) 72 | common.add_parser_prefix(p) 73 | common.add_parser_quiet(p) 74 | common.add_parser_use_index_cache(p) 75 | common.add_parser_use_local(p) 76 | common.add_parser_offline(p) 77 | common.add_parser_pscheck(p) 78 | p.add_argument( 79 | 'package_names', 80 | metavar='package_name', 81 | action="store", 82 | nargs='*', 83 | help="Package names to %s from the environment." % name, 84 | ).completer = common.InstalledPackages 85 | p.set_defaults(func=execute) 86 | 87 | 88 | def execute(args, parser): 89 | import sys 90 | 91 | import conda.plan as plan 92 | import conda.instructions as inst 93 | from conda.install import rm_rf, linked 94 | from conda import config 95 | 96 | if not (args.all or args.package_names): 97 | common.error_and_exit('no package names supplied,\n' 98 | ' try "conda remove -h" for more details', 99 | json=args.json, 100 | error_type="ValueError") 101 | 102 | prefix = common.get_prefix(args) 103 | if args.all and prefix == config.default_prefix: 104 | common.error_and_exit("cannot remove current environment. deactivate and run conda remove again") 105 | common.check_write('remove', prefix, json=args.json) 106 | common.ensure_override_channels_requires_channel(args, json=args.json) 107 | channel_urls = args.channel or () 108 | if args.use_local: 109 | from conda.fetch import fetch_index 110 | from conda.utils import url_path 111 | try: 112 | from conda_build.config import croot 113 | except ImportError: 114 | common.error_and_exit("you need to have 'conda-build >= 1.7.1' installed" 115 | " to use the --use-local option", 116 | json=args.json, 117 | error_type="RuntimeError") 118 | # remove the cache such that a refetch is made, 119 | # this is necessary because we add the local build repo URL 120 | fetch_index.cache = {} 121 | if exists(croot): 122 | channel_urls = [url_path(croot)] + list(channel_urls) 123 | index = common.get_index_trap(channel_urls=channel_urls, 124 | prepend=not args.override_channels, 125 | use_cache=args.use_index_cache, 126 | json=args.json, 127 | offline=args.offline) 128 | else: 129 | index = common.get_index_trap(channel_urls=channel_urls, 130 | prepend=not args.override_channels, 131 | use_cache=args.use_index_cache, 132 | json=args.json, 133 | offline=args.offline) 134 | specs = None 135 | if args.features: 136 | features = set(args.package_names) 137 | actions = plan.remove_features_actions(prefix, index, features) 138 | 139 | elif args.all: 140 | if plan.is_root_prefix(prefix): 141 | common.error_and_exit('cannot remove root environment,\n' 142 | ' add -n NAME or -p PREFIX option', 143 | json=args.json, 144 | error_type="CantRemoveRoot") 145 | 146 | actions = {inst.PREFIX: prefix} 147 | for dist in sorted(linked(prefix)): 148 | plan.add_unlink(actions, dist) 149 | 150 | else: 151 | specs = common.specs_from_args(args.package_names) 152 | if (plan.is_root_prefix(prefix) and 153 | common.names_in_specs(common.root_no_rm, specs)): 154 | common.error_and_exit('cannot remove %s from root environment' % 155 | ', '.join(common.root_no_rm), 156 | json=args.json, 157 | error_type="CantRemoveFromRoot") 158 | actions = plan.remove_actions(prefix, specs, index=index, pinned=args.pinned) 159 | 160 | if plan.nothing_to_do(actions): 161 | if args.all: 162 | rm_rf(prefix) 163 | 164 | if args.json: 165 | common.stdout_json({ 166 | 'success': True, 167 | 'actions': actions 168 | }) 169 | return 170 | common.error_and_exit('no packages found to remove from ' 171 | 'environment: %s' % prefix, 172 | json=args.json, 173 | error_type="PackageNotInstalled") 174 | 175 | if not args.json: 176 | print() 177 | print("Package plan for package removal in environment %s:" % prefix) 178 | plan.display_actions(actions, index) 179 | 180 | if args.json and args.dry_run: 181 | common.stdout_json({ 182 | 'success': True, 183 | 'dry_run': True, 184 | 'actions': actions 185 | }) 186 | return 187 | 188 | 189 | if not args.json: 190 | common.confirm_yn(args) 191 | 192 | if args.json and not args.quiet: 193 | with json_progress_bars(): 194 | plan.execute_actions(actions, index, verbose=not args.quiet) 195 | else: 196 | plan.execute_actions(actions, index, verbose=not args.quiet) 197 | if specs: 198 | try: 199 | with open(join(prefix, 'conda-meta', 'history'), 'a') as f: 200 | f.write('# remove specs: %s\n' % specs) 201 | except IOError as e: 202 | if e.errno == errno.EACCES: 203 | log.debug("Can't write the history file") 204 | else: 205 | raise 206 | 207 | if args.all: 208 | rm_rf(prefix) 209 | 210 | if args.json: 211 | common.stdout_json({ 212 | 'success': True, 213 | 'actions': actions 214 | }) 215 | -------------------------------------------------------------------------------- /conda/_version.py: -------------------------------------------------------------------------------- 1 | # (c) 2012 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | IN_LONG_VERSION_PY = True 10 | # This file helps to compute a version number in source trees obtained from 11 | # git-archive tarball (such as those provided by github's download-from-tag 12 | # feature). Distribution tarballs (build by setup.py sdist) and build 13 | # directories (produced by setup.py build) will contain a much shorter file 14 | # that just contains the computed version number. 15 | 16 | # This file is released into the public domain. Generated by 17 | # versioneer-0.7+ (https://github.com/warner/python-versioneer) 18 | 19 | # these strings will be replaced by git during git-archive 20 | git_refnames = " (HEAD -> master)" 21 | git_full = "aaa9d1127f19dc80dccee141d86243c8fb672c94" 22 | 23 | 24 | import subprocess 25 | import sys 26 | import re 27 | import os.path 28 | 29 | def run_command(args, cwd=None, verbose=False): 30 | try: 31 | # remember shell=False, so use git.cmd on windows, not just git 32 | p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd) 33 | except EnvironmentError: 34 | e = sys.exc_info()[1] 35 | if verbose: 36 | print("unable to run %s" % args[0]) 37 | print(e) 38 | return None 39 | stdout = p.communicate()[0].strip() 40 | if sys.version >= '3': 41 | stdout = stdout.decode() 42 | if p.returncode != 0: 43 | if verbose: 44 | print("unable to run %s (error)" % args[0]) 45 | return None 46 | return stdout 47 | 48 | def get_expanded_variables(versionfile_source): 49 | # the code embedded in _version.py can just fetch the value of these 50 | # variables. When used from setup.py, we don't want to import 51 | # _version.py, so we do it with a regexp instead. This function is not 52 | # used from _version.py. 53 | variables = {} 54 | try: 55 | for line in open(versionfile_source,"r").readlines(): 56 | if line.strip().startswith("git_refnames ="): 57 | mo = re.search(r'=\s*"(.*)"', line) 58 | if mo: 59 | variables["refnames"] = mo.group(1) 60 | if line.strip().startswith("git_full ="): 61 | mo = re.search(r'=\s*"(.*)"', line) 62 | if mo: 63 | variables["full"] = mo.group(1) 64 | except EnvironmentError: 65 | pass 66 | return variables 67 | 68 | def versions_from_expanded_variables(variables, tag_prefix, verbose=False): 69 | refnames = variables["refnames"].strip() 70 | if refnames.startswith("$Format"): 71 | if verbose: 72 | print("variables are unexpanded, not using") 73 | return {} # unexpanded, so not in an unpacked git-archive tarball 74 | refs = set([r.strip() for r in refnames.strip("()").split(",")]) 75 | for ref in list(refs): 76 | if not re.search(r'\d', ref): 77 | if verbose: 78 | print("discarding '%s', no digits" % ref) 79 | refs.discard(ref) 80 | # Assume all version tags have a digit. git's %d expansion 81 | # behaves like git log --decorate=short and strips out the 82 | # refs/heads/ and refs/tags/ prefixes that would let us 83 | # distinguish between branches and tags. By ignoring refnames 84 | # without digits, we filter out many common branch names like 85 | # "release" and "stabilization", as well as "HEAD" and "master". 86 | if verbose: 87 | print("remaining refs: %s" % ",".join(sorted(refs))) 88 | for ref in sorted(refs): 89 | # sorting will prefer e.g. "2.0" over "2.0rc1" 90 | if ref.startswith(tag_prefix): 91 | r = ref[len(tag_prefix):] 92 | if verbose: 93 | print("picking %s" % r) 94 | return { "version": r, 95 | "full": variables["full"].strip() } 96 | # no suitable tags, so we use the full revision id 97 | if verbose: 98 | print("no suitable tags, using full revision id") 99 | return { "version": variables["full"].strip(), 100 | "full": variables["full"].strip() } 101 | 102 | def versions_from_vcs(tag_prefix, versionfile_source, verbose=False): 103 | # this runs 'git' from the root of the source tree. That either means 104 | # someone ran a setup.py command (and this code is in versioneer.py, so 105 | # IN_LONG_VERSION_PY=False, thus the containing directory is the root of 106 | # the source tree), or someone ran a project-specific entry point (and 107 | # this code is in _version.py, so IN_LONG_VERSION_PY=True, thus the 108 | # containing directory is somewhere deeper in the source tree). This only 109 | # gets called if the git-archive 'subst' variables were *not* expanded, 110 | # and _version.py hasn't already been rewritten with a short version 111 | # string, meaning we're inside a checked out source tree. 112 | 113 | try: 114 | here = os.path.abspath(__file__) 115 | except NameError: 116 | # some py2exe/bbfreeze/non-CPython implementations don't do __file__ 117 | return {} # not always correct 118 | 119 | # versionfile_source is the relative path from the top of the source tree 120 | # (where the .git directory might live) to this file. Invert this to find 121 | # the root from __file__. 122 | root = here 123 | if IN_LONG_VERSION_PY: 124 | for i in range(len(versionfile_source.split("/"))): 125 | root = os.path.dirname(root) 126 | else: 127 | root = os.path.dirname(here) 128 | if not os.path.exists(os.path.join(root, ".git")): 129 | if verbose: 130 | print("no .git in %s" % root) 131 | return {} 132 | 133 | GIT = "git" 134 | if sys.platform == "win32": 135 | GIT = "git.exe" 136 | stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"], 137 | cwd=root) 138 | if stdout is None: 139 | return {} 140 | if not stdout.startswith(tag_prefix): 141 | if verbose: 142 | print("tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix)) 143 | return {} 144 | tag = stdout[len(tag_prefix):] 145 | stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root) 146 | if stdout is None: 147 | return {} 148 | full = stdout.strip() 149 | if tag.endswith("-dirty"): 150 | full += "-dirty" 151 | return {"version": tag, "full": full} 152 | 153 | 154 | def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False): 155 | if IN_LONG_VERSION_PY: 156 | # We're running from _version.py. If it's from a source tree 157 | # (execute-in-place), we can work upwards to find the root of the 158 | # tree, and then check the parent directory for a version string. If 159 | # it's in an installed application, there's no hope. 160 | try: 161 | here = os.path.abspath(__file__) 162 | except NameError: 163 | # py2exe/bbfreeze/non-CPython don't have __file__ 164 | return {} # without __file__, we have no hope 165 | # versionfile_source is the relative path from the top of the source 166 | # tree to _version.py. Invert this to find the root from __file__. 167 | root = here 168 | for i in range(len(versionfile_source.split("/"))): 169 | root = os.path.dirname(root) 170 | else: 171 | # we're running from versioneer.py, which means we're running from 172 | # the setup.py in a source tree. sys.argv[0] is setup.py in the root. 173 | here = os.path.abspath(sys.argv[0]) 174 | root = os.path.dirname(here) 175 | 176 | # Source tarballs conventionally unpack into a directory that includes 177 | # both the project name and a version string. 178 | dirname = os.path.basename(root) 179 | if not dirname.startswith(parentdir_prefix): 180 | if verbose: 181 | print("guessing rootdir is '%s', but '%s' doesn't start with prefix '%s'" % 182 | (root, dirname, parentdir_prefix)) 183 | return None 184 | return {"version": dirname[len(parentdir_prefix):], "full": ""} 185 | 186 | tag_prefix = "" 187 | parentdir_prefix = "conda-" 188 | versionfile_source = "conda/_version.py" 189 | 190 | def get_versions(default={"version": "unknown", "full": ""}, verbose=False): 191 | variables = { "refnames": git_refnames, "full": git_full } 192 | ver = versions_from_expanded_variables(variables, tag_prefix, verbose) 193 | if not ver: 194 | ver = versions_from_vcs(tag_prefix, versionfile_source, verbose) 195 | if not ver: 196 | ver = versions_from_parentdir(parentdir_prefix, versionfile_source, 197 | verbose) 198 | if not ver: 199 | ver = default 200 | return ver 201 | 202 | -------------------------------------------------------------------------------- /conda/cli/main.py: -------------------------------------------------------------------------------- 1 | # (c) Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | '''conda is a tool for managing environments and packages. 7 | 8 | conda provides the following commands: 9 | 10 | Information 11 | =========== 12 | 13 | info : display information about the current install 14 | list : list packages linked into a specified environment 15 | search : print information about a specified package 16 | help : display a list of available conda commands and their help 17 | strings 18 | 19 | Package Management 20 | ================== 21 | 22 | create : create a new conda environment from a list of specified 23 | packages 24 | install : install new packages into an existing conda environment 25 | update : update packages in a specified conda environment 26 | 27 | 28 | Packaging 29 | ========= 30 | 31 | build : build a package from recipe 32 | package : create a conda package in an environment 33 | index : updates repodata.json in channel directories 34 | 35 | Additional help for each command can be accessed by using: 36 | 37 | conda -h 38 | ''' 39 | 40 | from __future__ import print_function, division, absolute_import 41 | 42 | import sys 43 | 44 | def main(): 45 | if len(sys.argv) > 1: 46 | argv1 = sys.argv[1] 47 | if argv1 in ('..activate', '..deactivate', '..activateroot', '..checkenv'): 48 | import conda.cli.activate as activate 49 | activate.main() 50 | return 51 | if argv1 in ('..changeps1'): 52 | import conda.cli.misc as misc 53 | misc.main() 54 | return 55 | if argv1 == 'pip': 56 | sys.exit("""ERROR: 57 | The "conda pip" command has been removed from conda (as of version 1.8) for 58 | the following reasons: 59 | * users get the wrong impression that you *must* use conda pip (instead 60 | of simply pip) when using Anaconda 61 | * there should only be one preferred way to build packages, and that is 62 | the conda build command 63 | * the command did too many things at once, i.e. build a package and 64 | then also install it 65 | * the command is Python centric, whereas conda (from a package management 66 | perspective) is Python agnostic 67 | * packages created with conda pip are not robust, i.e. they will maybe 68 | not work on other people's systems 69 | 70 | In short: 71 | * use "conda build" if you want to build a conda package 72 | * use "conda install" if you want to install something 73 | * use "pip" if you want to install something that is on PyPI for which there 74 | isn't a conda package. 75 | """) 76 | if argv1 in ('activate', 'deactivate'): 77 | sys.stderr.write("Error: '%s' is not a conda command.\n" % argv1) 78 | if sys.platform != 'win32': 79 | sys.stderr.write('Did you mean "source %s" ?\n' % 80 | ' '.join(sys.argv[1:])) 81 | sys.exit(1) 82 | 83 | # for backwards compatibility of conda-api 84 | if sys.argv[1:4] == ['share', '--json', '--prefix']: 85 | import json 86 | from os.path import abspath 87 | from conda.share import old_create_bundle 88 | prefix = sys.argv[4] 89 | path, warnings = old_create_bundle(abspath(prefix)) 90 | json.dump(dict(path=path, warnings=warnings), 91 | sys.stdout, indent=2, sort_keys=True) 92 | return 93 | if sys.argv[1:4] == ['clone', '--json', '--prefix']: 94 | import json 95 | from os.path import abspath 96 | from conda.share import old_clone_bundle 97 | prefix, path = sys.argv[4:6] 98 | old_clone_bundle(path, abspath(prefix)) 99 | json.dump(dict(warnings=[]), sys.stdout, indent=2) 100 | return 101 | 102 | if len(sys.argv) == 1: 103 | sys.argv.append('-h') 104 | 105 | import logging 106 | from conda.cli import conda_argparse 107 | import argparse 108 | import conda 109 | 110 | p = conda_argparse.ArgumentParser( 111 | description='conda is a tool for managing and deploying applications, environments and packages.' 112 | ) 113 | p.add_argument( 114 | '-V', '--version', 115 | action='version', 116 | version='conda %s' % conda.__version__, 117 | help="Show the conda version number and exit." 118 | ) 119 | p.add_argument( 120 | "--debug", 121 | action = "store_true", 122 | help = "Show debug output." 123 | ) 124 | p.add_argument( 125 | "--json", 126 | action = "store_true", 127 | help = argparse.SUPPRESS, 128 | ) 129 | sub_parsers = p.add_subparsers( 130 | metavar = 'command', 131 | dest = 'cmd', 132 | ) 133 | 134 | from conda.cli import main_info 135 | main_info.configure_parser(sub_parsers) 136 | from conda.cli import main_help 137 | main_help.configure_parser(sub_parsers) 138 | from conda.cli import main_list 139 | main_list.configure_parser(sub_parsers) 140 | from conda.cli import main_search 141 | main_search.configure_parser(sub_parsers) 142 | from conda.cli import main_create 143 | main_create.configure_parser(sub_parsers) 144 | from conda.cli import main_install 145 | main_install.configure_parser(sub_parsers) 146 | from conda.cli import main_update 147 | main_update.configure_parser(sub_parsers) 148 | from conda.cli import main_remove 149 | main_remove.configure_parser(sub_parsers) 150 | main_remove.configure_parser(sub_parsers, name='uninstall') 151 | from conda.cli import main_run 152 | main_run.configure_parser(sub_parsers) 153 | from conda.cli import main_config 154 | main_config.configure_parser(sub_parsers) 155 | from conda.cli import main_init 156 | main_init.configure_parser(sub_parsers) 157 | from conda.cli import main_clean 158 | main_clean.configure_parser(sub_parsers) 159 | from conda.cli import main_package 160 | main_package.configure_parser(sub_parsers) 161 | from conda.cli import main_bundle 162 | main_bundle.configure_parser(sub_parsers) 163 | 164 | from conda.cli.find_commands import find_commands 165 | sub_parsers.completer = lambda prefix, **kwargs: [i for i in 166 | list(sub_parsers.choices) + find_commands() if i.startswith(prefix)] 167 | args = p.parse_args() 168 | 169 | if getattr(args, 'json', False): 170 | # Silence logging info to avoid interfering with JSON output 171 | for logger in logging.Logger.manager.loggerDict: 172 | if logger not in ('fetch', 'progress'): 173 | logging.getLogger(logger).setLevel(logging.CRITICAL + 1) 174 | 175 | if args.debug: 176 | logging.disable(logging.NOTSET) 177 | logging.basicConfig(level=logging.DEBUG) 178 | 179 | if (not main_init.is_initialized() and 180 | 'init' not in sys.argv and 'info' not in sys.argv): 181 | if hasattr(args, 'name') and hasattr(args, 'prefix'): 182 | import conda.config as config 183 | from conda.cli import common 184 | if common.get_prefix(args) == config.root_dir: 185 | sys.exit("""\ 186 | Error: This installation of conda is not initialized. Use 'conda create -n 187 | envname' to create a conda environment and 'source activate envname' to 188 | activate it. 189 | 190 | # Note that pip installing conda is not the recommended way for setting up your 191 | # system. The recommended way for setting up a conda system is by installing 192 | # Miniconda, see: http://repo.continuum.io/miniconda/index.html""") 193 | 194 | args_func(args, p) 195 | 196 | def args_func(args, p): 197 | from conda.cli import common 198 | 199 | use_json = getattr(args, 'json', False) 200 | try: 201 | args.func(args, p) 202 | except RuntimeError as e: 203 | if 'maximum recursion depth exceeded' in str(e): 204 | print_issue_message(e, use_json=use_json) 205 | raise 206 | common.error_and_exit(str(e), json=use_json) 207 | except Exception as e: 208 | print_issue_message(e, use_json=use_json) 209 | raise # as if we did not catch it 210 | 211 | def print_issue_message(e, use_json=False): 212 | from conda.cli import common 213 | if e.__class__.__name__ not in ('ScannerError', 'ParserError'): 214 | message = """\ 215 | An unexpected error has occurred, please consider sending the 216 | following traceback to the conda GitHub issue tracker at: 217 | 218 | https://github.com/conda/conda/issues 219 | 220 | Include the output of the command 'conda info' in your report. 221 | 222 | """ 223 | if use_json: 224 | import traceback 225 | common.error_and_exit(message + traceback.format_exc(), 226 | error_type="UnexpectedError", json=True) 227 | print(message) 228 | 229 | if __name__ == '__main__': 230 | main() 231 | -------------------------------------------------------------------------------- /utils/win_batlink.py: -------------------------------------------------------------------------------- 1 | # UNUSED MODULE 2 | """ 3 | Generate batch scripts to allow conda to update Python in the root 4 | environment in Windows. conda cannot do this because it itself runs in 5 | Python, and Windows will not allow writing to a dll that is open. 6 | 7 | The scripts should remain as small as possible. Only those things that conda 8 | itself cannot do should be in them. The rest should be done by conda. 9 | 10 | The way this works is that when conda comes to an action that it cannot 11 | perform (such as linking Python in the root environment), it serializes the 12 | actions that it cannot perform into a batch script (that's what's done here in 13 | this module), performs the rest of the actions that it can perform, calls this 14 | script, and exits (see conda.plan.execute_plan()). 15 | 16 | Implementation wise, the action serialization is just a custom batch file that 17 | does the linking/unlinking of everything in the package list, written to 18 | %PREFIX%\batlink.bat (note, we can assume that we have write permissions to 19 | %PREFIX% because otherwise we wouldn't be able to install in the root 20 | environment anyway (this issue only comes up when installing into the root 21 | environment)). conda calls this script and exits. 22 | 23 | Notes: 24 | 25 | - `mklink /H` creates a hardlink on Windows NT 6.0 and later (i.e., Windows 26 | Vista or later) 27 | - On older systems, like Windows XP, `fsutil.exe hardlink create` creates 28 | hard links. 29 | - In either case, the order of the arguments is backwards: dest source 30 | 31 | """ 32 | 33 | from os.path import join, abspath, split 34 | from distutils.spawn import find_executable 35 | 36 | # Redirect stderr on the mkdirs to ignore errors about directories that 37 | # already exist 38 | BAT_LINK_HEADER = """\ 39 | 40 | {mkdirs} 41 | 42 | 43 | {links} 44 | 45 | """ 46 | 47 | # Hide stderr for this one because it warns about nonempty directories, like 48 | # C:\Anaconda. 49 | BAT_UNLINK_HEADER = """\ 50 | {filedeletes} 51 | 52 | {dirdeletes} 53 | 54 | """ 55 | 56 | WINXP_LINK = "fsutil.exe hardlink create {dest} {source}" 57 | 58 | WINVISTA_LINK = "mklink /H {dest} {source}" 59 | 60 | MAKE_DIR = "mkdir {dst_dir}" 61 | 62 | FILE_DELETE = "del /Q {dest}" 63 | 64 | DIR_DELETE = "rmdir /Q {dest}" 65 | 66 | 67 | def make_bat_link(files, prefix, dist_dir): 68 | links = [] 69 | has_mklink = find_executable('mklink') 70 | LINK = WINVISTA_LINK if has_mklink else WINXP_LINK 71 | dirs = set() 72 | for file in files: 73 | source = abspath(join(dist_dir, file)) 74 | fdn, fbn = split(file) 75 | dst_dir = join(prefix, fdn) 76 | dirs.add(abspath(dst_dir)) 77 | dest = abspath(join(dst_dir, fbn)) 78 | links.append(LINK.format(source=source, dest=dest)) 79 | 80 | # mkdir will make intermediate directories, so we do not need to care 81 | # about the order 82 | mkdirs = [MAKE_DIR.format(dst_dir=dn) for dn in dirs] 83 | 84 | batchfile = BAT_LINK_HEADER.format(links='\n'.join(links), 85 | mkdirs='\n'.join(mkdirs)) 86 | 87 | return batchfile 88 | 89 | 90 | def make_bat_unlink(files, directories, prefix, dist_dir): 91 | filedeletes = [FILE_DELETE.format(dest=abspath(file)) for file in files] 92 | dirdeletes = [DIR_DELETE.format(dest=abspath(dir)) for dir in directories] 93 | batchfile = BAT_UNLINK_HEADER.format(filedeletes='\n'.join(filedeletes), 94 | dirdeletes='\n'.join(dirdeletes)) 95 | 96 | return batchfile 97 | 98 | 99 | def should_do_win_subprocess(cmd, arg, prefix): 100 | """ 101 | If the cmd needs to call out to a separate process on Windows (because the 102 | Windows file lock prevents Python from updating itself). 103 | """ 104 | return ( 105 | cmd in ('LINK', 'UNLINK') and 106 | install.on_win and 107 | abspath(prefix) == abspath(sys.prefix) and 108 | arg.rsplit('-', 2)[0] in install.win_ignore 109 | ) 110 | 111 | def win_subprocess_re_sort(plan, prefix): 112 | # TODO: Fix the progress numbers 113 | newplan = [] 114 | winplan = [] 115 | for line in plan: 116 | cmd_arg = cmds_from_plan([line]) 117 | if cmd_arg: 118 | [[cmd, arg]] = cmd_arg 119 | else: 120 | continue 121 | if should_do_win_subprocess(cmd, arg, prefix=prefix): 122 | if cmd == LINK: 123 | # The one post-link action that we need to worry about 124 | newplan.append("CREATEMETA %s" % arg) 125 | winplan.append(line) 126 | else: 127 | newplan.append(line) 128 | 129 | return newplan, winplan 130 | 131 | 132 | def test_win_subprocess(prefix): 133 | """ 134 | Make sure the windows subprocess stuff will work before we try it. 135 | """ 136 | import subprocess 137 | from conda.win_batlink import make_bat_link, make_bat_unlink 138 | from conda.builder.utils import rm_rf 139 | 140 | try: 141 | print("Testing if we can install certain packages") 142 | batfiles = ['ping 1.1.1.1 -n 1 -w 3000 > nul'] 143 | dist_dir = join(config.pkgs_dir, 'battest_pkg', 'battest') 144 | 145 | # First create a file in the prefix. 146 | print("making file in the prefix") 147 | prefix_battest = join(prefix, 'battest') 148 | print("making directories") 149 | os.makedirs(join(prefix, 'battest')) 150 | print("making file") 151 | with open(join(prefix_battest, 'battest1'), 'w') as f: 152 | f.write('test1') 153 | print("testing file") 154 | with open(join(prefix_battest, 'battest1')) as f: 155 | assert f.read() == 'test1' 156 | 157 | # Now unlink it. 158 | print("making unlink command") 159 | batfiles.append(make_bat_unlink([join(prefix_battest, 'battest1')], 160 | [prefix_battest], prefix, dist_dir)) 161 | 162 | # Now create a file in the pkgs dir 163 | print("making file in pkgs dir") 164 | print("making directories") 165 | os.makedirs(join(dist_dir, 'battest')) 166 | print("making file") 167 | with open(join(dist_dir, 'battest', 'battest2'), 'w') as f: 168 | f.write('test2') 169 | print("testing file") 170 | with open(join(dist_dir, 'battest', 'battest2')) as f: 171 | assert f.read() == 'test2' 172 | 173 | # And link it 174 | print("making link command") 175 | batfiles.append(make_bat_link([join('battest', 'battest2')], 176 | prefix, dist_dir)) 177 | 178 | batfile = '\n'.join(batfiles) 179 | 180 | print("writing batlink_test.bat file") 181 | with open(join(prefix, 'batlink_test.bat'), 'w') as f: 182 | f.write(batfile) 183 | print("running batlink_test.bat file") 184 | subprocess.check_call([join(prefix, 'batlink_test.bat')]) 185 | 186 | print("testing result") 187 | print("testing if old file does not exist") 188 | assert not os.path.exists(join(prefix_battest, 'battest1')) 189 | print("testing if new file does exist") 190 | assert os.path.exists(join(prefix_battest, 'battest2')) 191 | print("testing content of installed file") 192 | with open(join(prefix_battest, 'battest2')) as f: 193 | assert f.read() == 'test2' 194 | print("testing content of pkg file") 195 | with open(join(dist_dir, 'battest', 'battest2')) as f: 196 | assert f.read() == 'test2' 197 | 198 | finally: 199 | try: 200 | print("cleaning up") 201 | rm_rf(join(prefix, 'battest')) 202 | rm_rf(join(config.pkgs_dir, 'battest_pkg')) 203 | rm_rf(join(prefix, 'batlink_test.bat')) 204 | except Exception as e: 205 | print(e) 206 | 207 | 208 | def win_subprocess_write_bat(cmd, arg, prefix, plan): 209 | assert sys.platform == 'win32' 210 | 211 | import json 212 | from conda.win_batlink import make_bat_link, make_bat_unlink 213 | 214 | dist_dir = join(config.pkgs_dir, arg) 215 | info_dir = join(dist_dir, 'info') 216 | 217 | if cmd == LINK: 218 | files = list(install.yield_lines(join(info_dir, 'files'))) 219 | 220 | return make_bat_link(files, prefix, dist_dir) 221 | 222 | elif cmd == UNLINK: 223 | meta_path = join(prefix, 'conda-meta', arg + '.json') 224 | with open(meta_path) as fi: 225 | meta = json.load(fi) 226 | 227 | files = set([]) 228 | directories1 = set([]) 229 | for f in meta['files']: 230 | dst = abspath(join(prefix, f)) 231 | files.add(dst) 232 | directories1.add(dirname(dst)) 233 | files.add(meta_path) 234 | 235 | directories = set([]) 236 | for path in directories1: 237 | while len(path) > len(prefix): 238 | directories.add(path) 239 | path = dirname(path) 240 | directories.add(join(prefix, 'conda-meta')) 241 | directories.add(prefix) 242 | 243 | directories = sorted(directories, key=len, reverse=True) 244 | 245 | return make_bat_unlink(files, directories, prefix, dist_dir) 246 | else: 247 | raise ValueError 248 | 249 | 250 | def do_win_subprocess(batfile, prefix): 251 | import subprocess 252 | with open(join(prefix, 'batlink.bat'), 'w') as f: 253 | f.write(batfile) 254 | print("running subprocess") 255 | subprocess.Popen([join(prefix, 'batlink.bat')]) 256 | # If we ever hit a race condition, maybe we should use atexit 257 | sys.exit(0) 258 | -------------------------------------------------------------------------------- /conda/progressbar/widgets.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # progressbar - Text progress bar library for Python. 5 | # Copyright (c) 2005 Nilton Volpato 6 | # 7 | # This library is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU Lesser General Public 9 | # License as published by the Free Software Foundation; either 10 | # version 2.1 of the License, or (at your option) any later version. 11 | # 12 | # This library is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public 18 | # License along with this library; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | '''Default ProgressBar widgets''' 22 | 23 | from __future__ import print_function, division, absolute_import 24 | 25 | import datetime 26 | import math 27 | 28 | try: 29 | from abc import ABCMeta, abstractmethod 30 | abstractmethod; # silence pyflakes 31 | except ImportError: 32 | AbstractWidget = object 33 | abstractmethod = lambda fn: fn 34 | else: 35 | AbstractWidget = ABCMeta('AbstractWidget', (object,), {}) 36 | 37 | 38 | def format_updatable(updatable, pbar): 39 | if hasattr(updatable, 'update'): return updatable.update(pbar) 40 | else: return updatable 41 | 42 | 43 | class Widget(AbstractWidget): 44 | '''The base class for all widgets 45 | 46 | The ProgressBar will call the widget's update value when the widget should 47 | be updated. The widget's size may change between calls, but the widget may 48 | display incorrectly if the size changes drastically and repeatedly. 49 | 50 | The boolean TIME_SENSITIVE informs the ProgressBar that it should be 51 | updated more often because it is time sensitive. 52 | ''' 53 | 54 | TIME_SENSITIVE = False 55 | __slots__ = () 56 | 57 | @abstractmethod 58 | def update(self, pbar): 59 | '''Updates the widget. 60 | 61 | pbar - a reference to the calling ProgressBar 62 | ''' 63 | 64 | 65 | class WidgetHFill(Widget): 66 | '''The base class for all variable width widgets. 67 | 68 | This widget is much like the \\hfill command in TeX, it will expand to 69 | fill the line. You can use more than one in the same line, and they will 70 | all have the same width, and together will fill the line. 71 | ''' 72 | 73 | @abstractmethod 74 | def update(self, pbar, width): 75 | '''Updates the widget providing the total width the widget must fill. 76 | 77 | pbar - a reference to the calling ProgressBar 78 | width - The total width the widget must fill 79 | ''' 80 | 81 | 82 | class Timer(Widget): 83 | 'Widget which displays the elapsed seconds.' 84 | 85 | __slots__ = ('format',) 86 | TIME_SENSITIVE = True 87 | 88 | def __init__(self, format='Elapsed Time: %s'): 89 | self.format = format 90 | 91 | @staticmethod 92 | def format_time(seconds): 93 | 'Formats time as the string "HH:MM:SS".' 94 | 95 | return str(datetime.timedelta(seconds=int(seconds))) 96 | 97 | 98 | def update(self, pbar): 99 | 'Updates the widget to show the elapsed time.' 100 | 101 | return self.format % self.format_time(pbar.seconds_elapsed) 102 | 103 | 104 | class ETA(Timer): 105 | 'Widget which attempts to estimate the time of arrival.' 106 | 107 | TIME_SENSITIVE = True 108 | 109 | def update(self, pbar): 110 | 'Updates the widget to show the ETA or total time when finished.' 111 | 112 | if pbar.currval == 0: 113 | return 'ETA: --:--:--' 114 | elif pbar.finished: 115 | return 'Time: %s' % self.format_time(pbar.seconds_elapsed) 116 | else: 117 | elapsed = pbar.seconds_elapsed 118 | eta = elapsed * pbar.maxval / pbar.currval - elapsed 119 | return 'ETA: %s' % self.format_time(eta) 120 | 121 | 122 | class FileTransferSpeed(Widget): 123 | 'Widget for showing the transfer speed (useful for file transfers).' 124 | 125 | prefixes = ' kMGTPEZY' 126 | __slots__ = ('unit', 'format') 127 | 128 | def __init__(self, unit='B'): 129 | self.unit = unit 130 | self.format = '%6.2f %s%s/s' 131 | 132 | def update(self, pbar): 133 | 'Updates the widget with the current SI prefixed speed.' 134 | 135 | if pbar.seconds_elapsed < 2e-6 or pbar.currval < 2e-6: # =~ 0 136 | scaled = power = 0 137 | else: 138 | speed = pbar.currval / pbar.seconds_elapsed 139 | power = int(math.log(speed, 1000)) 140 | scaled = speed / 1000.**power 141 | 142 | return self.format % (scaled, self.prefixes[power], self.unit) 143 | 144 | 145 | class AnimatedMarker(Widget): 146 | '''An animated marker for the progress bar which defaults to appear as if 147 | it were rotating. 148 | ''' 149 | 150 | __slots__ = ('markers', 'curmark') 151 | 152 | def __init__(self, markers='|/-\\'): 153 | self.markers = markers 154 | self.curmark = -1 155 | 156 | def update(self, pbar): 157 | '''Updates the widget to show the next marker or the first marker when 158 | finished''' 159 | 160 | if pbar.finished: return self.markers[0] 161 | 162 | self.curmark = (self.curmark + 1) % len(self.markers) 163 | return self.markers[self.curmark] 164 | 165 | # Alias for backwards compatibility 166 | RotatingMarker = AnimatedMarker 167 | 168 | 169 | class Counter(Widget): 170 | 'Displays the current count' 171 | 172 | __slots__ = ('format',) 173 | 174 | def __init__(self, format='%d'): 175 | self.format = format 176 | 177 | def update(self, pbar): 178 | return self.format % pbar.currval 179 | 180 | 181 | class Percentage(Widget): 182 | 'Displays the current percentage as a number with a percent sign.' 183 | 184 | def update(self, pbar): 185 | return '%3d%%' % pbar.percentage() 186 | 187 | 188 | class FormatLabel(Timer): 189 | 'Displays a formatted label' 190 | 191 | mapping = { 192 | 'elapsed': ('seconds_elapsed', Timer.format_time), 193 | 'finished': ('finished', None), 194 | 'last_update': ('last_update_time', None), 195 | 'max': ('maxval', None), 196 | 'seconds': ('seconds_elapsed', None), 197 | 'start': ('start_time', None), 198 | 'value': ('currval', None) 199 | } 200 | 201 | __slots__ = ('format',) 202 | def __init__(self, format): 203 | self.format = format 204 | 205 | def update(self, pbar): 206 | context = {} 207 | for name, (key, transform) in self.mapping.items(): 208 | try: 209 | value = getattr(pbar, key) 210 | 211 | if transform is None: 212 | context[name] = value 213 | else: 214 | context[name] = transform(value) 215 | except: pass 216 | 217 | return self.format % context 218 | 219 | 220 | class SimpleProgress(Widget): 221 | 'Returns progress as a count of the total (e.g.: "5 of 47")' 222 | 223 | __slots__ = ('sep',) 224 | 225 | def __init__(self, sep=' of '): 226 | self.sep = sep 227 | 228 | def update(self, pbar): 229 | return '%d%s%d' % (pbar.currval, self.sep, pbar.maxval) 230 | 231 | 232 | class Bar(WidgetHFill): 233 | 'A progress bar which stretches to fill the line.' 234 | 235 | __slots__ = ('marker', 'left', 'right', 'fill', 'fill_left') 236 | 237 | def __init__(self, marker='#', left='|', right='|', fill=' ', 238 | fill_left=True): 239 | '''Creates a customizable progress bar. 240 | 241 | marker - string or updatable object to use as a marker 242 | left - string or updatable object to use as a left border 243 | right - string or updatable object to use as a right border 244 | fill - character to use for the empty part of the progress bar 245 | fill_left - whether to fill from the left or the right 246 | ''' 247 | self.marker = marker 248 | self.left = left 249 | self.right = right 250 | self.fill = fill 251 | self.fill_left = fill_left 252 | 253 | 254 | def update(self, pbar, width): 255 | 'Updates the progress bar and its subcomponents' 256 | 257 | left, marker, right = (format_updatable(i, pbar) for i in 258 | (self.left, self.marker, self.right)) 259 | 260 | width -= len(left) + len(right) 261 | # Marker must *always* have length of 1 262 | marker *= int(pbar.currval / pbar.maxval * width) 263 | 264 | if self.fill_left: 265 | return '%s%s%s' % (left, marker.ljust(width, self.fill), right) 266 | else: 267 | return '%s%s%s' % (left, marker.rjust(width, self.fill), right) 268 | 269 | 270 | class ReverseBar(Bar): 271 | 'A bar which has a marker which bounces from side to side.' 272 | 273 | def __init__(self, marker='#', left='|', right='|', fill=' ', 274 | fill_left=False): 275 | '''Creates a customizable progress bar. 276 | 277 | marker - string or updatable object to use as a marker 278 | left - string or updatable object to use as a left border 279 | right - string or updatable object to use as a right border 280 | fill - character to use for the empty part of the progress bar 281 | fill_left - whether to fill from the left or the right 282 | ''' 283 | self.marker = marker 284 | self.left = left 285 | self.right = right 286 | self.fill = fill 287 | self.fill_left = fill_left 288 | 289 | 290 | class BouncingBar(Bar): 291 | def update(self, pbar, width): 292 | 'Updates the progress bar and its subcomponents' 293 | 294 | left, marker, right = (format_updatable(i, pbar) for i in 295 | (self.left, self.marker, self.right)) 296 | 297 | width -= len(left) + len(right) 298 | 299 | if pbar.finished: return '%s%s%s' % (left, width * marker, right) 300 | 301 | position = int(pbar.currval % (width * 2 - 1)) 302 | if position > width: position = width * 2 - position 303 | lpad = self.fill * (position - 1) 304 | rpad = self.fill * (width - len(marker) - len(lpad)) 305 | 306 | # Swap if we want to bounce the other way 307 | if not self.fill_left: rpad, lpad = lpad, rpad 308 | 309 | return '%s%s%s%s%s' % (left, lpad, marker, rpad, right) 310 | -------------------------------------------------------------------------------- /conda/cli/main_info.py: -------------------------------------------------------------------------------- 1 | # (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io 2 | # All Rights Reserved 3 | # 4 | # conda is distributed under the terms of the BSD 3-clause license. 5 | # Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause. 6 | 7 | from __future__ import print_function, division, absolute_import 8 | 9 | import re 10 | import sys 11 | import os 12 | from os import listdir 13 | from os.path import isfile, exists, expanduser, join 14 | from collections import defaultdict, OrderedDict 15 | import json 16 | 17 | from conda.cli import common 18 | 19 | 20 | help = "Display information about current conda install." 21 | 22 | example = """ 23 | 24 | Examples: 25 | 26 | conda info -a 27 | """ 28 | 29 | def configure_parser(sub_parsers): 30 | p = sub_parsers.add_parser( 31 | 'info', 32 | description=help, 33 | help=help, 34 | epilog=example, 35 | ) 36 | common.add_parser_json(p) 37 | p.add_argument( 38 | '-a', "--all", 39 | action="store_true", 40 | help="Show all information, (environments, license, and system " 41 | "information.") 42 | p.add_argument( 43 | '-e', "--envs", 44 | action="store_true", 45 | help="List all known conda environments.", 46 | ) 47 | p.add_argument( 48 | '-l', "--license", 49 | action="store_true", 50 | help="Display information about the local conda licenses list.", 51 | ) 52 | p.add_argument( 53 | '-s', "--system", 54 | action="store_true", 55 | help="List environment variables.", 56 | ) 57 | p.add_argument( 58 | 'packages', 59 | action="store", 60 | nargs='*', 61 | help="Display information about packages.", 62 | ) 63 | p.add_argument( 64 | '--root', 65 | action='store_true', 66 | help='Display root environment path.', 67 | ) 68 | p.add_argument( 69 | '--unsafe-channels', 70 | action='store_true', 71 | help='Display list of channels with tokens exposed.', 72 | ) 73 | p.set_defaults(func=execute) 74 | 75 | 76 | def show_pkg_info(name): 77 | #import conda.install as install 78 | from conda.api import get_index 79 | from conda.resolve import MatchSpec, Resolve 80 | 81 | index = get_index() 82 | r = Resolve(index) 83 | print(name) 84 | if name in r.groups: 85 | for pkg in sorted(r.get_pkgs(MatchSpec(name))): 86 | print(' %-15s %15s %s' % ( 87 | pkg.version, 88 | pkg.build, 89 | common.disp_features(r.features(pkg.fn)))) 90 | else: 91 | print(' not available') 92 | # TODO 93 | 94 | python_re = re.compile('python\d\.\d') 95 | 96 | def get_user_site(): 97 | site_dirs = [] 98 | if sys.platform != 'win32': 99 | if exists(expanduser('~/.local/lib')): 100 | for path in listdir(expanduser('~/.local/lib/')): 101 | if python_re.match(path): 102 | site_dirs.append("~/.local/lib/%s" % path) 103 | else: 104 | if 'APPDATA' not in os.environ: 105 | return site_dirs 106 | APPDATA = os.environ['APPDATA'] 107 | if exists(join(APPDATA, 'Python')): 108 | site_dirs = [join(APPDATA, 'Python', i) for i in 109 | listdir(join(APPDATA, 'PYTHON'))] 110 | return site_dirs 111 | 112 | 113 | def pretty_package(pkg): 114 | import conda.config as config 115 | from conda.utils import human_bytes 116 | from conda.api import app_is_installed 117 | 118 | d = OrderedDict([ 119 | ('file name', pkg.fn), 120 | ('name', pkg.name), 121 | ('version', pkg.version), 122 | ('build number', pkg.build_number), 123 | ('build string', pkg.build), 124 | ('channel', config.canonical_channel_name(pkg.channel)), 125 | ('size', human_bytes(pkg.info['size'])), 126 | ]) 127 | rest = pkg.info.copy() 128 | for key in sorted(rest): 129 | if key in ['build', 'depends', 'requires', 'channel', 'name', 130 | 'version', 'build_number', 'size']: 131 | continue 132 | d[key] = rest[key] 133 | 134 | 135 | print() 136 | header = "%s %s %s" % (d['name'], d['version'], d['build string']) 137 | print(header) 138 | print('-'*len(header)) 139 | for key in d: 140 | print("%-12s: %s" % (key, d[key])) 141 | print("installed environments:") 142 | for env in app_is_installed(pkg.fn): 143 | print(' %s' % env) 144 | print('dependencies:') 145 | for dep in pkg.info['depends']: 146 | print(' %s' % dep) 147 | 148 | def execute(args, parser): 149 | import os 150 | from os.path import basename, dirname 151 | 152 | import conda 153 | import conda.config as config 154 | import conda.misc as misc 155 | from conda.resolve import Resolve, MatchSpec 156 | from conda.cli.main_init import is_initialized 157 | from conda.api import get_index, get_package_versions 158 | 159 | if args.root: 160 | if args.json: 161 | common.stdout_json({'root_prefix': config.root_dir}) 162 | else: 163 | print(config.root_dir) 164 | return 165 | 166 | if args.packages: 167 | if args.json: 168 | results = defaultdict(list) 169 | for arg in args.packages: 170 | for pkg in get_package_versions(arg): 171 | results[arg].append(pkg._asdict()) 172 | common.stdout_json(results) 173 | return 174 | index = get_index() 175 | r = Resolve(index) 176 | specs = map(common.arg2spec, args.packages) 177 | 178 | for spec in specs: 179 | versions = r.get_pkgs(MatchSpec(spec)) 180 | for pkg in sorted(versions): 181 | pretty_package(pkg) 182 | 183 | return 184 | 185 | options = 'envs', 'system', 'license' 186 | 187 | try: 188 | import requests 189 | requests_version = requests.__version__ 190 | except ImportError: 191 | requests_version = "could not import" 192 | except Exception as e: 193 | requests_version = "Error %s" % e 194 | 195 | try: 196 | import conda_build 197 | except ImportError: 198 | conda_build_version = "not installed" 199 | except Exception as e: 200 | conda_build_version = "Error %s" % e 201 | else: 202 | conda_build_version = conda_build.__version__ 203 | 204 | info_dict = dict(platform=config.subdir, 205 | conda_version=conda.__version__, 206 | conda_build_version=conda_build_version, 207 | root_prefix=config.root_dir, 208 | root_writable=config.root_writable, 209 | pkgs_dirs=config.pkgs_dirs, 210 | envs_dirs=config.envs_dirs, 211 | default_prefix=config.default_prefix, 212 | channels=config.get_channel_urls(), 213 | rc_path=config.rc_path, 214 | user_rc_path=config.user_rc_path, 215 | sys_rc_path=config.sys_rc_path, 216 | is_foreign=bool(config.foreign), 217 | envs=[], 218 | python_version='.'.join(map(str, sys.version_info)), 219 | requests_version=requests_version, 220 | ) 221 | 222 | if args.unsafe_channels: 223 | if not args.json: 224 | print("\n".join(info_dict["channels"])) 225 | else: 226 | print(json.dumps({"channels": info_dict["channels"]})) 227 | return 0 228 | else: 229 | info_dict['channels'] = [config.hide_binstar_tokens(c) for c in 230 | info_dict['channels']] 231 | if args.all or args.json: 232 | for option in options: 233 | setattr(args, option, True) 234 | 235 | if args.all or all(not getattr(args, opt) for opt in options): 236 | for key in 'pkgs_dirs', 'envs_dirs', 'channels': 237 | info_dict['_' + key] = ('\n' + 24 * ' ').join(info_dict[key]) 238 | info_dict['_rtwro'] = ('writable' if info_dict['root_writable'] else 239 | 'read only') 240 | print("""\ 241 | Current conda install: 242 | 243 | platform : %(platform)s 244 | conda version : %(conda_version)s 245 | conda-build version : %(conda_build_version)s 246 | python version : %(python_version)s 247 | requests version : %(requests_version)s 248 | root environment : %(root_prefix)s (%(_rtwro)s) 249 | default environment : %(default_prefix)s 250 | envs directories : %(_envs_dirs)s 251 | package cache : %(_pkgs_dirs)s 252 | channel URLs : %(_channels)s 253 | config file : %(rc_path)s 254 | is foreign system : %(is_foreign)s 255 | """ % info_dict) 256 | if not is_initialized(): 257 | print("""\ 258 | # NOTE: 259 | # root directory '%s' is uninitialized""" % config.root_dir) 260 | 261 | if args.envs: 262 | common.handle_envs_list(info_dict['envs'], not args.json) 263 | 264 | if args.system and not args.json: 265 | from conda.cli.find_commands import find_commands, find_executable 266 | 267 | print("sys.version: %s..." % (sys.version[:40])) 268 | print("sys.prefix: %s" % sys.prefix) 269 | print("sys.executable: %s" % sys.executable) 270 | print("conda location: %s" % dirname(conda.__file__)) 271 | for cmd in sorted(set(find_commands() + ['build'])): 272 | print("conda-%s: %s" % (cmd, find_executable('conda-' + cmd))) 273 | print("user site dirs: ", end='') 274 | site_dirs = get_user_site() 275 | if site_dirs: 276 | print(site_dirs[0]) 277 | else: 278 | print() 279 | for site_dir in site_dirs[1:]: 280 | print(' %s' % site_dir) 281 | print() 282 | 283 | evars = ['PATH', 'PYTHONPATH', 'PYTHONHOME', 'CONDA_DEFAULT_ENV', 284 | 'CIO_TEST', 'CONDA_ENVS_PATH'] 285 | if config.platform == 'linux': 286 | evars.append('LD_LIBRARY_PATH') 287 | elif config.platform == 'osx': 288 | evars.append('DYLD_LIBRARY_PATH') 289 | for ev in sorted(evars): 290 | print("%s: %s" % (ev, os.getenv(ev, ''))) 291 | print() 292 | 293 | if args.license and not args.json: 294 | try: 295 | from _license import show_info 296 | show_info() 297 | except ImportError: 298 | print("""\ 299 | WARNING: could not import _license.show_info 300 | # try: 301 | # $ conda install -n root _license""") 302 | 303 | if args.json: 304 | common.stdout_json(info_dict) 305 | --------------------------------------------------------------------------------