├── tests ├── testtemplate │ ├── mytemplates │ │ ├── __init__.py │ │ └── main.py │ ├── requirements.txt │ └── setup.py ├── testpackage │ ├── requirements.txt │ ├── testscript.py │ └── setup.py ├── test_expandpath.sh ├── test_support.sh ├── test_mkvirtualenv_requirements.sh ├── test_mkvirtualenv_install.sh ├── test_lazy_run.sh ├── test_log_file.sh ├── setup.sh ├── manual_test_install.sh ├── test_project_templates.sh ├── test_lazy_extending.sh ├── test_dir_stack.sh ├── test_virtualenvwrapper.sh ├── test_derive_workon_home.sh ├── test_project_cd.sh ├── test_hook_dir.sh ├── test_cd_alias.sh ├── test_allvirtualenv.sh ├── test_cd_space_in_name.sh ├── test_deactivate.sh ├── test_mktmpenv.sh ├── test_cd.sh ├── test_rmvirtualenv.sh ├── test_project.sh ├── test_run_hook.sh ├── run_tests ├── test_project_activate.sh ├── test_wipeenv.sh ├── test_lazy.sh ├── test_setvirtualenvproject.sh ├── test_tempfile.sh ├── test_lazy_loaded.sh ├── test_project_mk.sh ├── test_ls.sh ├── test_mkvirtualenv_associate.sh ├── test.sh ├── test_cp.sh ├── test_add2virtualenv.sh ├── test_workon.sh ├── test_mkvirtualenv.sh └── test_cpvirtualenv.sh ├── MANIFEST.in ├── docs ├── requirements.txt ├── spelling_wordlist.txt ├── source │ ├── glossary.rst │ ├── hooks.rst │ ├── projects.rst │ ├── extensions.rst │ ├── developers.rst │ ├── tips.rst │ ├── conf.py │ ├── scripts.rst │ ├── design.rst │ └── index.rst └── Makefile ├── .readthedocs.yaml ├── setup.py ├── .github ├── dependabot.yml └── workflows │ ├── python-publish.yaml │ └── test.yml ├── .gitignore ├── LICENSE ├── Makefile ├── tox.ini ├── virtualenvwrapper ├── project.py ├── hook_loader.py └── user_scripts.py ├── virtualenvwrapper_lazy.sh ├── .mergify.yml ├── README.es.rst ├── README.ja.rst ├── pyproject.toml └── README.txt /tests/testtemplate/mytemplates/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/testpackage/requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | -------------------------------------------------------------------------------- /tests/testtemplate/requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include docs *.rst *.py *.html *.css *.js *.png *.txt 2 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # sphinxcontrib-bitbucket 2 | # sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 3 | sphinx 4 | -------------------------------------------------------------------------------- /tests/testpackage/testscript.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | A test script 5 | """ 6 | 7 | print "Hello world" 8 | -------------------------------------------------------------------------------- /docs/spelling_wordlist.txt: -------------------------------------------------------------------------------- 1 | Hellmann 2 | Bicking 3 | virtualenvwrapper 4 | mkvirtualenv 5 | rmvirtualenv 6 | virtualenv 7 | stderr 8 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: docs/source/conf.py 5 | 6 | build: 7 | os: "ubuntu-20.04" 8 | tools: 9 | python: "3.11" 10 | 11 | python: 12 | # Install our python package before building the docs 13 | install: 14 | - method: pip 15 | path: . 16 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | setup( 6 | # Listing the scripts in pyproject.toml requires them to be python 7 | # entry points for console scripts, but they are shell scripts. 8 | scripts=[ 9 | "virtualenvwrapper.sh", 10 | "virtualenvwrapper_lazy.sh", 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /tests/testpackage/setup.py: -------------------------------------------------------------------------------- 1 | # Test package for virtualenvwrapper tests 2 | from setuptools import setup 3 | 4 | version = '1.0' 5 | 6 | setup( 7 | name='testpackage', 8 | version=version, 9 | description="Fake package", 10 | author="Ingeniweb", 11 | author_email='thomas.desvenain@gmail.com', 12 | url='http://pypi.python.org/pypi/testpackage/', 13 | scripts=['testscript.py'] 14 | ) 15 | -------------------------------------------------------------------------------- /docs/source/glossary.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | Glossary 3 | ========== 4 | 5 | .. glossary:: 6 | 7 | project directory 8 | 9 | Directory associated with a virtualenv, usually located elsewhere 10 | and containing more permanent development artifacts such as local 11 | source files, test data, etc. (see :ref:`variable-PROJECT_HOME`) 12 | 13 | template 14 | 15 | Input to :ref:`command-mkproject` that configures the *project 16 | directory* to contain default files. (see :ref:`plugins`) 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Via https://github.com/nedbat/scriv/blob/main/.github/dependabot.yml 2 | # 3 | # From: 4 | # https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/keeping-your-actions-up-to-date-with-dependabot 5 | # Set update schedule for GitHub Actions 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | # Check for updates to GitHub Actions once a week 13 | interval: "weekly" 14 | -------------------------------------------------------------------------------- /tests/test_expandpath.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | TMP_WORKON_HOME="$WORKON_HOME" 6 | 7 | oneTimeSetUp() { 8 | load_wrappers 9 | echo $PYTHONPATH 10 | } 11 | 12 | oneTimeTearDown() { 13 | return 0 14 | } 15 | 16 | test_tilde() { 17 | assertSame "$HOME" "$(virtualenvwrapper_expandpath ~)" 18 | } 19 | 20 | test_vars() { 21 | assertSame "$HOME" "$(virtualenvwrapper_expandpath '$HOME')" 22 | } 23 | 24 | test_tilde_vars() { 25 | assertSame "$HOME" "$(virtualenvwrapper_expandpath '~$USER')" 26 | } 27 | 28 | . "$test_dir/shunit2" 29 | -------------------------------------------------------------------------------- /docs/source/hooks.rst: -------------------------------------------------------------------------------- 1 | =============================== 2 | Customizing Virtualenvwrapper 3 | =============================== 4 | 5 | virtualenvwrapper adds several hook points you can use to change your 6 | settings, shell environment, or other configuration values when 7 | creating, deleting, or moving between environments. These hooks are 8 | exposed in two ways. :ref:`scripts` allows a user to perform generic actions 9 | for every virtualenv in your environment, including customization of 10 | virtualenv creation. :ref:`plugins` makes it possible to share common 11 | behaviors between systems and developers. 12 | 13 | .. toctree:: 14 | :maxdepth: 1 15 | 16 | scripts 17 | plugins 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | *~ 3 | *.py[co] 4 | README.html 5 | build 6 | dist 7 | *.egg 8 | distribute*.tar.gz 9 | docs/build 10 | docs/html 11 | docs/website 12 | docs/en 13 | docs/es 14 | docs/ja 15 | tests/catch_output 16 | tests/testpackage/build 17 | tests/testpackage/dist 18 | tests/testpackage/testpackage.egg-info 19 | tests/testtemplate/build 20 | tests/testtemplate/dist 21 | tests/testtemplate/testtemplate.egg-info 22 | trace.txt 23 | virtualenvwrapper.egg-info 24 | virtualenvwrapper/docs 25 | .tox 26 | *.orig 27 | TAGS 28 | ChangeLog 29 | AUTHORS 30 | bin 31 | include 32 | lib 33 | 34 | syntax: re 35 | .DS_Store 36 | ^web/ 37 | .python-version 38 | .eggs 39 | /virtualenvwrapper/version.py 40 | -------------------------------------------------------------------------------- /tests/test_support.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | } 15 | 16 | setUp () { 17 | echo 18 | } 19 | 20 | test_get_python_version () { 21 | expected="$($VIRTUAL_ENV/bin/python -c 'import sys; sys.stdout.write("%s.%s\n" % sys.version_info[:2])')" 22 | echo "Expecting: $expected" 23 | vers=$(virtualenvwrapper_get_python_version) 24 | echo "Got : $vers" 25 | assertSame "$expected" "$vers" 26 | } 27 | 28 | 29 | . "$test_dir/shunit2" 30 | -------------------------------------------------------------------------------- /tests/test_mkvirtualenv_requirements.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | rm -f "$TMPDIR/requirements.txt" 15 | } 16 | 17 | setUp () { 18 | echo 19 | } 20 | 21 | test_requirements_file () { 22 | echo "IPy" > "$TMPDIR/requirements.txt" 23 | mkvirtualenv -r "$TMPDIR/requirements.txt" "env3" >/dev/null 2>&1 24 | installed=$(pip freeze) 25 | assertTrue "IPy not found in $installed" "pip freeze | grep IPy" 26 | } 27 | 28 | . "$test_dir/shunit2" 29 | -------------------------------------------------------------------------------- /tests/testtemplate/mytemplates/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | # 4 | # Copyright (c) 2010 Doug Hellmann. All rights reserved. 5 | # 6 | """virtualenvwrapper.project plugin for tests 7 | """ 8 | 9 | import logging 10 | import os 11 | 12 | log = logging.getLogger(__name__) 13 | 14 | 15 | def template(args): 16 | """Creates a test file containing the args passed to us 17 | """ 18 | print('Running test template with args %r' % args) 19 | project, project_dir = args 20 | filename = os.path.join(project_dir, 'TEST_FILE') 21 | print('Writing to %s' % filename) 22 | output = open(filename, 'w') 23 | try: 24 | output.write('\n'.join(args)) 25 | finally: 26 | output.close() 27 | return 28 | -------------------------------------------------------------------------------- /tests/test_mkvirtualenv_install.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | rm -f "$test_dir/requirements.txt" 15 | } 16 | 17 | setUp () { 18 | echo 19 | } 20 | 21 | test_single_package () { 22 | mkvirtualenv -i IPy "env4" 23 | installed=$(pip freeze) 24 | assertTrue "IPy not found in $installed" "pip freeze | grep IPy" 25 | } 26 | 27 | test_multiple_packages () { 28 | mkvirtualenv -i IPy -i WebTest "env5" 29 | installed=$(pip freeze) 30 | assertTrue "IPy not found in $installed" "pip freeze | grep IPy" 31 | assertTrue "WebTest not found in $installed" "pip freeze | grep WebTest" 32 | } 33 | 34 | . "$test_dir/shunit2" 35 | -------------------------------------------------------------------------------- /tests/test_lazy_run.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | (cd "$WORKON_HOME" && virtualenv lazy_load_test >/dev/null 2>&1) 10 | source "$test_dir/../virtualenvwrapper_lazy.sh" 11 | } 12 | 13 | oneTimeTearDown() { 14 | rm -rf "$WORKON_HOME" 15 | } 16 | 17 | setUp () { 18 | echo 19 | } 20 | 21 | test_workon_changes_defs() { 22 | # See issue #144 23 | assertFalse "virtualenvwrapper_run_hook is already defined" "type virtualenvwrapper_run_hook" 24 | workon lazy_load_test >/dev/null 2>&1 25 | assertTrue "virtualenvwrapper_run_hook is not defined" "type virtualenvwrapper_run_hook" 26 | assertTrue "workon still set to run lazy loader" "typeset -f $name | grep 'virtualenvwrapper_load'" 27 | } 28 | 29 | . "$test_dir/shunit2" 30 | -------------------------------------------------------------------------------- /tests/test_log_file.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | setUp () { 7 | echo 8 | } 9 | 10 | test_set_by_user() { 11 | export VIRTUALENVWRAPPER_LOG_FILE="$WORKON_HOME/hooks.log" 12 | load_wrappers 13 | assertTrue "Log file was not created" "[ -f $VIRTUALENVWRAPPER_LOG_FILE ]" 14 | } 15 | 16 | test_file_permissions() { 17 | export VIRTUALENVWRAPPER_LOG_FILE="$WORKON_HOME/hooks.log" 18 | load_wrappers 19 | perms=$(ls -l "$VIRTUALENVWRAPPER_LOG_FILE" | cut -f1 -d' ') 20 | #echo $perms 21 | assertTrue "Log file permissions are wrong: $perms" "echo $perms | grep '^-rw-rw'" 22 | } 23 | 24 | test_not_set_by_user() { 25 | unset WORKON_HOME 26 | unset VIRTUALENVWRAPPER_LOG_FILE 27 | unset VIRTUALENVWRAPPER_HOOK_DIR 28 | load_wrappers 29 | assertSame "" "$VIRTUALENVWRAPPER_LOG_FILE" 30 | } 31 | 32 | . "$test_dir/shunit2" 33 | -------------------------------------------------------------------------------- /tests/setup.sh: -------------------------------------------------------------------------------- 1 | # Setup globals used by the tests 2 | 3 | #set -x 4 | 5 | # tmplocation=${TMPDIR:-/tmp} 6 | # export WORKON_HOME="$(echo ${tmplocation}/WORKON_HOME.$$ | sed 's|//|/|g')" 7 | # export PROJECT_HOME="$(echo ${tmplocation}/PROJECT_HOME.$$ | sed 's|//|/|g')" 8 | 9 | export WORKON_HOME=$(mktemp -d -t "WORKON_HOME.XXXX.$$") 10 | export PROJECT_HOME=$(mktemp -d -t "PROJECT_HOME.XXXX.$$") 11 | 12 | #unset HOOK_VERBOSE_OPTION 13 | 14 | SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 15 | 16 | # This should point to VIRTUAL_ENV/bin when running under tox. 17 | TEST_BIN_DIR=$(dirname $(which python)) 18 | 19 | load_wrappers() { 20 | if [ "$USING_TOX" = "1" ]; then 21 | # Use the version of the scripts installed as part of the 22 | # package. 23 | source "$TEST_BIN_DIR/virtualenvwrapper.sh" 24 | else 25 | echo "USING SOURCE VERSION OF SCRIPT" 26 | source "$SCRIPTDIR/../virtualenvwrapper.sh" 27 | fi 28 | } 29 | -------------------------------------------------------------------------------- /tests/manual_test_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Test installation of virtualenvwrapper in a new virtualenv. 4 | # 5 | 6 | test_dir=$(dirname $0) 7 | source "$test_dir/../virtualenvwrapper.sh" 8 | 9 | export WORKON_HOME="${TMPDIR:-/tmp}/WORKON_HOME" 10 | 11 | VERSION=$(python setup.py --version) 12 | 13 | oneTimeSetUp() { 14 | rm -rf "$WORKON_HOME" 15 | mkdir -p "$WORKON_HOME" 16 | } 17 | 18 | oneTimeTearDown() { 19 | rm -rf "$WORKON_HOME" 20 | } 21 | 22 | setUp () { 23 | echo 24 | } 25 | 26 | test_build_ok () { 27 | (cd "$test_dir/.." && make sdist) 28 | outcome=$? 29 | assertSame "0" "$outcome" 30 | } 31 | 32 | test_install () { 33 | dist_dir=$(dirname $test_dir)/dist 34 | pip install "$dist_dir/virtualenvwrapper-$VERSION.tar.gz" 35 | RC=$? 36 | assertTrue "Error code $RC" "[ $RC -eq 0 ]" 37 | assertTrue "Missing wrapper script" "[ -f $WORKON_HOME/installtest/bin/virtualenvwrapper.sh ]" 38 | } 39 | 40 | . "$test_dir/shunit2" 41 | -------------------------------------------------------------------------------- /tests/test_project_templates.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(dirname $0) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | (cd "$test_dir/testtemplate" && rm -rf build && "$VIRTUAL_ENV/bin/python" -m pip install .) 8 | rm -rf "$WORKON_HOME" 9 | mkdir -p "$WORKON_HOME" 10 | rm -rf "$PROJECT_HOME" 11 | mkdir -p "$PROJECT_HOME" 12 | load_wrappers 13 | } 14 | 15 | oneTimeTearDown() { 16 | rm -rf "$WORKON_HOME" 17 | rm -rf "$PROJECT_HOME" 18 | } 19 | 20 | setUp () { 21 | echo 22 | } 23 | 24 | test_list_templates () { 25 | output=$(mkproject -h 2>&1) 26 | assertTrue "Did not find test template in \"$output\"" "echo \"$output\" | grep -q 'Creates a test file'" 27 | } 28 | 29 | test_apply_template () { 30 | mkproject -t test proj1 >/dev/null 2>&1 31 | assertTrue "Test file not created" "[ -f TEST_FILE ]" 32 | assertTrue "project name not found" "grep -q proj1 TEST_FILE" 33 | } 34 | 35 | . "$test_dir/shunit2" 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Doug Hellmann, All Rights Reserved 2 | 3 | Permission to use, copy, modify, and distribute this software and its 4 | documentation for any purpose and without fee is hereby granted, 5 | provided that the above copyright notice appear in all copies and that 6 | both that copyright notice and this permission notice appear in 7 | supporting documentation, and that the name of Doug Hellmann not be used 8 | in advertising or publicity pertaining to distribution of the software 9 | without specific, written prior permission. 10 | 11 | DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 12 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 13 | EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR 14 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 15 | USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 16 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 | PERFORMANCE OF THIS SOFTWARE. 18 | -------------------------------------------------------------------------------- /tests/test_lazy_extending.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | [ ! -z "$ZSH_VERSION" ] && unsetopt shwordsplit 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | } 15 | 16 | setUp () { 17 | echo 18 | } 19 | 20 | function_defined_lazy() { 21 | name="$1" 22 | assertTrue "$name not defined" "type $name" 23 | assertTrue "$name does not load virtualenvwrapper" "typeset -f $name | grep 'virtualenvwrapper_load'" 24 | if [ "$name" = "mkvirtualenv" ] 25 | then 26 | lookfor="rmvirtualenv" 27 | else 28 | lookfor="mkvirtualenv" 29 | fi 30 | assertFalse "$name includes reference to $lookfor: $(typeset -f $name)" "typeset -f $name | grep $lookfor" 31 | } 32 | 33 | test_custom_defined_lazy() { 34 | _VIRTUALENVWRAPPER_API="my_custom_command" 35 | source "$test_dir/../virtualenvwrapper_lazy.sh" 36 | function_defined_lazy my_custom_command 37 | } 38 | 39 | . "$test_dir/shunit2" 40 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Default target is to show help 2 | help: 3 | @echo "sdist - Source distribution" 4 | @echo "html - HTML documentation" 5 | @echo "docclean - Remove documentation build files" 6 | @echo "upload - upload a new release to PyPI" 7 | @echo "develop - install development version" 8 | @echo "test - run the test suite" 9 | @echo "test-quick - run the test suite for bash and one version of Python ($(PYTHON27))" 10 | 11 | .PHONY: sdist 12 | sdist: html 13 | rm -f dist/*.gz 14 | rm -rf docs/website 15 | python setup.py sdist 16 | cp -v dist/*.gz ~/Desktop 17 | 18 | # Documentation 19 | .PHONY: html 20 | html: 21 | tox -e docs 22 | 23 | .PHONY: docclean 24 | docclean: 25 | rm -rf docs/build docs/html 26 | 27 | # Register the new version on pypi 28 | .PHONY: register 29 | register: 30 | echo "USE upload target" 31 | exit 1 32 | python setup.py register 33 | 34 | .PHONY: upload 35 | upload: 36 | python setup.py sdist upload 37 | 38 | # Testing 39 | test: 40 | tox 41 | 42 | test-quick: 43 | tox -e py27 44 | 45 | develop: 46 | python setup.py develop 47 | -------------------------------------------------------------------------------- /tests/test_dir_stack.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | test_begin_dir=$(pwd) 8 | } 9 | 10 | oneTimeTearDown() { 11 | cd "$test_begin_dir" 12 | } 13 | 14 | setUp () { 15 | rm -rf "$WORKON_HOME" 16 | mkdir -p "$WORKON_HOME" 17 | load_wrappers 18 | mkdir "$WORKON_HOME/start_here" 19 | mkdir "$WORKON_HOME/on_the_stack" 20 | echo 21 | } 22 | 23 | tearDown() { 24 | if type deactivate >/dev/null 2>&1 25 | then 26 | deactivate 27 | fi 28 | rm -rf "$WORKON_HOME" 29 | } 30 | 31 | test_ticket_101 () { 32 | mkvirtualenv some_env 33 | deactivate 34 | cd "$WORKON_HOME/start_here" 35 | pushd "$WORKON_HOME/on_the_stack" 36 | rmvirtualenv some_env 37 | mkvirtualenv some_env >/dev/null 2>&1 38 | #echo "After mkvirtualenv: `pwd`" 39 | deactivate 40 | #echo "After deactivate: `pwd`" 41 | popd 42 | #echo "After popd: `pwd`" 43 | current_dir=$(pwd) 44 | assertSame "$WORKON_HOME/start_here" "$current_dir" 45 | 46 | } 47 | 48 | . "$test_dir/shunit2" 49 | -------------------------------------------------------------------------------- /tests/test_virtualenvwrapper.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | # 3 | # Tests for help function 'virtualenvwrapper' 4 | 5 | test_dir=$(cd $(dirname $0) && pwd) 6 | source "$test_dir/setup.sh" 7 | 8 | oneTimeSetUp() { 9 | rm -rf "$WORKON_HOME" 10 | mkdir -p "$WORKON_HOME" 11 | } 12 | 13 | oneTimeTearDown() { 14 | rm -rf "$WORKON_HOME" 15 | } 16 | 17 | setUp () { 18 | echo 19 | unset VIRTUALENVWRAPPER_SCRIPT 20 | rm -f "$TMPDIR/catch_output" 21 | } 22 | 23 | test_virtualenvwrapper_script_set() { 24 | load_wrappers 25 | assertTrue "VIRTUALENVWRAPPER_SCRIPT is not right: $VIRTUALENVWRAPPER_SCRIPT" \ 26 | "echo $VIRTUALENVWRAPPER_SCRIPT | grep -q /virtualenvwrapper.sh" 27 | } 28 | 29 | test_virtualenvwrapper_version() { 30 | load_wrappers 31 | typeset ver=$(_virtualenvwrapper_version) 32 | assertTrue "version is empty" "[ -n $ver ]" 33 | } 34 | 35 | test_virtualenvwrapper_help_shows_version() { 36 | load_wrappers 37 | typeset pattern="Version: $(_virtualenvwrapper_version)" 38 | assertTrue "version not in command output" "virtualenvwrapper | grep \"$pattern\"" 39 | } 40 | 41 | . "$test_dir/shunit2" 42 | -------------------------------------------------------------------------------- /tests/test_derive_workon_home.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | TMP_WORKON_HOME="$WORKON_HOME" 6 | 7 | oneTimeSetUp() { 8 | rm -rf "$TMP_WORKON_HOME" 9 | mkdir -p "$TMP_WORKON_HOME" 10 | load_wrappers 11 | echo $PYTHONPATH 12 | } 13 | 14 | oneTimeTearDown() { 15 | rm -rf "$TMP_WORKON_HOME" 16 | } 17 | 18 | setUp () { 19 | echo 20 | WORKON_HOME="$TMP_WORKON_HOME" 21 | } 22 | 23 | test_default() { 24 | unset WORKON_HOME 25 | assertSame "$HOME/.virtualenvs" "$(virtualenvwrapper_derive_workon_home)" 26 | } 27 | 28 | test_includes_relative_path() { 29 | WORKON_HOME="$WORKON_HOME/../$(basename $WORKON_HOME)" 30 | assertSame "$WORKON_HOME" "$(virtualenvwrapper_derive_workon_home)" 31 | } 32 | 33 | test_begins_relative_path() { 34 | WORKON_HOME=".test-virtualenvs" 35 | assertSame "$HOME/.test-virtualenvs" "$(virtualenvwrapper_derive_workon_home)" 36 | } 37 | 38 | test_includes_tilde() { 39 | WORKON_HOME="~/.test-virtualenvs" 40 | assertSame "$HOME/.test-virtualenvs" "$(virtualenvwrapper_derive_workon_home)" 41 | } 42 | 43 | . "$test_dir/shunit2" 44 | -------------------------------------------------------------------------------- /tests/testtemplate/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | PROJECT = 'testtemplate' 4 | VERSION = '1.0' 5 | 6 | from setuptools import setup, find_packages 7 | 8 | setup( 9 | name=PROJECT, 10 | version=VERSION, 11 | 12 | description='template for testing mkproject', 13 | 14 | author='Doug Hellmann', 15 | author_email='doug.hellmann@gmail.com', 16 | 17 | url='http://www.doughellmann.com/projects/virtualenvwrapper/', 18 | 19 | classifiers=[ 20 | 'Development Status :: 5 - Production/Stable', 21 | 'License :: OSI Approved :: BSD License', 22 | 'Programming Language :: Python', 23 | 'Intended Audience :: Developers', 24 | 'Environment :: Console', 25 | ], 26 | 27 | platforms=['Any'], 28 | 29 | provides=['testtemplate', 30 | ], 31 | requires=['virtualenv', 32 | 'virtualenvwrapper (>=2.9)', 33 | ], 34 | 35 | packages=find_packages(), 36 | include_package_data=True, 37 | 38 | entry_points={ 39 | 'virtualenvwrapper.project.template': [ 40 | 'test = mytemplates.main:template', 41 | ], 42 | }, 43 | 44 | zip_safe=False, 45 | ) 46 | -------------------------------------------------------------------------------- /tests/test_project_cd.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(dirname $0) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | rm -rf "$PROJECT_HOME" 10 | mkdir -p "$PROJECT_HOME" 11 | load_wrappers 12 | } 13 | 14 | oneTimeTearDown() { 15 | rm -rf "$WORKON_HOME" 16 | rm -rf "$PROJECT_HOME" 17 | } 18 | 19 | setUp () { 20 | echo 21 | } 22 | 23 | test_with_project () { 24 | mkproject myproject >/dev/null 2>&1 25 | cd $TMPDIR 26 | cdproject 27 | assertSame "$PROJECT_HOME/myproject" "$(pwd)" 28 | deactivate 29 | } 30 | 31 | test_without_project () { 32 | mkvirtualenv myproject >/dev/null 2>&1 33 | cd $TMPDIR 34 | output=$(cdproject 2>&1) 35 | echo "$output" | grep -q "No project set" 36 | RC=$? 37 | assertSame "1" "$RC" 38 | deactivate 39 | } 40 | 41 | test_space_in_path () { 42 | ( 43 | set -e 44 | PROJECT_HOME="$PROJECT_HOME/with spaces" 45 | mkdir -p "$PROJECT_HOME" 46 | mkproject "myproject" >/dev/null 2>&1 47 | cd "$WORKON_HOME" 48 | cdproject 49 | test "$PROJECT_HOME/myproject" = "$PWD" 50 | ) 51 | assertTrue "Did not cd to project directory" $? 52 | } 53 | 54 | 55 | . "$test_dir/shunit2" 56 | -------------------------------------------------------------------------------- /tests/test_hook_dir.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | mkdir -p "$WORKON_HOME/hooks" 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | } 15 | 16 | setUp () { 17 | echo 18 | rm -f "$WORKON_HOME/hooks/*" 19 | } 20 | 21 | SOURCE_SCRIPTS="initialize postmkvirtualenv predeactivate postdeactivate postactivate " 22 | RUN_SCRIPTS="premkvirtualenv prermvirtualenv postrmvirtualenv preactivate get_env_details" 23 | 24 | test_virtualenvwrapper_initialize() { 25 | export VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME/hooks" 26 | load_wrappers 27 | for hook in $SOURCE_SCRIPTS 28 | do 29 | assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/hooks/$hook ]" 30 | assertFalse "Global $WORKON_HOME/$hook is executable" "[ -x $WORKON_HOME/hooks/$hook ]" 31 | done 32 | for hook in $RUN_SCRIPTS 33 | do 34 | assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/hooks/$hook ]" 35 | assertTrue "Global $WORKON_HOME/$hook is executable" "[ -x $WORKON_HOME/hooks/$hook ]" 36 | done 37 | } 38 | 39 | . "$test_dir/shunit2" 40 | -------------------------------------------------------------------------------- /tests/test_cd_alias.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | unset VIRTUAL_ENV 11 | mkvirtualenv cd-test >/dev/null 2>&1 12 | deactivate 13 | } 14 | 15 | oneTimeTearDown() { 16 | rm -rf "$WORKON_HOME" 17 | } 18 | 19 | setUp () { 20 | echo 21 | workon cd-test 22 | } 23 | 24 | tearDown () { 25 | deactivate >/dev/null 2>&1 26 | } 27 | 28 | test_cd() { 29 | alias cd='fail "Should not be using override cd function"' 30 | start_dir="$(pwd)" 31 | virtualenvwrapper_cd "$VIRTUAL_ENV" 32 | assertSame "$VIRTUAL_ENV" "$(pwd)" 33 | virtualenvwrapper_cd "$start_dir" 34 | unalias cd 35 | } 36 | 37 | # Define hook function to make cd break 38 | chpwd () { 39 | return 1 40 | } 41 | # Run a test that uses cd to ensure the hook is not called 42 | test_cd_zsh_chpwd_not_called () { 43 | if [ -n "$ZSH_VERSION" ]; then 44 | start_dir="$(pwd)" 45 | virtualenvwrapper_cd "$VIRTUAL_ENV" 46 | assertSame "$VIRTUAL_ENV" "$(pwd)" 47 | virtualenvwrapper_cd "$start_dir" 48 | fi 49 | unset -f chpwd >/dev/null 2>&1 50 | } 51 | 52 | . "$test_dir/shunit2" 53 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py,zsh,style 3 | 4 | [testenv] 5 | commands = bash ./tests/run_tests {envdir} [] 6 | pass_env = 7 | HOME 8 | USER 9 | setenv = 10 | USING_TOX = 1 11 | SHELL = /bin/bash 12 | allowlist_externals = 13 | bash 14 | zsh 15 | 16 | [testenv:fast] 17 | setenv = 18 | USING_TOX = 1 19 | SHELL = /bin/bash 20 | FAIL_FAST = true 21 | 22 | [testenv:style] 23 | deps = .[linter] 24 | commands = flake8 virtualenvwrapper docs/source/conf.py 25 | 26 | [flake8] 27 | max-line-length = 200 28 | 29 | [testenv:zsh] 30 | setenv = 31 | USING_TOX = 1 32 | SHELL = /bin/zsh 33 | test_shell_opts = -o shwordsplit 34 | commands = zsh -o shwordsplit ./tests/run_tests {envdir} [] 35 | 36 | [testenv:docs] 37 | deps = 38 | -r{toxinidir}/docs/requirements.txt 39 | commands = 40 | sphinx-build -W -j auto -b html -d docs/build/doctrees docs/source docs/build/html 41 | 42 | [testenv:linkcheck] 43 | deps = 44 | -r{toxinidir}/docs/requirements.txt 45 | commands = 46 | sphinx-build -W -j auto -b linkcheck -d docs/build/doctrees docs/source docs/build/linkcheck 47 | 48 | [testenv:pkglint] 49 | deps=.[build] 50 | commands= 51 | python -m build 52 | twine check dist/*.tar.gz dist/*.whl 53 | check-python-versions --only pyproject.toml,.github/workflows/test.yml 54 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yaml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | - push 8 | 9 | jobs: 10 | build-n-publish: 11 | name: Build and publish Python distributions to PyPI and TestPyPI 12 | if: ${{ github.repository_owner == 'python-virtualenvwrapper' }} 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v6 18 | with: 19 | fetch-depth: 0 20 | - name: Set up Python 21 | uses: actions/setup-python@v6 22 | with: 23 | python-version: '3.x' 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install .[build] 28 | - name: Build sdist and wheel 29 | run: | 30 | python -m build 31 | - name: Publish distribution to PyPI 32 | # This condition prevents PRs from being published as part of 33 | # the test job. 34 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 35 | uses: pypa/gh-action-pypi-publish@v1.13.0 36 | with: 37 | password: ${{ secrets.PYPI_API_TOKEN }} 38 | verbose: true 39 | print_hash: true 40 | -------------------------------------------------------------------------------- /tests/test_allvirtualenv.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | unset VIRTUAL_ENV 11 | # These three env names must sort the same whether the OS considers leading whitespace or not. 12 | # "test" is after " env" and after "env", so the asserts work on OSX and Linux. 13 | mkvirtualenv test1 >/dev/null 2>&1 14 | mkvirtualenv test2 >/dev/null 2>&1 15 | # Only test with leading and internal spaces. Directory names with trailing spaces are legal, 16 | # and work with virtualenv on OSX, but error out on Linux. 17 | mkvirtualenv " env with space" >/dev/null 2>&1 18 | deactivate 19 | } 20 | 21 | oneTimeTearDown() { 22 | rm -rf "$WORKON_HOME" 23 | } 24 | 25 | setUp () { 26 | echo 27 | } 28 | 29 | tearDown () { 30 | deactivate >/dev/null 2>&1 31 | } 32 | 33 | test_allvirtualenv_all() { 34 | assertTrue "Did not find test1" "allvirtualenv pwd | grep -q 'test1$'" 35 | assertTrue "Did not find test2" "allvirtualenv pwd | grep -q 'test2$'" 36 | assertTrue "Did not find ' env with space'" "allvirtualenv pwd | grep -q ' env with space'" 37 | } 38 | 39 | test_allvirtualenv_spaces() { 40 | assertTrue "Command did not output The Zen of Python" "allvirtualenv python -c 'import this' | grep -q 'The Zen of Python'" 41 | } 42 | 43 | . "$test_dir/shunit2" 44 | -------------------------------------------------------------------------------- /tests/test_cd_space_in_name.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | export WORKON_HOME="$WORKON_HOME/ this has spaces" 8 | rm -rf "$WORKON_HOME" 9 | mkdir -p "$WORKON_HOME" 10 | load_wrappers 11 | unset VIRTUAL_ENV 12 | # Only test with leading and internal spaces. Directory names with trailing spaces are legal, 13 | # and work with virtualenv on OSX, but error out on Linux. 14 | mkvirtualenv " env with space" >/dev/null 2>&1 15 | deactivate 16 | } 17 | 18 | oneTimeTearDown() { 19 | rm -rf "$WORKON_HOME" 20 | } 21 | 22 | setUp () { 23 | echo 24 | } 25 | 26 | tearDown () { 27 | deactivate >/dev/null 2>&1 28 | } 29 | 30 | cd () { 31 | fail "Should not be using override cd function" 32 | } 33 | 34 | test_cdvirtual_space_in_workon_home_space_in_name() { 35 | workon " env with space" 36 | start_dir="$(pwd)" 37 | cdvirtualenv 38 | assertSame "$VIRTUAL_ENV" "$(pwd)" 39 | cdvirtualenv bin 40 | assertSame "$VIRTUAL_ENV/bin" "$(pwd)" 41 | virtualenvwrapper_cd "$start_dir" 42 | } 43 | 44 | test_cdsitepackages_space_in_name () { 45 | workon " env with space" 46 | start_dir="$(pwd)" 47 | cdsitepackages 48 | pyvers=$(python -V 2>&1 | cut -f2 -d' ' | cut -f1-2 -d.) 49 | sitepackages="$VIRTUAL_ENV/lib/python${pyvers}/site-packages" 50 | assertSame "$sitepackages" "$(pwd)" 51 | virtualenvwrapper_cd "$start_dir" 52 | } 53 | 54 | 55 | . "$test_dir/shunit2" 56 | -------------------------------------------------------------------------------- /tests/test_deactivate.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | mkvirtualenv "env1" 11 | } 12 | 13 | oneTimeTearDown() { 14 | rm -rf "$WORKON_HOME" 15 | } 16 | 17 | setUp () { 18 | echo 19 | rm -f "$TMPDIR/catch_output" 20 | } 21 | 22 | test_deactivate () { 23 | workon env1 24 | assertNotNull "$VIRTUAL_ENV" 25 | deactivate 26 | assertNull "$VIRTUAL_ENV" 27 | assertFalse virtualenvwrapper_verify_active_environment 28 | } 29 | 30 | test_deactivate_hooks () { 31 | workon env1 32 | 33 | for t in pre post 34 | do 35 | echo "echo GLOBAL ${t}deactivate \$VIRTUALENVWRAPPER_LAST_VIRTUAL_ENV >> $TMPDIR/catch_output" > "$WORKON_HOME/${t}deactivate" 36 | echo "echo ENV ${t}deactivate \$VIRTUALENVWRAPPER_LAST_VIRTUAL_ENV >> $TMPDIR/catch_output" > "$WORKON_HOME/env1/bin/${t}deactivate" 37 | done 38 | 39 | touch "$TMPDIR/catch_output" 40 | 41 | deactivate 42 | 43 | output=$(cat "$TMPDIR/catch_output") 44 | expected="ENV predeactivate 45 | GLOBAL predeactivate 46 | ENV postdeactivate $WORKON_HOME/env1 47 | GLOBAL postdeactivate $WORKON_HOME/env1" 48 | assertSame "$expected" "$output" 49 | 50 | for t in pre post 51 | do 52 | rm -f "$WORKON_HOME/env1/bin/${t}activate" 53 | rm -f "$WORKON_HOME/${t}activate" 54 | done 55 | } 56 | 57 | . "$test_dir/shunit2" 58 | -------------------------------------------------------------------------------- /tests/test_mktmpenv.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | 7 | oneTimeSetUp() { 8 | rm -rf "$WORKON_HOME" 9 | mkdir -p "$WORKON_HOME" 10 | load_wrappers 11 | } 12 | 13 | oneTimeTearDown() { 14 | rm -rf "$WORKON_HOME" 15 | } 16 | 17 | setUp () { 18 | echo 19 | } 20 | 21 | test_mktmpenv_no_name() { 22 | before=$(lsvirtualenv -b) 23 | mktmpenv >/dev/null 2>&1 24 | after=$(lsvirtualenv -b) 25 | assertFalse "Environment was not created" "[ \"$before\" = \"$after\" ]" 26 | assertSame "$VIRTUAL_ENV" "$(pwd)" 27 | } 28 | 29 | test_mktmpenv_name() { 30 | mktmpenv name-given-by-user >/dev/null 2>&1 31 | RC=$? 32 | assertTrue "Error was not detected" "[ $RC -ne 0 ]" 33 | } 34 | 35 | test_mktmpenv_n() { 36 | mktmpenv -n >/dev/null 2>&1 37 | assertNotSame "$VIRTUAL_ENV" "$(pwd)" 38 | } 39 | 40 | test_mktmpenv_no_cd() { 41 | mktmpenv --no-cd >/dev/null 2>&1 42 | assertNotSame "$VIRTUAL_ENV" "$(pwd)" 43 | } 44 | 45 | test_mktmpenv_virtualenv_args() { 46 | mktmpenv --without-pip >/dev/null 2>&1 47 | contents="$(lssitepackages)" 48 | assertFalse "found pip in site-packages: ${contents}" "echo $contents | grep -q pip" 49 | } 50 | 51 | test_deactivate() { 52 | mktmpenv >/dev/null 2>&1 53 | assertTrue "Environment was not created" "[ ! -z \"$VIRTUAL_ENV\" ]" 54 | env_name=$(basename "$VIRTUAL_ENV") 55 | deactivate >/dev/null 2>&1 56 | assertFalse "Environment still exists" "[ -d \"$WORKON_HOME/$env_name\" ]" 57 | } 58 | 59 | . "$test_dir/shunit2" 60 | -------------------------------------------------------------------------------- /docs/source/projects.rst: -------------------------------------------------------------------------------- 1 | .. _project-management: 2 | 3 | ==================== 4 | Project Management 5 | ==================== 6 | 7 | A :term:`project directory` is associated with a virtualenv, but 8 | usually contains the source code under active development rather than 9 | the installed components needed to support the development. For 10 | example, the project directory may contain the source code checked out 11 | from a version control system, temporary artifacts created by testing, 12 | experimental files not committed to version control, etc. 13 | 14 | A project directory is created and bound to a virtualenv when 15 | :ref:`command-mkproject` is run instead of 16 | :ref:`command-mkvirtualenv`. To bind an existing project directory to 17 | a virtualenv, use :ref:`command-setvirtualenvproject`. 18 | 19 | Using Templates 20 | =============== 21 | 22 | A new project directory can be created empty, or populated using one 23 | or more :term:`template` plugins. Templates should be specified as 24 | arguments to :ref:`command-mkproject`. Multiple values can be provided 25 | to apply more than one template. For example, to check out a Mercurial 26 | repository from a project on BitBucket and create a new Django 27 | site, combine the :ref:`templates-bitbucket` and 28 | :ref:`templates-django` templates. 29 | 30 | :: 31 | 32 | $ mkproject -t bitbucket -t django my_site 33 | 34 | Project Hook Files 35 | ================== 36 | 37 | The project directory can include additional hook files for the 38 | `postactivate` and `predeactivate` hooks. Placing hook scripts in the 39 | project hook directory, `.virtualenvwrapper`, allows them to be 40 | checked into version control and shared more easily. 41 | 42 | When the :ref:`scripts-postactivate` hook runs, it looks for 43 | `.virtualenvwrapper/postactivate` within the project directory and if 44 | it is found it sources the file. 45 | 46 | When the :ref:`scripts-predeactivate` hook runs, it looks for 47 | `.virtualenvwrapper/predeactivate` within the project directory and if 48 | it is found it sources the file. 49 | 50 | .. seealso:: 51 | 52 | * :ref:`extensions-templates` 53 | * :ref:`variable-PROJECT_HOME` 54 | * :ref:`variable-VIRTUALENVWRAPPER_PROJECT_FILENAME` 55 | * :ref:`variable-VIRTUALENVWRAPPER_WORKON_CD` 56 | -------------------------------------------------------------------------------- /tests/test_cd.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | unset VIRTUAL_ENV 11 | mkvirtualenv cd-test >/dev/null 2>&1 12 | deactivate 13 | } 14 | 15 | oneTimeTearDown() { 16 | rm -rf "$WORKON_HOME" 17 | } 18 | 19 | setUp () { 20 | echo 21 | workon cd-test 22 | } 23 | 24 | tearDown () { 25 | deactivate >/dev/null 2>&1 26 | } 27 | 28 | cd () { 29 | fail "Should not be using override cd function" 30 | } 31 | 32 | test_cdvirtual() { 33 | start_dir="$(pwd)" 34 | cdvirtualenv 35 | assertSame "$VIRTUAL_ENV" "$(pwd)" 36 | cdvirtualenv bin 37 | assertSame "$VIRTUAL_ENV/bin" "$(pwd)" 38 | virtualenvwrapper_cd "$start_dir" 39 | } 40 | 41 | test_cdsitepackages () { 42 | start_dir="$(pwd)" 43 | cdsitepackages 44 | pyvers=$(python -V 2>&1 | cut -f2 -d' ' | cut -f1-2 -d.) 45 | sitepackages="$VIRTUAL_ENV/lib/python${pyvers}/site-packages" 46 | assertSame "$sitepackages" "$(pwd)" 47 | virtualenvwrapper_cd "$start_dir" 48 | } 49 | 50 | test_cdsitepackages_with_arg () { 51 | start_dir="$(pwd)" 52 | pyvers=$(python -V 2>&1 | cut -f2 -d' ' | cut -f1-2 -d.) 53 | sitepackage_subdir="$VIRTUAL_ENV/lib/python${pyvers}/site-packages/subdir" 54 | mkdir -p "${sitepackage_subdir}" 55 | cdsitepackages subdir 56 | assertSame "$sitepackage_subdir" "$(pwd)" 57 | virtualenvwrapper_cd "$start_dir" 58 | } 59 | 60 | test_cdvirtualenv_no_workon_home () { 61 | old_home="$WORKON_HOME" 62 | export WORKON_HOME="$WORKON_HOME/not_there" 63 | cdvirtualenv >"$old_home/output" 2>&1 64 | output=$(cat "$old_home/output") 65 | assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" 66 | WORKON_HOME="$old_home" 67 | } 68 | 69 | test_cdsitepackages_no_workon_home () { 70 | deactivate 2>&1 71 | old_home="$WORKON_HOME" 72 | virtualenvwrapper_cd "$WORKON_HOME" 73 | export WORKON_HOME="$WORKON_HOME/not_there" 74 | assertFalse "Was able to change to site-packages" cdsitepackages 75 | assertSame "$old_home" "$(pwd)" 76 | WORKON_HOME="$old_home" 77 | } 78 | 79 | 80 | . "$test_dir/shunit2" 81 | -------------------------------------------------------------------------------- /tests/test_rmvirtualenv.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | } 15 | 16 | setUp () { 17 | mkvirtualenv "deleteme" >/dev/null 2>&1 18 | # Only test with leading and internal spaces. Directory names with trailing spaces are legal, 19 | # and work with virtualenv on OSX, but error out on Linux. 20 | mkvirtualenv " env with space" >/dev/null 2>&1 21 | deactivate >/dev/null 2>&1 22 | } 23 | 24 | test_remove () { 25 | assertTrue "[ -d $WORKON_HOME/deleteme ]" 26 | rmvirtualenv "deleteme" 27 | assertFalse "[ -d $WORKON_HOME/deleteme ]" 28 | } 29 | 30 | test_remove_space_in_name () { 31 | assertTrue "[ -d $WORKON_HOME/\" env with space\" ]" 32 | rmvirtualenv " env with space" 33 | assertFalse "[ -d $WORKON_HOME/\" env with space\" ]" 34 | } 35 | 36 | test_remove_several_envs () { 37 | assertTrue "[ -d $WORKON_HOME/deleteme ]" 38 | assertTrue "[ -d $WORKON_HOME/\" env with space\" ]" 39 | rmvirtualenv deleteme " env with space" 40 | assertFalse "[ -d $WORKON_HOME/deleteme ]" 41 | assertFalse "[ -d $WORKON_HOME/\" env with space\" ]" 42 | } 43 | 44 | test_within_virtualenv () { 45 | mkvirtualenv "deleteme2" >/dev/null 2>&1 46 | assertTrue "[ -d $WORKON_HOME/deleteme2 ]" 47 | cdvirtualenv 48 | assertSame "$VIRTUAL_ENV" "$(pwd)" 49 | deactivate 50 | rmvirtualenv "deleteme2" 51 | assertSame "$WORKON_HOME" "$(pwd)" 52 | assertFalse "[ -d $WORKON_HOME/deleteme2 ]" 53 | } 54 | 55 | test_rm_aliased () { 56 | alias rm='rm -i' 57 | rmvirtualenv "deleteme" 58 | unalias rm 59 | } 60 | 61 | test_no_such_env () { 62 | assertFalse "[ -d $WORKON_HOME/deleteme2 ]" 63 | assertTrue "rmvirtualenv deleteme2" 64 | } 65 | 66 | test_no_workon_home () { 67 | old_home="$WORKON_HOME" 68 | export WORKON_HOME="$WORKON_HOME/not_there" 69 | rmvirtualenv should_not_be_created >"$old_home/output" 2>&1 70 | output=$(cat "$old_home/output") 71 | assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" 72 | WORKON_HOME="$old_home" 73 | } 74 | 75 | 76 | . "$test_dir/shunit2" 77 | -------------------------------------------------------------------------------- /tests/test_project.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(dirname $0) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | rm -rf "$PROJECT_HOME" 10 | mkdir -p "$PROJECT_HOME" 11 | } 12 | 13 | oneTimeTearDown() { 14 | rm -rf "$WORKON_HOME" 15 | rm -rf "$PROJECT_HOME" 16 | } 17 | 18 | setUp () { 19 | echo 20 | unset VIRTUALENVWRAPPER_INITIALIZED 21 | } 22 | 23 | test_initialize() { 24 | load_wrappers 25 | for hook in premkproject postmkproject 26 | do 27 | assertTrue "Global $hook was not created" "[ -f $WORKON_HOME/$hook ]" 28 | assertTrue "Global $hook is not executable" "[ -x $WORKON_HOME/$hook ]" 29 | done 30 | } 31 | 32 | test_initialize_hook_dir() { 33 | export VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME/hooks" 34 | mkdir -p "$VIRTUALENVWRAPPER_HOOK_DIR" 35 | load_wrappers 36 | for hook in premkproject postmkproject 37 | do 38 | assertTrue "Global $hook was not created" "[ -f $VIRTUALENVWRAPPER_HOOK_DIR/$hook ]" 39 | assertTrue "Global $hook is not executable" "[ -x $VIRTUALENVWRAPPER_HOOK_DIR/$hook ]" 40 | done 41 | VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME" 42 | } 43 | 44 | test_virtualenvwrapper_verify_project_home() { 45 | assertTrue "PROJECT_HOME not verified" virtualenvwrapper_verify_project_home 46 | } 47 | 48 | test_virtualenvwrapper_verify_project_home_missing_dir() { 49 | old_home="$PROJECT_HOME" 50 | PROJECT_HOME="$PROJECT_HOME/not_there" 51 | assertFalse "PROJECT_HOME verified unexpectedly" virtualenvwrapper_verify_project_home 52 | PROJECT_HOME="$old_home" 53 | } 54 | 55 | test_virtualenvwrapper_postactivate_hook() { 56 | load_wrappers 57 | mkproject "test_project_hook" 58 | mkdir .virtualenvwrapper 59 | echo "export TEST_PROJECT_HOOK_VAR=true" > .virtualenvwrapper/postactivate 60 | echo "unset TEST_PROJECT_HOOK_VAR" > .virtualenvwrapper/predeactivate 61 | deactivate 62 | 63 | # Variable should not be set to start 64 | assertSame "${TEST_PROJECT_HOOK_VAR}" "" 65 | 66 | # Activating the env should set it 67 | workon "test_project_hook" 68 | assertSame "true" "${TEST_PROJECT_HOOK_VAR}" 69 | 70 | # Deactivating should unset it 71 | deactivate 72 | assertSame "" "${TEST_PROJECT_HOOK_VAR}" 73 | } 74 | 75 | . "$test_dir/shunit2" 76 | -------------------------------------------------------------------------------- /virtualenvwrapper/project.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Copyright (c) 2010 Doug Hellmann. All rights reserved. 4 | # 5 | """virtualenvwrapper.project 6 | """ 7 | 8 | import logging 9 | import os 10 | 11 | from virtualenvwrapper.user_scripts import PERMISSIONS, make_hook, run_global 12 | 13 | log = logging.getLogger(__name__) 14 | 15 | GLOBAL_HOOKS = [ 16 | # mkproject 17 | ("premkproject", 18 | "This hook is run after a new project is created " 19 | "and before it is activated.", 20 | PERMISSIONS), 21 | ("postmkproject", 22 | "This hook is run after a new project is activated.", 23 | PERMISSIONS), 24 | ] 25 | 26 | 27 | def initialize(args): 28 | """Set up user hooks 29 | """ 30 | for filename, comment, permissions in GLOBAL_HOOKS: 31 | make_hook(os.path.join('$VIRTUALENVWRAPPER_HOOK_DIR', filename), 32 | comment, permissions) 33 | return 34 | 35 | 36 | def pre_mkproject(args): 37 | log.debug('pre_mkproject %s', str(args)) 38 | run_global('premkproject', *args) 39 | return 40 | 41 | 42 | def post_mkproject_source(args): 43 | return """ 44 | # 45 | # Run user-provided scripts 46 | # 47 | [ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postmkproject" ] && \ 48 | source "$VIRTUALENVWRAPPER_HOOK_DIR/postmkproject" 49 | """ 50 | 51 | 52 | def post_activate_source(args): 53 | return """ 54 | # 55 | # Change to the project directory, as long as we haven't been told not to. 56 | # 57 | [ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME" \ 58 | -a "$VIRTUALENVWRAPPER_PROJECT_CD" = 1 ] && \ 59 | virtualenvwrapper_cd \ 60 | "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")" 61 | if [ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME" ]; then 62 | if [ -f "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")/.virtualenvwrapper/postactivate" ]; then 63 | source "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")/.virtualenvwrapper/postactivate" 64 | fi 65 | fi 66 | """ 67 | 68 | 69 | def pre_deactivate_source(args): 70 | return """ 71 | if [ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME" ]; then 72 | if [ -f "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")/.virtualenvwrapper/predeactivate" ]; then 73 | source "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")/.virtualenvwrapper/predeactivate" 74 | fi 75 | fi 76 | """ 77 | -------------------------------------------------------------------------------- /docs/source/extensions.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | Existing Extensions 3 | ===================== 4 | 5 | Below is a list of some of the extensions available for use with 6 | virtualenvwrapper. 7 | 8 | emacs-desktop 9 | ============= 10 | 11 | Emacs desktop-mode_ lets you save the state of emacs (open buffers, 12 | kill rings, buffer positions, etc.) between sessions. It can also be 13 | used as a project file similar to other IDEs. The emacs-desktop_ 14 | plugin adds a trigger to save the current desktop file and load a new 15 | one when activating a new virtualenv using ``workon``. 16 | 17 | .. _desktop-mode: https://www.emacswiki.org/emacs/DeskTop 18 | 19 | .. _emacs-desktop: https://pypi.python.org/pypi/virtualenvwrapper-emacs-desktop 20 | 21 | .. _extensions-user_scripts: 22 | 23 | user_scripts 24 | ============ 25 | 26 | The ``user_scripts`` extension is delivered with virtualenvwrapper and 27 | enabled by default. It implements the user customization script 28 | features described in :ref:`scripts`. 29 | 30 | vim-virtualenv 31 | ============== 32 | 33 | `vim-virtualenv`_ is Jeremey Cantrell's plugin for controlling 34 | virtualenvs from within vim. When used together with 35 | virtualenvwrapper, vim-virtualenv identifies the virtualenv to 36 | activate based on the name of the file being edited. 37 | 38 | .. _vim-virtualenv: https://github.com/jmcantrell/vim-virtualenv 39 | 40 | .. _extensions-templates: 41 | 42 | Templates 43 | ========= 44 | 45 | Below is a list of some of the templates available for use with 46 | :ref:`command-mkproject`. 47 | 48 | .. _templates-bitbucket: 49 | 50 | bitbucket 51 | --------- 52 | 53 | The bitbucket_ extension automatically clones a mercurial repository 54 | from the specified Bitbucket project. 55 | 56 | .. _bitbucket: https://pypi.python.org/pypi/virtualenvwrapper.bitbucket 57 | 58 | .. _templates-django: 59 | 60 | django 61 | ------ 62 | 63 | The django_ extension automatically creates a new Django project. 64 | 65 | .. _django: https://pypi.python.org/pypi/virtualenvwrapper.django 66 | 67 | SublimeText 68 | ----------- 69 | 70 | Song Jin has created a template plugin for automatically generating 71 | the project files used by SublimeText. See the 72 | sublime_projectfile_maker_ page for details. 73 | 74 | .. _sublime_projectfile_maker: https://github.com/SongGithub/sublime_projectfile_maker 75 | 76 | .. seealso:: 77 | 78 | * :ref:`developer-templates` 79 | -------------------------------------------------------------------------------- /tests/test_run_hook.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | } 15 | 16 | setUp () { 17 | echo 18 | rm -f "$WORKON_HOME/initialize" 19 | rm -f "$WORKON_HOME/prermvirtualenv" 20 | rm -f "$TMPDIR/catch_output" 21 | } 22 | 23 | test_virtualenvwrapper_run_hook() { 24 | echo "echo run >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" 25 | chmod +x "$WORKON_HOME/initialize" 26 | virtualenvwrapper_run_hook "initialize" 27 | output=$(cat "$TMPDIR/catch_output") 28 | expected="run" 29 | assertSame "$expected" "$output" 30 | } 31 | 32 | test_virtualenvwrapper_run_hook_alternate_dir() { 33 | mkdir "$WORKON_HOME/hooks" 34 | echo "echo WORKON_HOME >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" 35 | echo "echo WORKON_HOME/hooks >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/hooks/initialize" 36 | chmod +x "$WORKON_HOME/initialize" 37 | chmod +x "$WORKON_HOME/hooks/initialize" 38 | VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME/hooks" 39 | virtualenvwrapper_run_hook "initialize" 40 | output=$(cat "$TMPDIR/catch_output") 41 | expected="WORKON_HOME/hooks" 42 | assertSame "$expected" "$output" 43 | VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME" 44 | } 45 | 46 | test_virtualenvwrapper_source_hook_permissions() { 47 | echo "echo run >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" 48 | chmod -x "$WORKON_HOME/initialize" 49 | virtualenvwrapper_run_hook "initialize" 50 | output=$(cat "$TMPDIR/catch_output") 51 | expected="run" 52 | assertSame "$expected" "$output" 53 | } 54 | 55 | test_virtualenvwrapper_run_hook_permissions() { 56 | echo "#!/bin/sh" > "$WORKON_HOME/prermvirtualenv" 57 | echo "echo run $@ >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/prermvirtualenv" 58 | chmod 0444 "$WORKON_HOME/prermvirtualenv" 59 | touch "$TMPDIR/catch_output" 60 | error=$(virtualenvwrapper_run_hook "pre_rmvirtualenv" "foo" 2>&1 | grep "could not run" | cut -f2- -d'[' | cut -f1 -d:) 61 | output=$(cat "$TMPDIR/catch_output") 62 | expected="" 63 | assertSame "$expected" "$output" 64 | assertSame "Errno 13] Permission denied" "$error" 65 | } 66 | 67 | . "$test_dir/shunit2" 68 | -------------------------------------------------------------------------------- /virtualenvwrapper_lazy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Alternative startup script for faster login times. 3 | 4 | export _VIRTUALENVWRAPPER_API="$_VIRTUALENVWRAPPER_API mkvirtualenv rmvirtualenv lsvirtualenv showvirtualenv workon add2virtualenv cdsitepackages cdvirtualenv lssitepackages toggleglobalsitepackages cpvirtualenv setvirtualenvproject mkproject cdproject mktmpenv wipeenv allvirtualenv" 5 | 6 | if [ -z "$VIRTUALENVWRAPPER_SCRIPT" ] 7 | then 8 | export VIRTUALENVWRAPPER_SCRIPT="$(command \which virtualenvwrapper.sh)" 9 | fi 10 | if [ -z "$VIRTUALENVWRAPPER_SCRIPT" ] 11 | then 12 | echo "ERROR: virtualenvwrapper_lazy.sh: Could not find virtualenvwrapper.sh" 1>&2 13 | fi 14 | 15 | # Load the real implementation of the API from virtualenvwrapper.sh 16 | function virtualenvwrapper_load { 17 | # Only source the script once. 18 | # We might get called multiple times, because not all of _VIRTUALENVWRAPPER_API gets 19 | # a real completion. 20 | if [ -z $VIRTUALENVWRAPPER_LAZY_LOADED ] 21 | then 22 | # NOTE: For Zsh, I have tried to unset any auto-load completion. 23 | # (via `compctl + $(echo ${_VIRTUALENVWRAPPER_API})`. 24 | # But this does not appear to work / triggers a crash. 25 | source "$VIRTUALENVWRAPPER_SCRIPT" 26 | VIRTUALENVWRAPPER_LAZY_LOADED=1 27 | fi 28 | } 29 | 30 | # Set up "alias" functions based on the API definition. 31 | function virtualenvwrapper_setup_lazy_loader { 32 | typeset venvw_name 33 | for venvw_name in $(echo ${_VIRTUALENVWRAPPER_API}) 34 | do 35 | eval " 36 | function $venvw_name { 37 | virtualenvwrapper_load 38 | ${venvw_name} \"\$@\" 39 | } 40 | " 41 | done 42 | } 43 | 44 | # Set up completion functions to virtualenvwrapper_load 45 | function virtualenvwrapper_setup_lazy_completion { 46 | if [ -n "$BASH" ] ; then 47 | function virtualenvwrapper_lazy_load { 48 | virtualenvwrapper_load 49 | return 124 50 | } 51 | complete -o nospace -F virtualenvwrapper_lazy_load $(echo ${_VIRTUALENVWRAPPER_API}) 52 | elif [ -n "$ZSH_VERSION" ] ; then 53 | compctl -K virtualenvwrapper_load $(echo ${_VIRTUALENVWRAPPER_API}) 54 | fi 55 | } 56 | 57 | virtualenvwrapper_setup_lazy_loader 58 | # Cannot be reset in zsh to fallback to files (e.g. mkvirtualenv). 59 | virtualenvwrapper_setup_lazy_completion 60 | 61 | unset virtualenvwrapper_setup_lazy_loader 62 | unset virtualenvwrapper_setup_lazy_completion 63 | -------------------------------------------------------------------------------- /tests/run_tests: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | #set -x 3 | 4 | envdir="$(cd $1 && pwd)" 5 | shift 6 | scripts="$*" 7 | if [ -z "$scripts" ] 8 | then 9 | scripts=$(ls tests/test*.sh) 10 | if [ -z "$scripts" ] 11 | then 12 | echo "Could not find any test scripts to run" 1>&2 13 | exit 1 14 | fi 15 | fi 16 | 17 | # Override FAIL_FAST to true to exit as soon as any test fails 18 | FAIL_FAST=${FAIL_FAST:-false} 19 | 20 | # Force the tox virtualenv to be active. 21 | # 22 | # Since this script runs from within a separate shell created by tox, 23 | # the name of the virtualenv (in $VIRTUAL_ENV) is inherited, but the 24 | # shell functions and other settings created by the activate script 25 | # are *not* inherited. 26 | # 27 | source "$envdir/bin/activate" 28 | TMPDIR="$envdir/tmp" 29 | export TMPDIR 30 | mkdir -p "$TMPDIR" 31 | 32 | # Set up virtualenvwrapper.hook_loader to print more details than usual for debugging. 33 | #export HOOK_VERBOSE_OPTION=-vvv 34 | HOOK_VERBOSE_OPTION="-q" 35 | export HOOK_VERBOSE_OPTION 36 | 37 | # Force virtualenvwrapper to use the python interpreter in the 38 | # tox-created virtualenv. 39 | VIRTUALENVWRAPPER_PYTHON="$envdir/bin/python3" 40 | export VIRTUALENVWRAPPER_PYTHON 41 | 42 | # Clear any user settings for the hook directory or log directory 43 | unset VIRTUALENVWRAPPER_HOOK_DIR 44 | unset VIRTUALENVWRAPPER_LOG_DIR 45 | unset VIRTUALENVWRAPPER_VIRTUALENV 46 | unset VIRTUALENVWRAPPER_VIRTUALENV_ARGS 47 | 48 | # Run the test scripts with a little formatting around them to make it 49 | # easier to find where each script output starts. 50 | RC=0 51 | for test_script in $scripts 52 | do 53 | 54 | echo 55 | echo '********************************************************************************' 56 | echo "Running $SHELL $test_shell_opts $test_script" 57 | echo " VIRTUAL_ENV=$VIRTUAL_ENV" 58 | echo " VIRTUALENVWRAPPER_PYTHON=$VIRTUALENVWRAPPER_PYTHON" 59 | echo " $($VIRTUALENVWRAPPER_PYTHON -V 2>&1)" 60 | echo " PYTHONPATH=$PYTHONPATH" 61 | echo " SHELL=$SHELL" 62 | echo " BASH_VERSION=$BASH_VERSION" 63 | echo " ZSH_VERSION=$ZSH_VERSION" 64 | echo " virtualenv=$(which virtualenv)" 65 | echo " test_shell_opts=$test_shell_opts" 66 | echo " ZSH=$ZSH_NAME $ZSH_EVAL_CONTEXT" 67 | echo " TMPDIR=$TMPDIR" 68 | 69 | echo 70 | SHUNIT_PARENT="$test_script" 71 | export SHUNIT_PARENT 72 | if ! $SHELL $test_shell_opts $test_script; then 73 | RC=1 74 | if $FAIL_FAST; then 75 | exit $RC 76 | fi 77 | fi 78 | echo 79 | 80 | done 81 | 82 | exit $RC 83 | -------------------------------------------------------------------------------- /tests/test_project_activate.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(dirname $0) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | rm -rf "$PROJECT_HOME" 10 | mkdir -p "$PROJECT_HOME" 11 | load_wrappers 12 | } 13 | 14 | # oneTimeTearDown() { 15 | # rm -rf "$WORKON_HOME" 16 | # rm -rf "$PROJECT_HOME" 17 | # } 18 | 19 | setUp () { 20 | echo 21 | # In case the user has the value set, we need to force it to the default 22 | VIRTUALENVWRAPPER_WORKON_CD=1 23 | } 24 | 25 | tearDown () { 26 | # In case a test has changed the value, we need to force it to the default 27 | VIRTUALENVWRAPPER_WORKON_CD=1 28 | } 29 | 30 | test_activate () { 31 | mkproject myproject >/dev/null 2>&1 32 | deactivate 33 | cd $TMPDIR 34 | assertSame "" "$VIRTUAL_ENV" 35 | workon myproject 36 | assertSame "myproject" "$(basename $VIRTUAL_ENV)" 37 | assertSame "$PROJECT_HOME/myproject" "$(pwd)" 38 | deactivate 39 | } 40 | 41 | test_activate_n () { 42 | mkproject myproject_n >/dev/null 2>&1 43 | assertTrue $? 44 | deactivate 45 | cd $TMPDIR 46 | assertSame "" "$VIRTUAL_ENV" 47 | workon -n myproject_n 48 | assertSame "myproject_n" "$(basename $VIRTUAL_ENV)" 49 | assertSame "$TMPDIR" "$(pwd)" 50 | deactivate 51 | } 52 | 53 | test_activate_no_cd () { 54 | mkproject myproject_no_cd >/dev/null 2>&1 55 | assertTrue $? 56 | deactivate 57 | cd $TMPDIR 58 | assertSame "" "$VIRTUAL_ENV" 59 | workon --no-cd myproject_no_cd 60 | assertSame "myproject_no_cd" "$(basename $VIRTUAL_ENV)" 61 | assertSame "$TMPDIR" "$(pwd)" 62 | deactivate 63 | } 64 | 65 | test_activate_workon_cd_disabled () { 66 | export VIRTUALENVWRAPPER_WORKON_CD=0 67 | mkproject myproject_cd_disabled >/dev/null 2>&1 68 | assertTrue $? 69 | deactivate 70 | cd $TMPDIR 71 | assertSame "" "$VIRTUAL_ENV" 72 | workon myproject_cd_disabled 73 | assertSame "myproject_cd_disabled" "$(basename $VIRTUAL_ENV)" 74 | assertSame "$TMPDIR" "$(pwd)" 75 | deactivate 76 | } 77 | 78 | test_space_in_path () { 79 | old_project_home="$PROJECT_HOME" 80 | PROJECT_HOME="$PROJECT_HOME/with spaces" 81 | mkdir -p "$PROJECT_HOME" 82 | mkproject "myproject" >/dev/null 2>&1 83 | deactivate 84 | cd $TMPDIR 85 | workon "myproject" 86 | assertSame "myproject" "$(basename $VIRTUAL_ENV)" 87 | assertSame "$PROJECT_HOME/myproject" "$(pwd)" 88 | deactivate 89 | PROJECT_HOME="$old_project_home" 90 | } 91 | 92 | 93 | . "$test_dir/shunit2" 94 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | 3 | - name: Add CI label 4 | conditions: 5 | - or: 6 | - "title~=^tox:" 7 | - "title~=^ci:" 8 | - "files~=tox.ini" 9 | - "files~=tests" 10 | actions: 11 | label: 12 | add: 13 | - ci 14 | 15 | - name: Add Mergify label 16 | conditions: 17 | - or: 18 | - "title~=^mergify:" 19 | - "files~=.mergify.yml$" 20 | actions: 21 | label: 22 | add: 23 | - mergify 24 | 25 | - name: Add Documentation label 26 | conditions: 27 | - or: 28 | - "title~=^docs:" 29 | - "files~=docs" 30 | - "files~=.rst$" 31 | - "files~=.readthedocs.yaml$" 32 | actions: 33 | label: 34 | add: 35 | - documentation 36 | 37 | - name: automatic approval for Dependabot pull requests 38 | conditions: 39 | - author=dependabot[bot] 40 | actions: 41 | review: 42 | type: APPROVE 43 | message: Automatically approving dependabot 44 | 45 | - name: Add breaking-change label 46 | # https://docs.openstack.org/pbr/latest/user/features.html 47 | conditions: 48 | - "body~=Sem-Ver: api-break" 49 | actions: 50 | label: 51 | add: 52 | - "breaking change" 53 | 54 | - name: Add feature label 55 | # https://docs.openstack.org/pbr/latest/user/features.html 56 | conditions: 57 | - "body~=Sem-Ver: feature" 58 | actions: 59 | label: 60 | add: 61 | - feature 62 | 63 | - name: Add automatic-merge label 64 | conditions: 65 | - or: 66 | - "author=dhellmann" 67 | - "author=jasonamyers" 68 | - "label!=mergify" 69 | actions: 70 | label: 71 | add: 72 | - automatic-merge 73 | 74 | - name: Automatic merge on approval 75 | conditions: 76 | - and: 77 | - "check-success=docs" 78 | - "check-success=style (style)" 79 | - "check-success=style (pkglint)" 80 | - "check-success=Test macOS (3.8)" 81 | - "check-success=Test macOS (3.9)" 82 | - "check-success=Test macOS (3.10)" 83 | - "check-success=Test macOS (3.11)" 84 | - "check-success=Test macOS (3.12)" 85 | - "check-success=Test Ubuntu (3.8)" 86 | - "check-success=Test Ubuntu (3.9)" 87 | - "check-success=Test Ubuntu (3.10)" 88 | - "check-success=Test Ubuntu (3.11)" 89 | - "check-success=Test Ubuntu (3.12)" 90 | - "check-success=Test Zsh" 91 | - "-draft" 92 | - or: 93 | - "#approved-reviews-by>=1" 94 | - "author=dhellmann" 95 | - "author=jasonamyers" 96 | actions: 97 | merge: 98 | method: merge 99 | -------------------------------------------------------------------------------- /tests/test_wipeenv.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | setUp () { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | echo 11 | } 12 | 13 | tearDown() { 14 | if type deactivate >/dev/null 2>&1 15 | then 16 | deactivate 17 | fi 18 | rm -rf "$WORKON_HOME" 19 | } 20 | 21 | test_wipeenv () { 22 | mkvirtualenv "wipetest" >/dev/null 2>&1 23 | (cd tests/testpackage && pip install .) >/dev/null 2>&1 24 | before="$(pip freeze)" 25 | assertTrue "testpackage not installed" "pip freeze | grep testpackage" 26 | wipeenv >/dev/null 2>&1 27 | after="$(pip freeze)" 28 | assertFalse "testpackage still installed" "pip freeze | grep testpackage" 29 | } 30 | 31 | test_wipeenv_pip_e () { 32 | mkvirtualenv "wipetest" >/dev/null 2>&1 33 | (cd tests/testpackage && pip install -e .) >/dev/null 2>&1 34 | before="$(pip freeze)" 35 | assertTrue "testpackage not installed: $before" "pip freeze | grep testpackage" 36 | wipeenv >/dev/null 2>&1 37 | after="$(pip freeze)" 38 | assertFalse "testpackage still installed: $after" "pip freeze | grep testpackage" 39 | } 40 | 41 | # test_wipeenv_pip_e_url () { 42 | # mkvirtualenv "wipetest" >/dev/null 2>&1 43 | # (cd tests/testpackage && pip install -e 'git+https://github.com/kennethreitz/legit.git@3c4d3214811c7892edf903682fdbb44f4050b99a#egg=legit-origin') 44 | # # >/dev/null 2>&1 45 | # before="$(pip freeze)" 46 | # pip freeze 47 | # assertTrue "legit not installed" "pip freeze | grep legit" 48 | # wipeenv >/dev/null 2>&1 49 | # after="$(pip freeze)" 50 | # assertFalse "legit still installed" "pip freeze | grep legit" 51 | # } 52 | 53 | test_wipeenv_develop () { 54 | mkvirtualenv "wipetest" >/dev/null 2>&1 55 | (cd tests/testpackage && pip install -e . --config-settings editable_mode=compat) >/dev/null 2>&1 56 | before="$(pip freeze)" 57 | assertTrue "testpackage not installed" "pip freeze | grep testpackage" 58 | wipeenv >/dev/null 2>&1 59 | after="$(pip freeze)" 60 | assertFalse "testpackage still installed" "pip freeze | grep testpackage" 61 | } 62 | 63 | test_empty_env () { 64 | mkvirtualenv "wipetest" >/dev/null 2>&1 65 | before="$(pip freeze)" 66 | assertFalse "testpackage still installed" "pip freeze | grep testpackage" 67 | wipeenv >/dev/null 2>&1 68 | after="$(pip freeze)" 69 | assertFalse "testpackage still installed" "pip freeze | grep testpackage" 70 | } 71 | 72 | test_not_active_env () { 73 | mkvirtualenv "wipetest" >/dev/null 2>&1 74 | deactivate 75 | assertFalse "wipenv did not report an error" "wipeenv >/dev/null 2>&1" 76 | } 77 | 78 | . "$test_dir/shunit2" 79 | 80 | -------------------------------------------------------------------------------- /tests/test_lazy.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | [ ! -z "$ZSH_VERSION" ] && unsetopt shwordsplit 10 | source "$test_dir/../virtualenvwrapper_lazy.sh" 11 | [ ! -z "$ZSH_VERSION" ] && setopt shwordsplit 12 | } 13 | 14 | oneTimeTearDown() { 15 | rm -rf "$WORKON_HOME" 16 | } 17 | 18 | setUp () { 19 | echo 20 | } 21 | 22 | function_defined_lazy() { 23 | name="$1" 24 | assertTrue "$name not defined" "type $name" 25 | assertTrue "$name does not load virtualenvwrapper" "typeset -f $name | grep 'virtualenvwrapper_load'" 26 | if [ "$name" = "mkvirtualenv" ] 27 | then 28 | lookfor="rmvirtualenv" 29 | else 30 | lookfor="mkvirtualenv" 31 | fi 32 | assertFalse "$name includes reference to $lookfor: $(typeset -f $name)" "typeset -f $name | grep $lookfor" 33 | } 34 | 35 | test_mkvirtualenv_defined_lazy() { 36 | function_defined_lazy mkvirtualenv 37 | } 38 | 39 | test_rmvirtualenv_defined_lazy() { 40 | function_defined_lazy rmvirtualenv 41 | } 42 | 43 | test_lsvirtualenv_defined_lazy() { 44 | function_defined_lazy lsvirtualenv 45 | } 46 | 47 | test_showvirtualenv_defined_lazy() { 48 | function_defined_lazy showvirtualenv 49 | } 50 | 51 | test_workon_defined_lazy() { 52 | function_defined_lazy workon 53 | } 54 | 55 | test_add2virtualenv_defined_lazy() { 56 | function_defined_lazy add2virtualenv 57 | } 58 | 59 | test_cdsitepackages_defined_lazy() { 60 | function_defined_lazy cdsitepackages 61 | } 62 | 63 | test_cdvirtualenv_defined_lazy() { 64 | function_defined_lazy cdvirtualenv 65 | } 66 | 67 | test_cdvirtualenv_defined_lazy() { 68 | function_defined_lazy cdvirtualenv 69 | } 70 | 71 | test_lssitepackages_defined_lazy() { 72 | function_defined_lazy lssitepackages 73 | } 74 | 75 | test_toggleglobalsitepackages_defined_lazy() { 76 | function_defined_lazy toggleglobalsitepackages 77 | } 78 | 79 | test_cpvirtualenv_defined_lazy() { 80 | function_defined_lazy cpvirtualenv 81 | } 82 | 83 | test_setvirtualenvproject_defined_lazy() { 84 | function_defined_lazy setvirtualenvproject 85 | } 86 | 87 | test_mkproject_defined_lazy() { 88 | function_defined_lazy mkproject 89 | } 90 | 91 | test_cdproject_defined_lazy() { 92 | function_defined_lazy cdproject 93 | } 94 | 95 | test_mktmpenv_defined_lazy() { 96 | function_defined_lazy mktmpenv 97 | } 98 | 99 | test_wipeenv_defined_lazy() { 100 | function_defined_lazy wipeenv 101 | } 102 | 103 | test_allvirtualenv_defined_lazy() { 104 | function_defined_lazy allvirtualenv 105 | } 106 | 107 | . "$test_dir/shunit2" 108 | -------------------------------------------------------------------------------- /tests/test_setvirtualenvproject.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(dirname $0) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | rm -rf "$PROJECT_HOME" 10 | mkdir -p "$PROJECT_HOME" 11 | load_wrappers 12 | } 13 | 14 | oneTimeTearDown() { 15 | rm -rf "$WORKON_HOME" 16 | rm -rf "$PROJECT_HOME" 17 | } 18 | 19 | setUp () { 20 | echo 21 | } 22 | 23 | test_setvirtualenvproject() { 24 | n=1 25 | project="$WORKON_HOME/project$n" 26 | mkdir "$project" 27 | env="env$n" 28 | ptrfile="$WORKON_HOME/$env/.project" 29 | mkvirtualenv "$env" >/dev/null 2>&1 30 | setvirtualenvproject "$env" "$project" >/dev/null 2>&1 31 | assertTrue ".project not found" "[ -f $ptrfile ]" 32 | assertEquals "$ptrfile contains wrong content" "$project" "$(cat $ptrfile)" 33 | } 34 | 35 | test_setvirtualenvproject_relative_path() { 36 | cd "$WORKON_HOME" 37 | n=2 38 | project="project$n" 39 | mkdir "$project" 40 | env="env$n" 41 | ptrfile="$WORKON_HOME/$env/.project" 42 | mkvirtualenv "$env" >/dev/null 2>&1 43 | setvirtualenvproject "$env" "$project" >/dev/null 2>&1 44 | assertTrue ".project not found" "[ -f $ptrfile ]" 45 | assertEquals \ 46 | "$ptrfile contains wrong content" \ 47 | "$WORKON_HOME/$project" \ 48 | "$(cat $ptrfile | sed 's|^/private||')" 49 | } 50 | 51 | test_setvirtualenvproject_not_a_directory() { 52 | cd "$WORKON_HOME" 53 | n=3 54 | project="project$n" 55 | touch "$project" 56 | env="env$n" 57 | ptrfile="$WORKON_HOME/$env/.project" 58 | mkvirtualenv "$env" >/dev/null 2>&1 59 | setvirtualenvproject "$env" "$project" >/dev/null 2>&1 60 | RC=$? 61 | assertTrue "setvirtualenvproject should have failed" "[ $RC -ne 0 ]" 62 | } 63 | 64 | test_setvirtualenvproject_does_not_exist() { 65 | n=4 66 | project="project$n" 67 | env="env$n" 68 | ptrfile="$WORKON_HOME/$env/.project" 69 | mkvirtualenv "$env" >/dev/null 2>&1 70 | setvirtualenvproject "$env" "$project" >/dev/null 2>&1 71 | RC=$? 72 | assertTrue "setvirtualenvproject should have failed" "[ $RC -ne 0 ]" 73 | } 74 | 75 | test_setvirtualenvproject_relative_with_dots() { 76 | cd "$WORKON_HOME" 77 | n=5 78 | project="project$n" 79 | mkdir $project 80 | mkdir $project.sibling 81 | cd $project.sibling 82 | # Change the reference to a sibling directory 83 | project="../$project" 84 | env="env$n" 85 | ptrfile="$WORKON_HOME/$env/.project" 86 | mkvirtualenv "$env" >/dev/null 2>&1 87 | setvirtualenvproject "$env" "$project" >/dev/null 2>&1 88 | assertTrue ".project not found" "[ -f $ptrfile ]" 89 | assertEquals \ 90 | "$ptrfile contains wrong content" \ 91 | "$WORKON_HOME/project$n" \ 92 | "$(cat $ptrfile | sed 's|^/private||')" 93 | } 94 | 95 | . "$test_dir/shunit2" 96 | -------------------------------------------------------------------------------- /tests/test_tempfile.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | #export HOOK_VERBOSE_OPTION=-v 7 | 8 | oneTimeSetUp() { 9 | rm -rf "$WORKON_HOME" 10 | mkdir -p "$WORKON_HOME" 11 | load_wrappers 12 | echo $PYTHONPATH 13 | } 14 | 15 | oneTimeTearDown() { 16 | rm -rf "$WORKON_HOME" 17 | } 18 | 19 | setUp () { 20 | echo 21 | } 22 | 23 | test_tempfile () { 24 | if [ "$(uname)" = "Darwin" ]; then 25 | # macOS doesn't seem to allow controlling where mktemp creates 26 | # the output files 27 | return 0 28 | fi 29 | filename=$(virtualenvwrapper_tempfile hook) 30 | assertTrue "Filename is empty" "[ ! -z \"$filename\" ]" 31 | assertTrue "File doesn't exist" "[ -f \"$filename\" ]" 32 | rm -f $filename 33 | comparable_tmpdir=$(echo $TMPDIR | sed 's|/$||') 34 | comparable_dirname=$(dirname $filename | sed 's|/$||') 35 | assertSame "Temporary directory \"$TMPDIR\" and path not the same for $filename" "$comparable_tmpdir" "$comparable_dirname" 36 | assertTrue "virtualenvwrapper-hook not in filename." "echo $filename | grep virtualenvwrapper-hook" 37 | } 38 | 39 | test_bad_mktemp() { 40 | # All of the following bogus mktemp programs should cause 41 | # virtualenvwrapper_tempfile to return non-zero status 42 | mktemp_nonzero() { return 1; } 43 | mktemp_empty_string() { return 0; } 44 | mktemp_missing_executable() { /foo/bar/baz/qux 2>/dev/null; } # returns status 127 45 | mktemp_missing_result() { echo /foo/bar/baz/qux; } 46 | 47 | for mktemp_func in mktemp_nonzero mktemp_empty_string \ 48 | mktemp_missing_executable mktemp_missing_result 49 | do 50 | virtualenvwrapper_mktemp() { $mktemp_func "$@"; } 51 | filename=$(virtualenvwrapper_tempfile hook 2>/dev/null) 52 | assertSame "($mktemp_func) Unexpected exit code $?" "1" "$?" 53 | done 54 | 55 | # Restore the "real" definition of the replaceable function 56 | virtualenvwrapper_mktemp() { command mktemp "$@"; } 57 | } 58 | 59 | test_no_such_tmpdir () { 60 | if [ "$(uname)" = "Darwin" ]; then 61 | # macOS doesn't seem to allow controlling where mktemp creates 62 | # the output files 63 | return 0 64 | fi 65 | old_tmpdir="$TMPDIR" 66 | export TMPDIR="$TMPDIR/does-not-exist" 67 | virtualenvwrapper_run_hook "initialize" >/dev/null 2>&1 68 | RC=$? 69 | assertSame "Unexpected exit code $RC" "1" "$RC" 70 | TMPDIR="$old_tmpdir" 71 | } 72 | 73 | test_tmpdir_not_writable () { 74 | if [ "$(uname)" = "Darwin" ]; then 75 | # macOS doesn't seem to allow controlling where mktemp creates 76 | # the output files 77 | return 0 78 | fi 79 | old_tmpdir="$TMPDIR" 80 | export TMPDIR="$TMPDIR/cannot-write" 81 | mkdir "$TMPDIR" 82 | chmod ugo-w "$TMPDIR" 83 | virtualenvwrapper_run_hook "initialize" >/dev/null 2>&1 84 | RC=$? 85 | assertSame "Unexpected exit code $RC" "1" "$RC" 86 | chmod ugo+w "$TMPDIR" 87 | rmdir "$TMPDIR" 88 | TMPDIR="$old_tmpdir" 89 | } 90 | 91 | . "$test_dir/shunit2" 92 | -------------------------------------------------------------------------------- /tests/test_lazy_loaded.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | source "$test_dir/../virtualenvwrapper_lazy.sh" 10 | virtualenvwrapper_load 11 | } 12 | 13 | oneTimeTearDown() { 14 | rm -rf "$WORKON_HOME" 15 | } 16 | 17 | setUp () { 18 | echo 19 | } 20 | 21 | function_defined_normal() { 22 | name="$1" 23 | assertTrue "$name not defined" "type $name" 24 | assertFalse "$name still set to run lazy loader" "typeset -f $name | grep 'virtualenvwrapper_load'" 25 | } 26 | 27 | test_mkvirtualenv_defined_normal() { 28 | function_defined_normal mkvirtualenv 29 | } 30 | 31 | test_rmvirtualenv_defined_normal() { 32 | function_defined_normal rmvirtualenv 33 | } 34 | 35 | test_lsvirtualenv_defined_normal() { 36 | function_defined_normal lsvirtualenv 37 | } 38 | 39 | test_showvirtualenv_defined_normal() { 40 | function_defined_normal showvirtualenv 41 | } 42 | 43 | test_workon_defined_normal() { 44 | function_defined_normal workon 45 | } 46 | 47 | test_add2virtualenv_defined_normal() { 48 | function_defined_normal add2virtualenv 49 | } 50 | 51 | test_cdsitepackages_defined_normal() { 52 | function_defined_normal cdsitepackages 53 | } 54 | 55 | test_cdvirtualenv_defined_normal() { 56 | function_defined_normal cdvirtualenv 57 | } 58 | 59 | test_cdvirtualenv_defined_normal() { 60 | function_defined_normal cdvirtualenv 61 | } 62 | 63 | test_lssitepackages_defined_normal() { 64 | function_defined_normal lssitepackages 65 | } 66 | 67 | test_cpvirtualenv_defined_normal() { 68 | function_defined_normal cpvirtualenv 69 | } 70 | 71 | test_setvirtualenvproject_defined_normal() { 72 | function_defined_normal setvirtualenvproject 73 | } 74 | 75 | test_mkproject_defined_normal() { 76 | function_defined_normal mkproject 77 | } 78 | 79 | test_cdproject_defined_normal() { 80 | function_defined_normal cdproject 81 | } 82 | 83 | test_mktmpenv_defined_normal() { 84 | function_defined_normal mktmpenv 85 | } 86 | 87 | test_wipeenv_defined_normal() { 88 | function_defined_normal wipeenv 89 | } 90 | 91 | test_allvirtualenv_defined_normal() { 92 | function_defined_normal allvirtualenv 93 | } 94 | 95 | 96 | # test_virtualenvwrapper_initialize() { 97 | # assertTrue "Initialized" virtualenvwrapper_initialize 98 | # for hook in premkvirtualenv postmkvirtualenv prermvirtualenv postrmvirtualenv preactivate postactivate predeactivate postdeactivate 99 | # do 100 | # assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/$hook ]" 101 | # assertTrue "Global $WORKON_HOME/$hook is not executable" "[ -x $WORKON_HOME/$hook ]" 102 | # done 103 | # assertTrue "Log file was not created" "[ -f $WORKON_HOME/hook.log ]" 104 | # echo "echo GLOBAL initialize >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" 105 | # virtualenvwrapper_initialize 106 | # output=$(cat "$TMPDIR/catch_output") 107 | # expected="GLOBAL initialize" 108 | # assertSame "$expected" "$output" 109 | # } 110 | 111 | . "$test_dir/shunit2" 112 | -------------------------------------------------------------------------------- /tests/test_project_mk.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(dirname $0) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | rm -rf "$PROJECT_HOME" 10 | mkdir -p "$PROJECT_HOME" 11 | export VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME/hooks" 12 | load_wrappers 13 | } 14 | 15 | oneTimeTearDown() { 16 | rm -rf "$WORKON_HOME" 17 | rm -rf "$PROJECT_HOME" 18 | } 19 | 20 | setUp () { 21 | echo 22 | rm -f "$TMPDIR/catch_output" 23 | } 24 | 25 | tearDown () { 26 | type deactivate >/dev/null 2>&1 && deactivate 27 | } 28 | 29 | test_create_directories () { 30 | mkproject myproject1 >/dev/null 2>&1 31 | assertTrue "env directory not created" "[ -d $WORKON_HOME/myproject1 ]" 32 | assertTrue "project directory not created" "[ -d $PROJECT_HOME/myproject1 ]" 33 | } 34 | 35 | test_create_virtualenv () { 36 | mkproject myproject2 >/dev/null 2>&1 37 | assertSame "myproject2" $(basename "$VIRTUAL_ENV") 38 | assertSame "$PROJECT_HOME/myproject2" "$(cat $VIRTUAL_ENV/.project)" 39 | } 40 | 41 | test_hooks () { 42 | echo "echo GLOBAL premkproject \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$VIRTUALENVWRAPPER_HOOK_DIR/premkproject" 43 | chmod +x "$VIRTUALENVWRAPPER_HOOK_DIR/premkproject" 44 | echo "echo GLOBAL postmkproject \`pwd\` >> $TMPDIR/catch_output" > "$VIRTUALENVWRAPPER_HOOK_DIR/postmkproject" 45 | 46 | mkproject myproject3 >/dev/null 2>&1 47 | 48 | output=$(cat "$TMPDIR/catch_output") 49 | 50 | expected="GLOBAL premkproject $WORKON_HOME myproject3 51 | GLOBAL postmkproject $PROJECT_HOME/myproject3" 52 | assertSame "$expected" "$output" 53 | 54 | rm -f "$VIRTUALENVWRAPPER_HOOK_DIR/premkproject" 55 | rm -f "$VIRTUALENVWRAPPER_HOOK_DIR/postmkproject" 56 | } 57 | 58 | test_no_project_home () { 59 | old_home="$PROJECT_HOME" 60 | export PROJECT_HOME="$PROJECT_HOME/not_there" 61 | output=`mkproject should_not_be_created 2>&1` 62 | assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" 63 | PROJECT_HOME="$old_home" 64 | } 65 | 66 | test_project_exists () { 67 | mkproject myproject4 >/dev/null 2>&1 68 | output=`mkproject myproject4 2>&1` 69 | assertTrue "Did not see expected message 'already exists' in: $output" "echo $output | grep 'already exists'" 70 | output=`mkproject -f myproject4 2>&1` 71 | assertFalse "Saw unexpected message 'already exists' in: $output" "echo $output | grep 'already exists'" 72 | } 73 | 74 | test_same_workon_and_project_home () { 75 | old_project_home="$PROJECT_HOME" 76 | export PROJECT_HOME="$WORKON_HOME" 77 | mkproject myproject5 >/dev/null 2>&1 78 | assertTrue "env directory not created" "[ -d $WORKON_HOME/myproject1 ]" 79 | assertTrue "project directory was created" "[ -d $old_project_home/myproject1 ]" 80 | PROJECT_HOME="$old_project_home" 81 | } 82 | 83 | test_alternate_linkage_filename () { 84 | export VIRTUALENVWRAPPER_PROJECT_FILENAME=".not-project" 85 | mkproject myproject6 >/dev/null 2>&1 86 | assertSame "myproject6" $(basename "$VIRTUAL_ENV") 87 | assertSame "$PROJECT_HOME/myproject6" "$(cat $VIRTUAL_ENV/.not-project)" 88 | export VIRTUALENVWRAPPER_PROJECT_FILENAME=".project" 89 | } 90 | 91 | . "$test_dir/shunit2" 92 | -------------------------------------------------------------------------------- /README.es.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst -*- 2 | 3 | ################# 4 | virtualenvwrapper 5 | ################# 6 | 7 | virtualenvwrapper es un conjunto de extensiones de la herramienta de Ian 8 | Bicking `virtualenv `_. Las extensiones 9 | incluyen funciones para la creación y eliminación de entornos virtuales y por otro 10 | lado administración de tu rutina de desarrollo, haciendo fácil trabajar en más 11 | de un proyecto al mismo tiempo sin introducir conflictos entre sus dependencias. 12 | 13 | =============== 14 | Características 15 | =============== 16 | 17 | 1. Organiza todos tus entornos virtuales en un sólo lugar. 18 | 19 | 2. Funciones para administrar tus entornos virtuales (crear, eliminar, copiar). 20 | 21 | 3. Usa un sólo comando para cambiar entre los entornos. 22 | 23 | 4. Completa con Tab los comandos que toman un entorno virtual como argumento. 24 | 25 | 5. Ganchos configurables para todas las operaciones. 26 | 27 | 6. Sistema de plugins para la creación de extensiones compartibles. 28 | 29 | Rich Leland ha grabado un pequeño `screencast 30 | `__ 31 | mostrando las características de virtualenvwrapper. 32 | 33 | 34 | =========== 35 | Instalación 36 | =========== 37 | 38 | Ve a la `documentación del proyecto `__ 39 | para las instrucciones de instalación y configuración. 40 | 41 | Actualizar desde 1.x 42 | ==================== 43 | 44 | El script de shell que contiene las funciones ha sido renombrado en la serie 45 | 2.x para reflejar el hecho de que otros shells, además de bash, son soportados. En 46 | tu archivo de inicio del shell, cambia ``source 47 | /usr/local/bin/virtualenvwrapper_bashrc`` por ``source 48 | /usr/local/bin/virtualenvwrapper.sh``. 49 | 50 | ============== 51 | Contribuciones 52 | ============== 53 | 54 | Antes de contribuir con nuevas características al *core* de virtualenvwrapper, 55 | por favor considera, en vez, si no debe ser implementada como una extensión. 56 | 57 | Ve a la `documentación para desarrolladores 58 | `__ 59 | por trucos sobre parches. 60 | 61 | ======== 62 | Licencia 63 | ======== 64 | 65 | Copyright Doug Hellmann, All Rights Reserved 66 | 67 | Permission to use, copy, modify, and distribute this software and its 68 | documentation for any purpose and without fee is hereby granted, 69 | provided that the above copyright notice appear in all copies and that 70 | both that copyright notice and this permission notice appear in 71 | supporting documentation, and that the name of Doug Hellmann not be used 72 | in advertising or publicity pertaining to distribution of the software 73 | without specific, written prior permission. 74 | 75 | DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 76 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 77 | EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR 78 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 79 | USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 80 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 81 | PERFORMANCE OF THIS SOFTWARE. 82 | 83 | .. _github: https://github.com/python-virtualenvwrapper/virtualenvwrapper/ 84 | -------------------------------------------------------------------------------- /README.ja.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst -*- 2 | 3 | ################# 4 | virtualenvwrapper 5 | ################# 6 | 7 | virtualenvwrapper は Ian Bicking の 8 | `virtualenv `_ ツールの 9 | 拡張機能です。この拡張機能は仮想環境の作成・削除を行ったり、 10 | 開発ワークフローを管理するラッパーを提供します。このラッパーを 11 | 使用することで、開発環境の依存による競合を発生させず、1つ以上の 12 | プロジェクトで同時に作業し易くなります。 13 | 14 | ==== 15 | 機能 16 | ==== 17 | 18 | 1. 1つの開発環境で全ての仮想環境を構成する 19 | 20 | 2. 仮想環境を管理(作成、削除、コピー)するラッパー 21 | 22 | 3. たった1つのコマンドで仮想環境を切り替える 23 | 24 | 4. コマンドの引数として仮想環境がタブ補完できる 25 | 26 | 5. 全ての操作に対してユーザ設定でフックできる(:ref:`scripts` を参照) 27 | 28 | 6. さらに共有可能な拡張機能を作成できるプラグインシステム(:ref:`plugins` を参照) 29 | 30 | Rich Leland は virtualenvwrapper の機能を誇示するために短い 31 | `スクリーンキャスト `__ 32 | を作成しました。 33 | 34 | ============ 35 | インストール 36 | ============ 37 | 38 | インストールとインフラを設定するには 39 | `プロジェクトのドキュメント `__ 40 | を参照してください。 41 | 42 | サポートシェル 43 | ============== 44 | 45 | virtualenvwrapper は Bourne シェル互換の構文で定義された 46 | シェル *関数* のセットです。それは `bash`, `ksh` と `zsh` で 47 | テストされています。その他のシェルでも動作するかもしれませんが、 48 | ここに記載されていないシェルで動作することを発見したら私に 49 | 教えてください。もしあなたがその他のシェルで動作させるために 50 | virtualenvwrapper を完全に書き直すことなく修正できるなら、 51 | GitHub のプロジェクトページを通じて pull リクエストを 52 | 送ってください。あなたが非互換なシェル上で動作させるクローンを 53 | 作成するなら、このページでリンクを張るので私に連絡してください。 54 | 55 | Python バージョン 56 | ================= 57 | 58 | virtualenvwrapper は Python 2.4 - 2.7 でテストされています。 59 | 60 | 1.x からのアップグレード 61 | ======================== 62 | 63 | ラッパー関数を含むシェルスクリプトは 2.x バージョンで bash 64 | 以外のシェルをサポートするためにその名前が変更されました。 65 | あなたの起動ファイルの ``source /usr/local/bin/virtualenvwrapper_bashrc`` を 66 | ``source /usr/local/bin/virtualenvwrapper.sh`` へ変更してください。 67 | 68 | ==== 69 | 貢献 70 | ==== 71 | 72 | virtualenvwrapper のコアへ新しい機能を追加する前に、 73 | その代わりに機能拡張として実装すべきかどうかをよく考えてください。 74 | 75 | パッチを提供するための tips は 76 | `開発者ドキュメント `__ 77 | を参照してください。 78 | 79 | ======== 80 | サポート 81 | ======== 82 | 83 | 問題や機能を議論するには 84 | `virtualenvwrapper Google Group `__ 85 | に参加してください。 86 | 87 | `GitHub のバグトラッカー `__ 88 | でバグを報告してください。 89 | 90 | シェルエイリアス 91 | ================ 92 | 93 | virtualenvwrapper は大きなシェルスクリプトなので、 94 | 多くのアクションはシェルコマンドを使用します。 95 | あなたの環境が多くのシェルエイリアスやその他の 96 | カスタマイズを行っているなら、何かしら問題に 97 | 遭遇する可能性があります。バグトラッカーにバグを 98 | 報告する前に、そういったエイリアスを無効な *状態* で 99 | テストしてください。あなたがその問題を引き起こす 100 | エイリアスを判別できるなら virtualenvwrapper を 101 | もっと堅牢なものにすることに役立つでしょう。 102 | 103 | ========== 104 | ライセンス 105 | ========== 106 | 107 | Copyright Doug Hellmann, All Rights Reserved 108 | 109 | Permission to use, copy, modify, and distribute this software and its 110 | documentation for any purpose and without fee is hereby granted, 111 | provided that the above copyright notice appear in all copies and that 112 | both that copyright notice and this permission notice appear in 113 | supporting documentation, and that the name of Doug Hellmann not be used 114 | in advertising or publicity pertaining to distribution of the software 115 | without specific, written prior permission. 116 | 117 | DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 118 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 119 | EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR 120 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 121 | USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 122 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 123 | PERFORMANCE OF THIS SOFTWARE. 124 | -------------------------------------------------------------------------------- /tests/test_ls.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | } 15 | 16 | setUp () { 17 | echo 18 | } 19 | 20 | test_get_site_packages_dir () { 21 | mkvirtualenv "lssitepackagestest" >/dev/null 2>&1 22 | d=$(virtualenvwrapper_get_site_packages_dir) 23 | echo "site-packages in $d" 24 | assertTrue "site-packages dir $d does not exist" "[ -d $d ]" 25 | deactivate 26 | } 27 | 28 | test_lssitepackages () { 29 | mkvirtualenv "lssitepackagestest" >/dev/null 2>&1 30 | contents="$(lssitepackages)" 31 | assertTrue "did not find pip in site-packages: ${contents}" "echo $contents | grep -q pip" 32 | deactivate 33 | } 34 | 35 | test_lssitepackages_space_in_name () { 36 | # Only test with leading and internal spaces. Directory names with trailing spaces are legal, 37 | # and work with virtualenv on OSX, but error out on Linux. 38 | mkvirtualenv " space lssitepackagestest" >/dev/null 2>&1 39 | contents="$(lssitepackages)" 40 | assertTrue "did not find pip in site-packages: ${contents}" "echo $contents | grep -q pip" 41 | deactivate 42 | } 43 | 44 | test_lssitepackages_add2virtualenv () { 45 | mkvirtualenv "lssitepackagestest" >/dev/null 2>&1 46 | parent_dir=$(dirname $(pwd)) 47 | base_dir=$(basename $(pwd)) 48 | add2virtualenv "../$base_dir" 49 | contents="$(lssitepackages)" 50 | actual=$(echo $contents | grep $base_dir) 51 | expected=$(echo $contents) 52 | assertSame "$expected" "$actual" 53 | deactivate 54 | } 55 | 56 | test_lssitepackages_no_workon_home () { 57 | old_home="$WORKON_HOME" 58 | export WORKON_HOME="$WORKON_HOME/not_there" 59 | rm -rf "$WORKON_HOME" 60 | lssitepackages >"$old_home/output" 2>&1 61 | output=$(cat "$old_home/output") 62 | assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" 63 | WORKON_HOME="$old_home" 64 | } 65 | 66 | test_lsvirtualenv_no_workon_home () { 67 | old_home="$WORKON_HOME" 68 | export WORKON_HOME="$WORKON_HOME/not_there" 69 | rm -rf "$WORKON_HOME" 70 | lsvirtualenv >"$old_home/output" 2>&1 71 | output=$(cat "$old_home/output") 72 | assertTrue "Did not see expected message" "echo $output | grep -q 'does not exist'" 73 | WORKON_HOME="$old_home" 74 | } 75 | 76 | test_lsvirtualenv_space_in_workon_home () { 77 | old_home="$WORKON_HOME" 78 | export WORKON_HOME="$WORKON_HOME/with space" 79 | mkdir "$WORKON_HOME" 80 | (cd "$WORKON_HOME"; virtualenv testenv) >/dev/null 2>&1 81 | lsvirtualenv -b >"$old_home/output" 82 | output=$(cat "$old_home/output") 83 | assertTrue "Did not see expected message in \"$output\"" "echo $output | grep -q 'testenv'" 84 | WORKON_HOME="$old_home" 85 | } 86 | 87 | test_lsvirtualenv_space_in_env_name () { 88 | # Only test with leading and internal spaces. Directory names with trailing spaces are legal, 89 | # and work with virtualenv on OSX, but error out on Linux. 90 | mkvirtualenv " env with space" >/dev/null 2>&1 91 | lsvirtualenv -b >"$WORKON_HOME/output" 2>&1 92 | assertTrue "Did not see expected message in \"$output\"" "cat \"$WORKON_HOME/output\" | grep -q ' env with space'" 93 | } 94 | 95 | test_lsvirtualenv_blank_lines () { 96 | # There should be no blank lines in the list of virtualenvs 97 | mkvirtualenv "at-least-one-env" >/dev/null 2>&1 98 | assertFalse "Found blank line in virtualenvwrapper_show_workon_options output" "virtualenvwrapper_show_workon_options | grep -q '^$'" 99 | } 100 | 101 | 102 | . "$test_dir/shunit2" 103 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | 9 | # Test different python versions with bash on Ubuntu. 10 | ubuntu: 11 | name: Test Ubuntu 12 | runs-on: ubuntu-latest 13 | if: ${{ !startsWith(github.ref, 'refs/tags') }} 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | python-version: 19 | - 3.8 20 | - 3.9 21 | - "3.10" 22 | - "3.11" 23 | - "3.12" 24 | 25 | steps: 26 | - uses: actions/checkout@v6 27 | with: 28 | fetch-depth: 0 29 | 30 | - name: Set up Python ${{ matrix.python-version }} 31 | uses: actions/setup-python@v6 32 | with: 33 | python-version: ${{ matrix.python-version }} 34 | 35 | - name: Install dependencies 36 | run: python -m pip install tox 37 | 38 | - name: Run tests 39 | run: tox -e py 40 | 41 | # Test the latest python version with zsh on macOS 42 | zsh: 43 | name: Test Zsh 44 | runs-on: macos-latest 45 | if: ${{ !startsWith(github.ref, 'refs/tags') }} 46 | 47 | strategy: 48 | fail-fast: false 49 | 50 | steps: 51 | - uses: actions/checkout@v6 52 | with: 53 | fetch-depth: 0 54 | 55 | - name: Set up Python ${{ matrix.python-version }} 56 | uses: actions/setup-python@v6 57 | with: 58 | python-version: "3.11" 59 | 60 | - name: Install dependencies 61 | run: python -m pip install tox 62 | 63 | - name: Run tests 64 | run: tox -e zsh 65 | 66 | # Test different python versions with bash on macOS 67 | macos: 68 | name: Test macOS 69 | runs-on: macos-latest 70 | if: ${{ !startsWith(github.ref, 'refs/tags') }} 71 | 72 | strategy: 73 | fail-fast: false 74 | matrix: 75 | python-version: 76 | - 3.8 77 | - 3.9 78 | - "3.10" 79 | - "3.11" 80 | - "3.12" 81 | 82 | steps: 83 | - uses: actions/checkout@v6 84 | with: 85 | fetch-depth: 0 86 | 87 | - name: Set up Python ${{ matrix.python-version }} 88 | uses: actions/setup-python@v6 89 | with: 90 | python-version: ${{ matrix.python-version }} 91 | 92 | - name: Install dependencies 93 | run: python -m pip install tox 94 | 95 | - name: Run tests 96 | run: tox -e py 97 | 98 | # Run various style checkers 99 | style: 100 | runs-on: ubuntu-latest 101 | if: ${{ !startsWith(github.ref, 'refs/tags') }} 102 | 103 | strategy: 104 | fail-fast: false 105 | matrix: 106 | tox-environment: 107 | - docs 108 | - style 109 | - pkglint 110 | 111 | steps: 112 | - uses: actions/checkout@v6 113 | with: 114 | fetch-depth: 0 115 | 116 | - name: Set up Python 117 | uses: actions/setup-python@v6 118 | with: 119 | python-version: "3.11" 120 | 121 | - name: Install dependencies 122 | run: python -m pip install tox 123 | 124 | - name: Run 125 | run: tox -e ${{ matrix.tox-environment }} 126 | 127 | # Test build the docs 128 | docs: 129 | runs-on: ubuntu-latest 130 | if: ${{ !startsWith(github.ref, 'refs/tags') }} 131 | 132 | strategy: 133 | fail-fast: false 134 | 135 | steps: 136 | - uses: actions/checkout@v6 137 | with: 138 | fetch-depth: 0 139 | 140 | - name: Set up Python 141 | uses: actions/setup-python@v6 142 | with: 143 | # Pin this to the same version used on in .readthedocs.yaml 144 | python-version: "3.11" 145 | 146 | - name: Install dependencies 147 | run: python -m pip install tox 148 | 149 | - name: Run 150 | run: tox -e docs 151 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "setuptools_scm[toml]>=6.2"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | authors = [ 7 | {name = "Doug Hellmann", email = "doug@doughellmann.com"}, 8 | {name = "Jason Myers", email = "jason@mailthemyers.com"}, 9 | ] 10 | 11 | classifiers = [ 12 | "Development Status :: 5 - Production/Stable", 13 | "License :: OSI Approved :: MIT License", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.8", 17 | "Programming Language :: Python :: 3.9", 18 | "Programming Language :: Python :: 3.10", 19 | "Programming Language :: Python :: 3.11", 20 | "Programming Language :: Python :: 3.12", 21 | "Intended Audience :: Developers", 22 | "Environment :: Console", 23 | ] 24 | 25 | name = "virtualenvwrapper" 26 | description = "" 27 | dynamic = ["version"] 28 | keywords = ["virtualenv"] 29 | license = {text = "MIT"} 30 | readme = "README.txt" 31 | requires-python = ">=3.8" 32 | 33 | dependencies = [ 34 | "virtualenv", 35 | "virtualenv-clone", 36 | "stevedore", 37 | ] 38 | 39 | [project.optional-dependencies] 40 | linter = [ 41 | "flake8", 42 | ] 43 | build = [ 44 | "build", 45 | "twine", 46 | "check-python-versions", 47 | ] 48 | 49 | # https://github.com/pypa/setuptools_scm/ 50 | [tool.setuptools_scm] 51 | write_to = "virtualenvwrapper/version.py" 52 | 53 | [project.urls] 54 | homepage = "https://virtualenvwrapper.readthedocs.io/" 55 | repository = "https://github.com/python-virtualenvwrapper/virtualenvwrapper" 56 | 57 | [project.entry-points."virtualenvwrapper.initialize"] 58 | user_scripts = "virtualenvwrapper.user_scripts:initialize" 59 | project = "virtualenvwrapper.project:initialize" 60 | 61 | [project.entry-points."virtualenvwrapper.initialize_source"] 62 | user_scripts = "virtualenvwrapper.user_scripts:initialize_source" 63 | 64 | [project.entry-points."virtualenvwrapper.pre_mkvirtualenv"] 65 | user_scripts = "virtualenvwrapper.user_scripts:pre_mkvirtualenv" 66 | 67 | [project.entry-points."virtualenvwrapper.post_mkvirtualenv_source"] 68 | user_scripts = "virtualenvwrapper.user_scripts:post_mkvirtualenv_source" 69 | 70 | [project.entry-points."virtualenvwrapper.pre_cpvirtualenv"] 71 | user_scripts = "virtualenvwrapper.user_scripts:pre_cpvirtualenv" 72 | 73 | [project.entry-points."virtualenvwrapper.post_cpvirtualenv_source"] 74 | user_scripts = "virtualenvwrapper.user_scripts:post_cpvirtualenv_source" 75 | 76 | [project.entry-points."virtualenvwrapper.pre_rmvirtualenv"] 77 | user_scripts = "virtualenvwrapper.user_scripts:pre_rmvirtualenv" 78 | 79 | [project.entry-points."virtualenvwrapper.post_rmvirtualenv"] 80 | user_scripts = "virtualenvwrapper.user_scripts:post_rmvirtualenv" 81 | 82 | [project.entry-points."virtualenvwrapper.project.pre_mkproject"] 83 | project = "virtualenvwrapper.project:pre_mkproject" 84 | 85 | [project.entry-points."virtualenvwrapper.project.post_mkproject_source"] 86 | project = "virtualenvwrapper.project:post_mkproject_source" 87 | 88 | [project.entry-points."virtualenvwrapper.pre_activate"] 89 | user_scripts = "virtualenvwrapper.user_scripts:pre_activate" 90 | 91 | [project.entry-points."virtualenvwrapper.post_activate_source"] 92 | project = "virtualenvwrapper.project:post_activate_source" 93 | user_scripts = "virtualenvwrapper.user_scripts:post_activate_source" 94 | 95 | [project.entry-points."virtualenvwrapper.pre_deactivate_source"] 96 | project = "virtualenvwrapper.project:pre_deactivate_source" 97 | user_scripts = "virtualenvwrapper.user_scripts:pre_deactivate_source" 98 | 99 | [project.entry-points."virtualenvwrapper.post_deactivate_source"] 100 | user_scripts = "virtualenvwrapper.user_scripts:post_deactivate_source" 101 | 102 | [project.entry-points."virtualenvwrapper.get_env_details"] 103 | user_scripts = "virtualenvwrapper.user_scripts:get_env_details" 104 | -------------------------------------------------------------------------------- /tests/test_mkvirtualenv_associate.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | rm -f "$test_dir/requirements.txt" 15 | } 16 | 17 | setUp () { 18 | echo 19 | echo "#!/bin/sh" > "$WORKON_HOME/preactivate" 20 | echo "#!/bin/sh" > "$WORKON_HOME/postactivate" 21 | rm -f "$TMPDIR/catch_output" 22 | cd "$WORKON_HOME" 23 | } 24 | 25 | test_associate() { 26 | n=1 27 | project="$WORKON_HOME/project$n" 28 | mkdir "$project" 29 | env="env$n" 30 | ptrfile="$WORKON_HOME/$env/.project" 31 | mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 32 | assertTrue ".project not found" "[ -f $ptrfile ]" 33 | assertEquals "$ptrfile contains wrong content" "$project" "$(cat $ptrfile)" 34 | } 35 | 36 | test_associate_relative_path() { 37 | n=2 38 | project="project$n" 39 | mkdir "$project" 40 | env="env$n" 41 | ptrfile="$WORKON_HOME/$env/.project" 42 | mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 43 | assertTrue ".project not found" "[ -f $ptrfile ]" 44 | assertEquals \ 45 | "$ptrfile contains wrong content" \ 46 | "$WORKON_HOME/$project" \ 47 | "$(cat $ptrfile | sed 's|^/private||')" 48 | } 49 | 50 | test_associate_not_a_directory() { 51 | n=3 52 | project="project$n" 53 | touch "$project" 54 | env="env$n" 55 | ptrfile="$WORKON_HOME/$env/.project" 56 | mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 57 | RC=$? 58 | assertTrue "mkvirtualenv should have failed" "[ $RC -ne 0 ]" 59 | } 60 | 61 | test_associate_does_not_exist() { 62 | n=4 63 | project="project$n" 64 | env="env$n" 65 | ptrfile="$WORKON_HOME/$env/.project" 66 | mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 67 | RC=$? 68 | assertTrue "mkvirtualenv should have failed" "[ $RC -ne 0 ]" 69 | } 70 | 71 | test_preactivate() { 72 | n=5 73 | project="project$n" 74 | mkdir "$project" 75 | env="env$n" 76 | ptrfile="$WORKON_HOME/$env/.project" 77 | cat - >"$WORKON_HOME/preactivate" <> "$TMPDIR/catch_output" 82 | else 83 | echo noexists >> "$TMPDIR/catch_output" 84 | fi 85 | EOF 86 | chmod +x "$WORKON_HOME/preactivate" 87 | mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 88 | assertSame "preactivate did not find file" "exists" "$(cat $TMPDIR/catch_output)" 89 | } 90 | 91 | test_postactivate() { 92 | n=6 93 | project="project$n" 94 | mkdir "$project" 95 | env="env$n" 96 | ptrfile="$WORKON_HOME/$env/.project" 97 | cat - >"$WORKON_HOME/postactivate" <> "$TMPDIR/catch_output" 102 | else 103 | echo noexists >> "$TMPDIR/catch_output" 104 | fi 105 | EOF 106 | chmod +x "$WORKON_HOME/postactivate" 107 | mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 108 | assertSame "postactivate did not find file" "exists" "$(cat $TMPDIR/catch_output)" 109 | } 110 | 111 | test_associate_relative_with_dots() { 112 | cd "$WORKON_HOME" 113 | n=7 114 | project="project$n" 115 | mkdir $project 116 | mkdir $project.sibling 117 | cd $project.sibling 118 | # Change the reference to a sibling directory 119 | project="../$project" 120 | env="env$n" 121 | ptrfile="$WORKON_HOME/$env/.project" 122 | mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 123 | assertTrue ".project not found" "[ -f $ptrfile ]" 124 | # Sometimes OS X prepends /private on the front of the temporary 125 | # directory, but the directory is the same as without it, so strip 126 | # it if we see the value in the project file. 127 | assertEquals \ 128 | "$ptrfile contains wrong content" \ 129 | "$WORKON_HOME/project$n" \ 130 | "$(cat $ptrfile | sed 's|^/private||')" 131 | } 132 | 133 | . "$test_dir/shunit2" 134 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst -*- 2 | 3 | ################# 4 | virtualenvwrapper 5 | ################# 6 | 7 | virtualenvwrapper is a set of extensions to Ian Bicking's `virtualenv 8 | `_ tool. The extensions include 9 | wrappers for creating and deleting virtual environments and otherwise 10 | managing your development workflow, making it easier to work on more 11 | than one project at a time without introducing conflicts in their 12 | dependencies. 13 | 14 | **Warning:** The 5.x release requires virtualenv 20+ 15 | 16 | ======== 17 | Features 18 | ======== 19 | 20 | 1. Organizes all of your virtual environments in one place. 21 | 22 | 2. Wrappers for creating, copying and deleting environments, including 23 | user-configurable hooks. 24 | 25 | 3. Use a single command to switch between environments. 26 | 27 | 4. Tab completion for commands that take a virtual environment as 28 | argument. 29 | 30 | 5. User-configurable hooks for all operations. 31 | 32 | 6. Plugin system for more creating sharable extensions. 33 | 34 | Rich Leland has created a short `screencast 35 | `__ showing off the features of 36 | virtualenvwrapper. 37 | 38 | ============ 39 | Installation 40 | ============ 41 | 42 | See the `project documentation 43 | `__ for 44 | installation and setup instructions. 45 | 46 | Supported Shells 47 | ================ 48 | 49 | virtualenvwrapper is a set of shell *functions* defined in Bourne 50 | shell compatible syntax. It is tested under ``bash`` and 51 | ``zsh``. It may work with other shells, so if you find that it does 52 | work with a shell not listed here please let us know by opening a 53 | `ticket on GitHub 54 | `_. 55 | If you can modify it to work with another shell, without completely 56 | rewriting it, send a pull request through the `GitHub project page 57 | `_. If 58 | you write a clone to work with an incompatible shell, let us know and 59 | we will link to it from this page. 60 | 61 | Python Versions 62 | =============== 63 | 64 | virtualenvwrapper is tested under Python 3.8 - 3.12 on macOS and Linux. 65 | 66 | ======= 67 | Support 68 | ======= 69 | 70 | Join the `virtualenvwrapper Google Group 71 | `__ to discuss 72 | issues and features. 73 | 74 | Report bugs via the `bug tracker on GitHub 75 | `__. 76 | 77 | Shell Aliases 78 | ============= 79 | 80 | Since virtualenvwrapper is largely a shell script, it uses shell 81 | commands for a lot of its actions. If your environment makes heavy 82 | use of shell aliases or other customizations, you may encounter 83 | issues. Before reporting bugs in the bug tracker, please test 84 | *without* your aliases enabled. If you can identify the alias causing 85 | the problem, that will help make virtualenvwrapper more robust. 86 | 87 | ========== 88 | Change Log 89 | ========== 90 | 91 | The `release history`_ is part of the project documentation. 92 | 93 | .. _release history: https://virtualenvwrapper.readthedocs.io/en/latest/history.html 94 | 95 | 96 | ======= 97 | License 98 | ======= 99 | 100 | Copyright Doug Hellmann, All Rights Reserved 101 | 102 | Permission to use, copy, modify, and distribute this software and its 103 | documentation for any purpose and without fee is hereby granted, 104 | provided that the above copyright notice appear in all copies and that 105 | both that copyright notice and this permission notice appear in 106 | supporting documentation, and that the name of Doug Hellmann not be used 107 | in advertising or publicity pertaining to distribution of the software 108 | without specific, written prior permission. 109 | 110 | DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 111 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 112 | EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR 113 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 114 | USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 115 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 116 | PERFORMANCE OF THIS SOFTWARE. 117 | -------------------------------------------------------------------------------- /docs/source/developers.rst: -------------------------------------------------------------------------------- 1 | ############## 2 | For Developers 3 | ############## 4 | 5 | If you would like to contribute to virtualenvwrapper directly, these 6 | instructions should help you get started. Patches, bug reports, and 7 | feature requests are all welcome through the `GitHub site 8 | `_. Contributions 9 | in the form of patches or pull requests are easier to integrate and 10 | will receive priority attention. 11 | 12 | .. note:: 13 | 14 | Before contributing new features to virtualenvwrapper core, please 15 | consider whether they should be implemented as an extension instead. 16 | 17 | Building Documentation 18 | ====================== 19 | 20 | The documentation for virtualenvwrapper is written in reStructuredText 21 | and converted to HTML using Sphinx. The build itself is driven by 22 | make. You will need the following packages in order to build the 23 | docs: 24 | 25 | - Sphinx 26 | - docutils 27 | - sphinxcontrib-bitbucket 28 | 29 | Once all of the tools are installed into a virtualenv using 30 | pip, run ``make html`` to generate the HTML version of the 31 | documentation:: 32 | 33 | $ make html 34 | rm -rf virtualenvwrapper/docs 35 | (cd docs && make html SPHINXOPTS="-c sphinx/pkg") 36 | sphinx-build -b html -d build/doctrees -c sphinx/pkg source build/html 37 | Running Sphinx v0.6.4 38 | loading pickled environment... done 39 | building [html]: targets for 2 source files that are out of date 40 | updating environment: 0 added, 2 changed, 0 removed 41 | reading sources... [ 50%] command_ref 42 | reading sources... [100%] developers 43 | 44 | looking for now-outdated files... none found 45 | pickling environment... done 46 | checking consistency... done 47 | preparing documents... done 48 | writing output... [ 33%] command_ref 49 | writing output... [ 66%] developers 50 | writing output... [100%] index 51 | 52 | writing additional files... search 53 | copying static files... WARNING: static directory '/Users/dhellmann/Devel/virtualenvwrapper/plugins/docs/sphinx/pkg/static' does not exist 54 | done 55 | dumping search index... done 56 | dumping object inventory... done 57 | build succeeded, 1 warning. 58 | 59 | Build finished. The HTML pages are in build/html. 60 | cp -r docs/build/html virtualenvwrapper/docs 61 | 62 | The output version of the documentation ends up in 63 | ``./virtualenvwrapper/docs`` inside your sandbox. 64 | 65 | Running Tests 66 | ============= 67 | 68 | The test suite for virtualenvwrapper uses shunit2_ and tox_. The 69 | shunit2 source is included in the ``tests`` directory, but tox must be 70 | installed separately (``pip install tox``). 71 | 72 | To run the tests under bash and zsh for the default Python, 73 | run ``tox`` from the top level directory of the hg repository:: 74 | 75 | $ tox 76 | 77 | To run individual test scripts, use a command like:: 78 | 79 | $ tox -- tests/test_cd.sh 80 | 81 | To run tests under a single version of Python, specify the appropriate 82 | environment when running tox:: 83 | 84 | $ tox -e py311 85 | 86 | Combine the two modes to run specific tests with a single version of 87 | Python:: 88 | 89 | $ tox -e py311 -- tests/test_cd.sh 90 | 91 | To stop the test suite as soon as any test fails, use the `fast` tox 92 | target:: 93 | 94 | $ tox -e fast 95 | 96 | Add new tests by modifying an existing file or creating new script in 97 | the ``tests`` directory. 98 | 99 | .. _shunit2: https://github.com/kward/shunit2 100 | 101 | .. _tox: https://tox.wiki/ 102 | 103 | .. _developer-templates: 104 | 105 | Creating a New Template 106 | ======================= 107 | 108 | virtualenvwrapper.project templates work like `virtualenvwrapper 109 | plugins 110 | `__. 111 | The *entry point* group name is 112 | ``virtualenvwrapper.project.template``. Configure your entry point to 113 | refer to a function that will **run** (source hooks are not supported 114 | for templates). 115 | 116 | The argument to the template function is the name of the project being 117 | created. The current working directory is the directory created to 118 | hold the project files (``$PROJECT_HOME/$envname``). 119 | 120 | Help Text 121 | --------- 122 | 123 | One difference between project templates and other virtualenvwrapper 124 | extensions is that only the templates specified by the user are run. 125 | The ``mkproject`` command has a help option to give the user a list of 126 | the available templates. The names are taken from the registered 127 | entry point names, and the descriptions are taken from the docstrings 128 | for the template functions. 129 | -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | } 15 | 16 | setUp () { 17 | echo 18 | unset VIRTUALENVWRAPPER_INITIALIZED 19 | rm -f "$TMPDIR/catch_output" 20 | } 21 | 22 | SOURCE_SCRIPTS="initialize postmkvirtualenv predeactivate postdeactivate postactivate " 23 | RUN_SCRIPTS="premkvirtualenv prermvirtualenv postrmvirtualenv preactivate get_env_details" 24 | 25 | test_virtualenvwrapper_initialize() { 26 | assertTrue "Initialized" virtualenvwrapper_initialize 27 | for hook in $SOURCE_SCRIPTS 28 | do 29 | assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/$hook ]" 30 | assertFalse "Global $WORKON_HOME/$hook is executable" "[ -x $WORKON_HOME/$hook ]" 31 | done 32 | for hook in $RUN_SCRIPTS 33 | do 34 | assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/$hook ]" 35 | assertTrue "Global $WORKON_HOME/$hook is executable" "[ -x $WORKON_HOME/$hook ]" 36 | done 37 | echo "echo GLOBAL initialize >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" 38 | virtualenvwrapper_initialize 39 | output=$(cat "$TMPDIR/catch_output") 40 | expected="GLOBAL initialize" 41 | assertSame "$expected" "$output" 42 | } 43 | 44 | test_virtualenvwrapper_space_in_workon_home() { 45 | before="$WORKON_HOME" 46 | export WORKON_HOME="$WORKON_HOME/this has spaces" 47 | expected="$WORKON_HOME" 48 | mkdir -p "$expected" 49 | virtualenvwrapper_initialize 50 | RC=$? 51 | assertSame "$expected" "$WORKON_HOME" 52 | assertSame "0" "$RC" 53 | export WORKON_HOME="$before" 54 | } 55 | 56 | test_virtualenvwrapper_verify_workon_home() { 57 | assertTrue "WORKON_HOME not verified" virtualenvwrapper_verify_workon_home 58 | } 59 | 60 | test_virtualenvwrapper_verify_workon_home_missing_dir() { 61 | old_home="$WORKON_HOME" 62 | WORKON_HOME="$WORKON_HOME/not_there" 63 | assertTrue "Directory already exists" "[ ! -d \"$WORKON_HOME\" ]" 64 | virtualenvwrapper_verify_workon_home >"$old_home/output" 2>&1 65 | output=$(cat "$old_home/output") 66 | assertSame "NOTE: Virtual environments directory $WORKON_HOME does not exist. Creating..." "$output" 67 | WORKON_HOME="$old_home" 68 | } 69 | 70 | test_virtualenvwrapper_verify_workon_home_missing_dir_quiet() { 71 | old_home="$WORKON_HOME" 72 | WORKON_HOME="$WORKON_HOME/not_there_quiet" 73 | assertTrue "Directory already exists" "[ ! -d \"$WORKON_HOME\" ]" 74 | output=$(virtualenvwrapper_verify_workon_home -q 2>&1) 75 | assertSame "" "$output" 76 | WORKON_HOME="$old_home" 77 | } 78 | 79 | test_virtualenvwrapper_verify_workon_home_missing_dir_grep_options() { 80 | old_home="$WORKON_HOME" 81 | WORKON_HOME="$WORKON_HOME/not_there" 82 | # This should prevent the message from being found if it isn't 83 | # unset correctly. 84 | export GREP_OPTIONS="--count" 85 | assertTrue "WORKON_HOME not verified" virtualenvwrapper_verify_workon_home 86 | WORKON_HOME="$old_home" 87 | unset GREP_OPTIONS 88 | } 89 | 90 | test_python_interpreter_set_incorrectly() { 91 | return_to="$(pwd)" 92 | cd "$WORKON_HOME" 93 | mkvirtualenv no_wrappers >/dev/null 2>&1 94 | RC=$? 95 | assertEquals "mkvirtualenv return code wrong" "0" "$RC" 96 | expected="No module named virtualenvwrapper" 97 | # test_shell is set by tests/run_tests 98 | if [ "$test_shell" = "" ] 99 | then 100 | export test_shell=$SHELL 101 | fi 102 | outfilename="$WORKON_HOME/test_out.$$" 103 | subshell_output=$(VIRTUALENVWRAPPER_PYTHON="$WORKON_HOME/no_wrappers/bin/python" $test_shell $return_to/virtualenvwrapper.sh >"$outfilename" 2>&1) 104 | #echo "$subshell_output" 105 | cat "$outfilename" | sed "s/'//g" | grep -q "$expected" 2>&1 106 | found_it=$? 107 | #echo "$found_it" 108 | assertTrue "Expected \'$expected\', got: \'$(cat "$outfilename")\'" "[ $found_it -eq 0 ]" 109 | assertFalse "Failed to detect invalid Python location" "VIRTUALENVWRAPPER_PYTHON=$VIRTUAL_ENV/bin/python virtualenvwrapper_run_hook initialize >/dev/null 2>&1" 110 | cd "$return_to" 111 | deactivate 112 | } 113 | 114 | test_virtualenvwrapper_verify_virtualenv(){ 115 | assertTrue "Verified unable to verify virtualenv" virtualenvwrapper_verify_virtualenv 116 | 117 | VIRTUALENVWRAPPER_VIRTUALENV="thiscannotpossiblyexist123" 118 | assertFalse "Incorrectly verified virtualenv" virtualenvwrapper_verify_virtualenv 119 | } 120 | 121 | test_virtualenvwrapper_verify_virtualenv_clone(){ 122 | assertTrue "Verified unable to verify virtualenv_clone" virtualenvwrapper_verify_virtualenv_clone 123 | 124 | VIRTUALENVWRAPPER_VIRTUALENV_CLONE="thiscannotpossiblyexist123" 125 | assertFalse "Incorrectly verified virtualenv_clone" virtualenvwrapper_verify_virtualenv_clone 126 | } 127 | 128 | . "$test_dir/shunit2" 129 | -------------------------------------------------------------------------------- /tests/test_cp.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | setUp () { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | rm -f "$TMPDIR/catch_output" 11 | echo 12 | } 13 | 14 | tearDown() { 15 | if type deactivate >/dev/null 2>&1 16 | then 17 | deactivate 18 | fi 19 | rm -rf "$WORKON_HOME" 20 | } 21 | 22 | test_new_env_activated () { 23 | mkvirtualenv "source" >/dev/null 2>&1 24 | RC=$? 25 | assertEquals "0" "$RC" 26 | (cd tests/testpackage && pip install .) >/dev/null 2>&1 27 | cpvirtualenv "source" "destination" >/dev/null 2>&1 28 | rmvirtualenv "source" >/dev/null 2>&1 29 | testscript="$(which testscript.py)" 30 | assertTrue "Environment test script not found in path" "[ $WORKON_HOME/destination/bin/testscript.py -ef $testscript ]" 31 | testscriptcontent="$(cat $testscript)" 32 | assertTrue "No cpvirtualenvtest in $testscriptcontent" "echo $testscriptcontent | grep cpvirtualenvtest" 33 | assertTrue virtualenvwrapper_verify_active_environment 34 | } 35 | 36 | test_virtual_env_variable () { 37 | mkvirtualenv "source" >/dev/null 2>&1 38 | cpvirtualenv "source" "destination" >/dev/null 2>&1 39 | assertSame "Wrong virtualenv name" "destination" $(basename "$VIRTUAL_ENV") 40 | assertTrue "$WORKON_HOME not in $VIRTUAL_ENV" "echo $VIRTUAL_ENV | grep -q $WORKON_HOME" 41 | } 42 | 43 | test_virtual_env_variable_space_in_name () { 44 | # Only test with leading and internal spaces. Directory names with trailing spaces are legal, 45 | # and work with virtualenv on OSX, but error out on Linux. 46 | mkvirtualenv " space source" >/dev/null 2>&1 47 | cpvirtualenv " space source" " space destination" >/dev/null 2>&1 48 | assertSame "Wrong virtualenv name" " space destination" "$(basename "$VIRTUAL_ENV")" 49 | assertTrue "$WORKON_HOME not in $VIRTUAL_ENV" "echo $VIRTUAL_ENV | grep -q $WORKON_HOME" 50 | } 51 | 52 | fake_venv () { 53 | ### 54 | # create a silly file to ensure copy happens 55 | ### 56 | typeset envname="$1" 57 | virtualenv $@ 58 | touch "$WORKON_HOME/$envname/fake_virtualenv_was_here" 59 | } 60 | 61 | fake_venv_clone () { 62 | ### 63 | # create a silly file to ensure copy happens 64 | ### 65 | typeset src_path="$1" 66 | touch "$src_path/fake_virtualenv_clone_was_here" 67 | virtualenv-clone $@ 68 | } 69 | 70 | test_virtualenvwrapper_virtualenv_variable () { 71 | 72 | eval 'virtualenvwrapper_verify_virtualenv () { 73 | return 0 74 | }' 75 | 76 | VIRTUALENVWRAPPER_VIRTUALENV=fake_venv 77 | assertSame "VIRTUALENVWRAPPER_VIRTUALENV is not set correctly" "$VIRTUALENVWRAPPER_VIRTUALENV" "fake_venv" 78 | 79 | mkvirtualenv "source" >/dev/null 2>&1 80 | assertTrue "Fake file not made in fake_venv" "[ -f "$VIRTUAL_ENV/fake_virtualenv_was_here" ]" 81 | cpvirtualenv "source" "destination" >/dev/null 2>&1 82 | unset VIRTUALENVWRAPPER_VIRTUALENV 83 | assertTrue "VIRTUALENVWRAPPER_CLONE did not clone fake file" "[ -f $WORKON_HOME/destination/fake_virtualenv_was_here ]" 84 | } 85 | 86 | test_virtualenvwrapper_virtualenv_clone_variable () { 87 | 88 | eval 'virtualenvwrapper_verify_virtualenv_clone () { 89 | return 0 90 | }' 91 | 92 | VIRTUALENVWRAPPER_VIRTUALENV_CLONE=fake_venv_clone 93 | assertSame "VIRTUALENVWRAPPER_VIRTUALENV_CLONE is not set correctly" "$VIRTUALENVWRAPPER_VIRTUALENV_CLONE" "fake_venv_clone" 94 | 95 | mkvirtualenv "source" >/dev/null 2>&1 96 | cpvirtualenv "source" "destination" >/dev/null 2>&1 97 | unset VIRTUALENVWRAPPER_VIRTUALENV_CLONE 98 | assertTrue "VIRTUALENVWRAPPER_CLONE did not clone fake file" "[ -f $WORKON_HOME/destination/fake_virtualenv_clone_was_here ]" 99 | } 100 | 101 | test_source_does_not_exist () { 102 | assertSame "Please provide a valid virtualenv to copy." "$(cpvirtualenv virtualenvthatdoesntexist foo)" 103 | } 104 | 105 | test_hooks () { 106 | mkvirtualenv "source" >/dev/null 2>&1 107 | 108 | # Set the interpreter of the hook script to the simple shell 109 | echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" 110 | echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" 111 | chmod +x "$WORKON_HOME/premkvirtualenv" 112 | 113 | echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" 114 | 115 | # Set the interpreter of the hook script to the simple shell 116 | echo "#!/bin/sh" > "$WORKON_HOME/precpvirtualenv" 117 | echo "echo GLOBAL precpvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/precpvirtualenv" 118 | chmod +x "$WORKON_HOME/precpvirtualenv" 119 | 120 | # Set the interpreter of the hook script to the simple shell 121 | echo "#!/bin/sh" > "$WORKON_HOME/postcpvirtualenv" 122 | echo "echo GLOBAL postcpvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postcpvirtualenv" 123 | 124 | cpvirtualenv "source" "destination" >/dev/null 2>&1 125 | 126 | output=$(cat "$TMPDIR/catch_output") 127 | workon_home_as_pwd=$(cd $WORKON_HOME; pwd) 128 | 129 | expected="GLOBAL precpvirtualenv $workon_home_as_pwd $workon_home_as_pwd/source destination 130 | GLOBAL premkvirtualenv $workon_home_as_pwd destination 131 | GLOBAL postmkvirtualenv 132 | GLOBAL postcpvirtualenv" 133 | 134 | assertSame "$expected" "$output" 135 | 136 | rm -f "$WORKON_HOME/premkvirtualenv" 137 | rm -f "$WORKON_HOME/postmkvirtualenv" 138 | } 139 | 140 | . "$test_dir/shunit2" 141 | -------------------------------------------------------------------------------- /tests/test_add2virtualenv.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers >/dev/null 2>&1 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | } 15 | 16 | setUp () { 17 | echo 18 | } 19 | 20 | test_add2virtualenv () { 21 | mkvirtualenv "pathtest" >/dev/null 2>&1 22 | full_path=$(pwd) 23 | add2virtualenv "$full_path" 24 | cdsitepackages 25 | # Check contents of path file 26 | path_file="./_virtualenv_path_extensions.pth" 27 | assertTrue "No $full_path in $(cat $path_file)" "grep -q $full_path $path_file" 28 | assertTrue "No path insert code in $(cat $path_file)" "grep -q sys.__egginsert $path_file" 29 | # Check the path we inserted is actually at the top 30 | expected="$full_path" 31 | actual=$($WORKON_HOME/pathtest/bin/python -c "import sys; sys.stdout.write(sys.path[1]+'\n')") 32 | assertSame "$expected" "$actual" 33 | # Make sure the temporary file created 34 | # during the edit was removed 35 | assertFalse "Temporary file ${path_file}.tmp still exists" "[ -f ${path_file}.tmp ]" 36 | cd - >/dev/null 2>&1 37 | } 38 | 39 | test_add2virtualenv_zsh_noclobber () { 40 | # See issue #137 41 | if [ ! -z $ZSH_VERSION ] 42 | then 43 | set -o noclobber 44 | elif [[ "$SHELL" =~ "bash" ]] 45 | then 46 | shopt -o -s noclobber 47 | else 48 | return 0 49 | fi 50 | mkvirtualenv "pathtest_noclobber" >/dev/null 2>&1 51 | full_path=$(pwd) 52 | add2virtualenv "$full_path" 53 | RC=$? 54 | if [ ! -z $ZSH_VERSION ] 55 | then 56 | unsetopt noclobber 57 | elif [[ "/$SHELL" =~ "/bash" ]] 58 | then 59 | shopt -o -u noclobber 60 | fi 61 | assertEquals "0" "$RC" 62 | cdsitepackages 63 | # Check contents of path file 64 | path_file="./_virtualenv_path_extensions.pth" 65 | assertTrue "No $full_path in $(cat $path_file)" "grep -q $full_path $path_file" 66 | assertTrue "No path insert code in $(cat $path_file)" "grep -q sys.__egginsert $path_file" 67 | # Check the path we inserted is actually at the top 68 | expected="$full_path" 69 | actual=$($WORKON_HOME/pathtest_noclobber/bin/python -c "import sys; sys.stdout.write(sys.path[1]+'\n')") 70 | assertSame "$expected" "$actual" 71 | # Make sure the temporary file created 72 | # during the edit was removed 73 | assertFalse "Temporary file ${path_file}.tmp still exists" "[ -f ${path_file}.tmp ]" 74 | cd - >/dev/null 2>&1 75 | } 76 | 77 | test_add2virtualenv_relative () { 78 | mkvirtualenv "pathtest_relative" >/dev/null 2>&1 79 | parent_dir=$(dirname $(pwd)) 80 | base_dir=$(basename $(pwd)) 81 | add2virtualenv "../$base_dir" 82 | cdsitepackages 83 | path_file="./_virtualenv_path_extensions.pth" 84 | assertTrue "No $parent_dir/$base_dir in \"`cat $path_file`\"" "grep -q \"$parent_dir/$base_dir\" $path_file" 85 | cd - >/dev/null 2>&1 86 | } 87 | 88 | test_add2virtualenv_space () { 89 | # see #132 90 | mkvirtualenv "pathtest_space" >/dev/null 2>&1 91 | parent_dir=$(dirname $(pwd)) 92 | cdvirtualenv 93 | mkdir 'a b' 94 | add2virtualenv 'a b' 95 | cdsitepackages 96 | path_file="./_virtualenv_path_extensions.pth" 97 | assertTrue "No 'a b' in \"`cat $path_file`\"" "grep -q 'a b' $path_file" 98 | cd - >/dev/null 2>&1 99 | } 100 | 101 | test_add2virtualenv_ampersand () { 102 | # see #132 103 | mkvirtualenv "pathtest_ampersand" >/dev/null 2>&1 104 | parent_dir=$(dirname $(pwd)) 105 | cdvirtualenv 106 | mkdir 'a & b' 107 | add2virtualenv 'a & b' 108 | cdsitepackages 109 | path_file="./_virtualenv_path_extensions.pth" 110 | assertTrue "No 'a & b' in \"`cat $path_file`\"" "grep -q 'a & b' $path_file" 111 | cd - >/dev/null 2>&1 112 | } 113 | 114 | test_add2virtualenv_delete () { 115 | path_file="./_virtualenv_path_extensions.pth" 116 | mkvirtualenv "pathtest_delete" >/dev/null 2>&1 117 | cdsitepackages 118 | # Make sure it was added 119 | add2virtualenv "/full/path" 120 | assertTrue "No /full/path in $(cat $path_file)" "grep -q /full/path $path_file" 121 | # Remove it and verify that change 122 | add2virtualenv -d "/full/path" 123 | assertFalse "/full/path in `cat $path_file`" "grep -q /full/path $path_file" 124 | cd - >/dev/null 2>&1 125 | } 126 | 127 | test_add2virtualenv_delete_space () { 128 | path_file="./_virtualenv_path_extensions.pth" 129 | mkvirtualenv "pathtest_delete_space" >/dev/null 2>&1 130 | cdsitepackages 131 | # Make sure it was added 132 | add2virtualenv "/full/path with spaces" 133 | assertTrue "No /full/path with spaces in $(cat $path_file)" "grep -q '/full/path with spaces' $path_file" 134 | # Remove it and verify that change 135 | add2virtualenv -d "/full/path with spaces" 136 | assertFalse "/full/path with spaces in `cat $path_file`" "grep -q '/full/path with spaces' $path_file" 137 | cd - >/dev/null 2>&1 138 | } 139 | 140 | test_add2virtualenv_delete_ampersand () { 141 | path_file="./_virtualenv_path_extensions.pth" 142 | mkvirtualenv "pathtest_delete_ampersand" >/dev/null 2>&1 143 | cdsitepackages 144 | # Make sure it was added 145 | add2virtualenv "/full/path & dir" 146 | assertTrue "No /full/path & dir in $(cat $path_file)" "grep -q '/full/path & dir' $path_file" 147 | # Remove it and verify that change 148 | add2virtualenv -d "/full/path & dir" 149 | assertFalse "/full/path & dir in `cat $path_file`" "grep -q '/full/path & dir' $path_file" 150 | cd - >/dev/null 2>&1 151 | } 152 | 153 | 154 | . "$test_dir/shunit2" 155 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/virtualenvwrapper.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/virtualenvwrapper.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/virtualenvwrapper" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/virtualenvwrapper" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /tests/test_workon.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | mkvirtualenv "test1" >/dev/null 2>&1 11 | mkvirtualenv "test2" >/dev/null 2>&1 12 | # Only test with leading and internal spaces. Directory names with trailing spaces are legal, 13 | # and work with virtualenv on OSX, but error out on Linux. 14 | mkvirtualenv " env with space" >/dev/null 2>&1 15 | deactivate >/dev/null 2>&1 16 | } 17 | 18 | oneTimeTearDown() { 19 | rm -rf "$WORKON_HOME" 20 | } 21 | 22 | setUp () { 23 | echo 24 | rm -f "$TMPDIR/catch_output" 25 | } 26 | 27 | tearDown () { 28 | deactivate >/dev/null 2>&1 29 | } 30 | 31 | test_workon () { 32 | workon test1 33 | assertTrue virtualenvwrapper_verify_active_environment 34 | assertSame "test1" $(basename "$VIRTUAL_ENV") 35 | } 36 | 37 | test_workon_activate_hooks () { 38 | for t in pre post 39 | do 40 | echo "#!/bin/sh" > "$WORKON_HOME/${t}activate" 41 | echo "echo GLOBAL ${t}activate >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/${t}activate" 42 | chmod +x "$WORKON_HOME/${t}activate" 43 | 44 | echo "#!/bin/sh" > "$WORKON_HOME/test2/bin/${t}activate" 45 | echo "echo ENV ${t}activate >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/test1/bin/${t}activate" 46 | chmod +x "$WORKON_HOME/test1/bin/${t}activate" 47 | done 48 | 49 | rm -f "$TMPDIR/catch_output" 50 | touch "$TMPDIR/catch_output" 51 | 52 | workon test1 53 | 54 | output=$(cat "$TMPDIR/catch_output") 55 | expected="GLOBAL preactivate 56 | ENV preactivate 57 | GLOBAL postactivate 58 | ENV postactivate" 59 | 60 | assertSame "$expected" "$output" 61 | 62 | for t in pre post 63 | do 64 | rm -f "$WORKON_HOME/test1/bin/${t}activate" 65 | rm -f "$WORKON_HOME/${t}activate" 66 | done 67 | } 68 | 69 | test_workon_deactivate_hooks () { 70 | for t in pre post 71 | do 72 | echo "#!/bin/sh" > "$WORKON_HOME/${t}deactivate" 73 | echo "echo GLOBAL ${t}deactivate >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/${t}deactivate" 74 | chmod +x "$WORKON_HOME/${t}deactivate" 75 | 76 | echo "#!/bin/sh" > "$WORKON_HOME/test2/bin/${t}deactivate" 77 | echo "echo ENV ${t}deactivate >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/test1/bin/${t}deactivate" 78 | chmod +x "$WORKON_HOME/test1/bin/${t}deactivate" 79 | done 80 | 81 | rm -f "$TMPDIR/catch_output" 82 | touch "$TMPDIR/catch_output" 83 | 84 | workon test1 85 | workon test2 86 | 87 | output=$(cat "$TMPDIR/catch_output") 88 | expected="ENV predeactivate 89 | GLOBAL predeactivate 90 | ENV postdeactivate 91 | GLOBAL postdeactivate" 92 | 93 | assertSame "$expected" "$output" 94 | 95 | for t in pre post 96 | do 97 | rm -f "$WORKON_HOME/test1/bin/${t}deactivate" 98 | rm -f "$WORKON_HOME/${t}deactivate" 99 | done 100 | } 101 | 102 | test_virtualenvwrapper_show_workon_options () { 103 | mkdir "$WORKON_HOME/not_env" 104 | (cd "$WORKON_HOME"; ln -s test1 link_env) 105 | envs=$(virtualenvwrapper_show_workon_options | tr '\n' ' ') 106 | # On OSX there are two trailing spaces, on Linux one, so compare substring 107 | assertSame " env with space link_env test1 test2 " "${envs:0:37}" 108 | rmdir "$WORKON_HOME/not_env" 109 | rm -f "$WORKON_HOME/link_env" 110 | } 111 | 112 | test_virtualenvwrapper_show_workon_options_grep_options () { 113 | mkdir "$WORKON_HOME/not_env" 114 | (cd "$WORKON_HOME"; ln -s test1 link_env) 115 | export GREP_OPTIONS="--count" 116 | envs=$(virtualenvwrapper_show_workon_options | tr '\n' ' ') 117 | unset GREP_OPTIONS 118 | # On OSX there are two trailing spaces, on Linux one, so compare substring 119 | assertSame " env with space link_env test1 test2 " "${envs:0:37}" 120 | rmdir "$WORKON_HOME/not_env" 121 | rm -f "$WORKON_HOME/link_env" 122 | } 123 | 124 | test_virtualenvwrapper_show_workon_options_chpwd () { 125 | # https://bitbucket.org/dhellmann/virtualenvwrapper/issue/153 126 | function chpwd { 127 | local SEARCH=' ' 128 | local REPLACE='%20' 129 | local PWD_URL="file://$HOSTNAME${PWD//$SEARCH/$REPLACE}" 130 | printf '\e]7;%s\a' "$PWD_URL" 131 | echo -n "\033]0;${HOST//.*}:$USER\007" 132 | } 133 | mkdir "$WORKON_HOME/not_env" 134 | envs=$(virtualenvwrapper_show_workon_options | tr '\n' ' ') 135 | # On OSX there are two trailing spaces, on Linux one, so compare substring 136 | assertSame " env with space test1 test2 " "${envs:0:28}" 137 | rmdir "$WORKON_HOME/not_env" 138 | rm -f "$WORKON_HOME/link_env" 139 | } 140 | 141 | test_virtualenvwrapper_show_workon_options_no_envs () { 142 | old_home="$WORKON_HOME" 143 | export WORKON_HOME=${TMPDIR:-/tmp}/$$ 144 | # On OSX there is a space and on Linux there is not, so strip all spaces 145 | envs=$(virtualenvwrapper_show_workon_options 2>/dev/null | sed 's/\n //g') 146 | assertSame "" "$envs" 147 | export WORKON_HOME="$old_home" 148 | } 149 | 150 | test_no_workon_home () { 151 | old_home="$WORKON_HOME" 152 | export WORKON_HOME="$WORKON_HOME/not_there" 153 | workon should_not_be_created >"$old_home/output" 2>&1 154 | output=$(cat "$old_home/output") 155 | assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" 156 | WORKON_HOME="$old_home" 157 | } 158 | 159 | test_workon_dot () { 160 | cd $WORKON_HOME/test1 161 | workon . 162 | assertTrue virtualenvwrapper_verify_active_environment 163 | assertSame "test1" $(basename "$VIRTUAL_ENV") 164 | } 165 | 166 | test_workon_dot_with_space () { 167 | cd $WORKON_HOME/" env with space" 168 | workon . 169 | assertTrue virtualenvwrapper_verify_active_environment 170 | env_name=$(basename "$VIRTUAL_ENV") 171 | assertSame " env with space" "$env_name" 172 | } 173 | 174 | test_workon_with_space () { 175 | workon " env with space" 176 | assertTrue virtualenvwrapper_verify_active_environment 177 | env_name=$(basename "$VIRTUAL_ENV") 178 | assertSame " env with space" "$env_name" 179 | } 180 | 181 | . "$test_dir/shunit2" 182 | -------------------------------------------------------------------------------- /docs/source/tips.rst: -------------------------------------------------------------------------------- 1 | .. _tips-and-tricks: 2 | 3 | ================= 4 | Tips and Tricks 5 | ================= 6 | 7 | This is a list of user-contributed tips for making virtualenv and 8 | virtualenvwrapper even more useful. If you have tip to share, drop me 9 | an email or post a comment on `this blog post 10 | `__ 11 | and I'll add it here. 12 | 13 | Enhanced bash/zsh Prompt 14 | ======================== 15 | 16 | Via `Stephan Sokolow `_ 17 | 18 | While the virtualenv ``activate`` script does attempt to provide 19 | an indicator in the prompt, it has various shortcomings, and 20 | cannot be customized. 21 | 22 | However, it does also set a shell variable named 23 | ``VIRTUAL_ENV`` which can be used as the basis for disabling the 24 | built-in prompt indicator and substituting an improved one, 25 | as a customization to ``.bashrc`` or ``.zshrc``:: 26 | 27 | virtualenv_prompt() { 28 | # If not in a virtualenv, print nothing 29 | [[ "$VIRTUAL_ENV" == "" ]] && return 30 | 31 | # Distinguish between the shell where the virtualenv was activated 32 | # and its children 33 | local venv_name="${VIRTUAL_ENV##*/}" 34 | if typeset -f deactivate >/dev/null; then 35 | echo "[${venv_name}] " 36 | else 37 | echo "<${venv_name}> " 38 | fi 39 | } 40 | 41 | # Display a "we are in a virtualenv" indicator that works in child shells too 42 | VIRTUAL_ENV_DISABLE_PROMPT=1 43 | PS1='$(virtualenv_prompt)'"$PS1" 44 | 45 | This basic example works in both bash and zsh and has the following 46 | advantages: 47 | 48 | 1. It will also display in sub-shells, because it works by having the 49 | shell detect an active virtualenv, rather than by having the ``activate`` 50 | script modify the prompt for just the current shell instance. 51 | 2. It will clearly indicate if you're in a subshell, where the 52 | virtualenv will still apply, but the ``deactivate`` command will be 53 | missing. 54 | 55 | However, if you are using zsh, a better example of what the design 56 | is capable of can be constructed by taking advantage of zsh's built-in 57 | support for easily adding color and right-aligned segments to prompts:: 58 | 59 | zsh_virtualenv_prompt() { 60 | # If not in a virtualenv, print nothing 61 | [[ "$VIRTUAL_ENV" == "" ]] && return 62 | 63 | # Distinguish between the shell where the virtualenv was activated 64 | # and its children 65 | local venv_name="${VIRTUAL_ENV##*/}" 66 | if typeset -f deactivate >/dev/null; then 67 | echo "[%F{green}${venv_name}%f] " 68 | else 69 | echo "<%F{green}${venv_name}%f> " 70 | fi 71 | } 72 | 73 | setopt PROMPT_SUBST PROMPT_PERCENT 74 | 75 | # Display a "we are in a virtualenv" indicator that works in child shells too 76 | VIRTUAL_ENV_DISABLE_PROMPT=1 77 | RPS1='$(zsh_virtualenv_prompt)' 78 | 79 | Updating cached ``$PATH`` entries 80 | ================================= 81 | 82 | From Nat (was blogger.com/profile/16779944428406910187): 83 | 84 | I also added the command 'rehash' to ``$WORKON_HOME/postactivate`` and 85 | ``$WORKON_HOME/postdeactivate`` as I was having some problems with zsh 86 | not picking up the new paths immediately. 87 | 88 | Creating Project Work Directories 89 | ================================= 90 | 91 | Via `James `_: 92 | 93 | In the ``postmkvirtualenv`` script I have the following to create a 94 | directory based on the project name, add that directory to the python 95 | path and then cd into it:: 96 | 97 | proj_name=$(basename $VIRTUAL_ENV) 98 | mkdir $HOME/projects/$proj_name 99 | add2virtualenv $HOME/projects/$proj_name 100 | cd $HOME/projects/$proj_name 101 | 102 | 103 | In the ``postactivate`` script I have it set to automatically change 104 | to the project directory when I use the workon command:: 105 | 106 | proj_name=$(basename $VIRTUAL_ENV) 107 | cd ~/projects/$proj_name 108 | 109 | Automatically Run workon When Entering a Directory 110 | ================================================== 111 | 112 | `Justin Abrahms posted 113 | `__ 114 | about some code he added to his shell environment to look at the 115 | directory each time he runs ``cd``. If it finds a ``.venv`` file, it 116 | activates the environment named within. On leaving that directory, 117 | the current virtualenv is automatically deactivated. 118 | 119 | Installing Common Tools Automatically in New Environments 120 | ========================================================= 121 | 122 | Via rizumu (was rizumu.myopenid.com): 123 | 124 | I have this ``postmkvirtualenv`` to install the get a basic setup. 125 | 126 | :: 127 | 128 | $ cat postmkvirtualenv 129 | #!/usr/bin/env bash 130 | curl -O http://python-distribute.org/distribute_setup.p... />python distribute_setup.py 131 | rm distribute_setup.py 132 | easy_install pip==dev 133 | pip install Mercurial 134 | 135 | Then I have a pip requirement file with my dev tools. 136 | 137 | :: 138 | 139 | $ cat developer_requirements.txt 140 | ipdb 141 | ipython 142 | pastescript 143 | nose 144 | http://douglatornell.ca/software/python/Nosy-1.0.tar.gz 145 | coverage 146 | sphinx 147 | grin 148 | pyflakes 149 | pep8 150 | 151 | Then each project has it's own pip requirement file for things like 152 | PIL, psycopg2, django-apps, numpy, etc. 153 | 154 | Changing the Default Behavior of ``cd`` 155 | ======================================= 156 | 157 | Via `mae `__: 158 | 159 | This is supposed to be executed after workon, that is as a 160 | ``postactivate`` hook. It basically overrides ``cd`` to know about the 161 | VENV so instead of doing ``cd`` to go to ``~`` you will go to the venv 162 | root, IMO very handy and I can't live without it anymore. If you pass 163 | it a proper path then it will do the right thing. 164 | 165 | :: 166 | 167 | cd () { 168 | if (( $# == 0 )) 169 | then 170 | builtin cd $VIRTUAL_ENV 171 | else 172 | builtin cd "$@" 173 | fi 174 | } 175 | 176 | cd 177 | 178 | And to finally restore the default behaviour of ``cd`` once you 179 | bailout of a VENV via a ``deactivate`` command, you need to add this 180 | as a ``postdeactivate`` hook:: 181 | 182 | unset -f cd 183 | 184 | Clean up environments on exit 185 | ======================================= 186 | 187 | Via `Michael `__: 188 | 189 | When you use a temporary virtualenv via ``mktmpenv`` or if you have a 190 | :ref:`plugins-post_deactivate` hook, you have to actually run 191 | ``deactivate`` to clean up the temporary environment or run the hook, 192 | respectively. It's easy to forget and just exit the shell. Put the 193 | following in ``~/.bash_logout`` (or your shell's equivalent file) to 194 | always deactivate environments before exiting the shell:: 195 | 196 | [ "$VIRTUAL_ENV" ] && deactivate 197 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # virtualenvwrapper documentation build configuration file, created by 4 | # sphinx-quickstart on Thu May 28 22:35:13 2009. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import datetime 16 | 17 | import virtualenvwrapper.version 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | # sys.path.append(os.path.abspath('.')) 23 | 24 | # -- General configuration --------------------------------------------------- 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be 27 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 28 | # ones. 29 | # extensions = ['sphinxcontrib.bitbucket'] 30 | 31 | bitbucket_project_url = 'https://bitbucket.org/virtualenvwrapper/virtualenvw' \ 32 | 'rapper/' 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | # templates_path = ['pkg/templates'] 36 | 37 | # The suffix of source filenames. 38 | source_suffix = '.rst' 39 | 40 | # The encoding of source files. 41 | # source_encoding = 'utf-8' 42 | 43 | # The master toctree document. 44 | master_doc = 'index' 45 | 46 | # General information about the project. 47 | project = u'virtualenvwrapper' 48 | copyright = u'2009-%s, Doug Hellmann' % datetime.datetime.today().year 49 | 50 | # The version info for the project you're documenting, acts as replacement for 51 | # |version| and |release|, also used in various other places throughout the 52 | # built documents. 53 | # 54 | # The short X.Y version. 55 | version = virtualenvwrapper.version.version 56 | # The full version, including alpha/beta/rc tags. 57 | release = version 58 | 59 | # The language for content autogenerated by Sphinx. Refer to documentation 60 | # for a list of supported languages. 61 | # language = None 62 | 63 | # There are two options for replacing |today|: either, you set today to some 64 | # non-false value, then it is used: 65 | # today = '' 66 | # Else, today_fmt is used as the format for a strftime call. 67 | # today_fmt = '%B %d, %Y' 68 | 69 | # List of documents that shouldn't be included in the build. 70 | # unused_docs = [] 71 | 72 | # List of directories, relative to source directory, that shouldn't be searched 73 | # for source files. 74 | exclude_trees = ['_build'] 75 | 76 | # The reST default role (used for this markup: `text`) to use for all 77 | # documents. 78 | # default_role = None 79 | 80 | # If true, '()' will be appended to :func: etc. cross-reference text. 81 | # add_function_parentheses = True 82 | 83 | # If true, the current module name will be prepended to all description 84 | # unit titles (such as .. function::). 85 | # add_module_names = True 86 | 87 | # If true, sectionauthor and moduleauthor directives will be shown in the 88 | # output. They are ignored by default. 89 | # show_authors = False 90 | 91 | # The name of the Pygments (syntax highlighting) style to use. 92 | pygments_style = 'sphinx' 93 | 94 | # A list of ignored prefixes for module index sorting. 95 | # modindex_common_prefix = [] 96 | 97 | 98 | # -- Options for HTML output ------------------------------------------------- 99 | 100 | # The theme to use for HTML and HTML Help pages. Major themes that come with 101 | # Sphinx are currently 'default' and 'sphinxdoc'. 102 | html_theme = 'default' 103 | 104 | # Theme options are theme-specific and customize the look and feel of a theme 105 | # further. For a list of options available for each theme, see the 106 | # documentation. 107 | # html_theme_options = {} 108 | 109 | # Add any paths that contain custom themes here, relative to this directory. 110 | # html_theme_path = [] 111 | 112 | # The name for this set of Sphinx documents. If None, it defaults to 113 | # " v documentation". 114 | # html_title = None 115 | 116 | # A shorter title for the navigation bar. Default is the same as html_title. 117 | # html_short_title = None 118 | 119 | # The name of an image file (relative to this directory) to place at the top 120 | # of the sidebar. 121 | # html_logo = None 122 | 123 | # The name of an image file (within the static path) to use as favicon of the 124 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 125 | # pixels large. 126 | # html_favicon = None 127 | 128 | # Add any paths that contain custom static files (such as style sheets) here, 129 | # relative to this directory. They are copied after the builtin static files, 130 | # so a file named "default.css" will overwrite the builtin "default.css". 131 | # html_static_path = ['static'] 132 | 133 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 134 | # using the given strftime format. 135 | # html_last_updated_fmt = '%b %d, %Y' 136 | 137 | # If true, SmartyPants will be used to convert quotes and dashes to 138 | # typographically correct entities. 139 | # html_use_smartypants = True 140 | 141 | # Custom sidebar templates, maps document names to template names. 142 | # html_sidebars = {} 143 | 144 | # Additional templates that should be rendered to pages, maps page names to 145 | # template names. 146 | # html_additional_pages = {} 147 | 148 | # If false, no module index is generated. 149 | html_use_modindex = False 150 | 151 | # If false, no index is generated. 152 | html_use_index = False 153 | 154 | # If true, the index is split into individual pages for each letter. 155 | # html_split_index = False 156 | 157 | # If true, links to the reST sources are added to the pages. 158 | # html_show_sourcelink = True 159 | 160 | # If true, an OpenSearch description file will be output, and all pages will 161 | # contain a tag referring to it. The value of this option must be the 162 | # base URL from which the finished HTML is served. 163 | # html_use_opensearch = '' 164 | 165 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 166 | # html_file_suffix = '' 167 | 168 | # Output file base name for HTML help builder. 169 | htmlhelp_basename = 'virtualenvwrapperdoc' 170 | 171 | 172 | # -- Options for LaTeX output ------------------------------------------------ 173 | 174 | # The paper size ('letter' or 'a4'). 175 | # latex_paper_size = 'letter' 176 | 177 | # The font size ('10pt', '11pt' or '12pt'). 178 | # latex_font_size = '10pt' 179 | 180 | # Grouping the document tree into LaTeX files. List of tuples 181 | # (source start file, target name, title, author, documentclass 182 | # [howto/manual]). 183 | latex_documents = [ 184 | ('index', 'virtualenvwrapper.tex', u'virtualenvwrapper Documentation', 185 | u'Doug Hellmann', 'manual'), 186 | ] 187 | 188 | # The name of an image file (relative to this directory) to place at the top of 189 | # the title page. 190 | # latex_logo = None 191 | 192 | # For "manual" documents, if this is true, then toplevel headings are parts, 193 | # not chapters. 194 | # latex_use_parts = False 195 | 196 | # Additional stuff for the LaTeX preamble. 197 | # latex_preamble = '' 198 | 199 | # Documents to append as an appendix to all manuals. 200 | # latex_appendices = [] 201 | 202 | # If false, no module index is generated. 203 | # latex_use_modindex = True 204 | -------------------------------------------------------------------------------- /virtualenvwrapper/hook_loader.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Copyright (c) 2010 Doug Hellmann. All rights reserved. 4 | # 5 | """Load hooks for virtualenvwrapper. 6 | """ 7 | 8 | import inspect 9 | import itertools 10 | import logging 11 | import logging.handlers 12 | import optparse 13 | import os 14 | import sys 15 | 16 | from stevedore import ExtensionManager, NamedExtensionManager 17 | 18 | import virtualenvwrapper.version 19 | 20 | LOG_FORMAT = '%(asctime)s %(levelname)s %(name)s %(message)s' 21 | 22 | 23 | class GroupWriteRotatingFileHandler(logging.handlers.RotatingFileHandler): 24 | """Taken from http://stackoverflow.com/questions/1407474 25 | """ 26 | def _open(self): 27 | prevumask = os.umask(0o002) 28 | rtv = logging.handlers.RotatingFileHandler._open(self) 29 | os.umask(prevumask) 30 | return rtv 31 | 32 | 33 | def main(): 34 | parser = optparse.OptionParser( 35 | usage='usage: %prog [options] []', 36 | prog='virtualenvwrapper.hook_loader', 37 | description='Manage hooks for virtualenvwrapper', 38 | ) 39 | 40 | parser.add_option( 41 | '-S', '--script', 42 | help='Runs "hook" then "_source", writing the ' + 43 | 'result to ', 44 | dest='script_filename', 45 | default=None, 46 | ) 47 | parser.add_option( 48 | '-s', '--source', 49 | help='Print the shell commands to be run in the current shell', 50 | action='store_true', 51 | dest='sourcing', 52 | default=False, 53 | ) 54 | parser.add_option( 55 | '-l', '--list', 56 | help='Print a list of the plugins available for the given hook', 57 | action='store_true', 58 | default=False, 59 | dest='listing', 60 | ) 61 | parser.add_option( 62 | '-v', '--verbose', 63 | help='Show more information on the console', 64 | action='store_const', 65 | const=2, 66 | default=1, 67 | dest='verbose_level', 68 | ) 69 | parser.add_option( 70 | '-q', '--quiet', 71 | help='Show less information on the console', 72 | action='store_const', 73 | const=0, 74 | dest='verbose_level', 75 | ) 76 | parser.add_option( 77 | '-n', '--name', 78 | help='Only run the hook from the named plugin', 79 | action='append', 80 | dest='names', 81 | default=[], 82 | ) 83 | parser.add_option( 84 | '--version', 85 | help='Show the version of virtualenvwrapper', 86 | action='store_true', 87 | default=False, 88 | ) 89 | parser.disable_interspersed_args() # stop when on option without an '-' 90 | options, args = parser.parse_args() 91 | 92 | if options.version: 93 | print(virtualenvwrapper.version.version) 94 | return 0 95 | 96 | root_logger = logging.getLogger('virtualenvwrapper') 97 | 98 | # Set up logging to a file 99 | logfile = os.environ.get('VIRTUALENVWRAPPER_LOG_FILE') 100 | if logfile: 101 | root_logger.setLevel(logging.DEBUG) 102 | file_handler = GroupWriteRotatingFileHandler( 103 | logfile, 104 | maxBytes=10240, 105 | backupCount=1, 106 | ) 107 | formatter = logging.Formatter(LOG_FORMAT) 108 | file_handler.setFormatter(formatter) 109 | root_logger.addHandler(file_handler) 110 | 111 | # Send higher-level messages to the console, too 112 | console = logging.StreamHandler(sys.stderr) 113 | console_level = [logging.WARNING, 114 | logging.INFO, 115 | logging.DEBUG, 116 | ][options.verbose_level] 117 | console.setLevel(console_level) 118 | formatter = logging.Formatter('%(name)s %(message)s') 119 | console.setFormatter(formatter) 120 | root_logger.addHandler(console) 121 | root_logger.setLevel(console_level) 122 | 123 | # logging.getLogger(__name__).debug('cli args %s', args) 124 | 125 | # Determine which hook we're running 126 | if not args: 127 | if options.listing: 128 | list_hooks() 129 | return 0 130 | else: 131 | parser.error('Please specify the hook to run') 132 | hook = args[0] 133 | 134 | if options.sourcing and options.script_filename: 135 | parser.error('--source and --script are mutually exclusive.') 136 | 137 | if options.sourcing: 138 | hook += '_source' 139 | 140 | log = logging.getLogger('virtualenvwrapper.hook_loader') 141 | 142 | log.debug('Running %s hooks', hook) 143 | run_hooks(hook, options, args) 144 | 145 | if options.script_filename: 146 | log.debug('Saving sourcable %s hooks to %s', 147 | hook, options.script_filename) 148 | options.sourcing = True 149 | try: 150 | with open(options.script_filename, "w") as output: 151 | output.write('# %s\n' % hook) 152 | # output.write('echo %s\n' % hook) 153 | # output.write('set -x\n') 154 | run_hooks(hook + '_source', options, args, output) 155 | except (IOError, OSError) as e: 156 | log.error('Error while writing to %s: \n %s', 157 | options.script_filename, e) 158 | sys.exit(1) 159 | 160 | return 0 161 | 162 | 163 | def run_hooks(hook, options, args, output=None): 164 | log = logging.getLogger('virtualenvwrapper.hook_loader') 165 | if output is None: 166 | output = sys.stdout 167 | 168 | namespace = 'virtualenvwrapper.%s' % hook 169 | if options.names: 170 | log.debug('looking for %s hooks %s' % (namespace, options.names)) 171 | hook_mgr = NamedExtensionManager(namespace, options.names) 172 | else: 173 | log.debug('looking for %s hooks' % namespace) 174 | hook_mgr = ExtensionManager(namespace) 175 | 176 | if options.listing: 177 | def show(ext): 178 | output.write(' %-10s -- %s\n' % 179 | (ext.name, inspect.getdoc(ext.plugin) or '')) 180 | try: 181 | hook_mgr.map(show) 182 | except RuntimeError: # no templates 183 | output.write(' No templates installed.\n') 184 | 185 | elif options.sourcing: 186 | def get_source(ext, args): 187 | # Show the shell commands so they can 188 | # be run in the calling shell. 189 | log.debug('getting source instructions for %s' % ext.name) 190 | contents = (ext.plugin(args) or '').strip() 191 | if contents: 192 | output.write('# %s\n' % ext.name) 193 | output.write(contents) 194 | output.write("\n") 195 | try: 196 | hook_mgr.map(get_source, args[1:]) 197 | except RuntimeError: 198 | pass 199 | 200 | else: 201 | # Just run the plugin ourselves 202 | def invoke(ext, args): 203 | log.debug('running %s' % ext.name) 204 | ext.plugin(args) 205 | try: 206 | hook_mgr.map(invoke, args[1:]) 207 | except RuntimeError: 208 | pass 209 | 210 | 211 | def list_hooks(output=None): 212 | if output is None: 213 | output = sys.stdout 214 | static_names = [ 215 | 'initialize', 216 | 'get_env_details', 217 | 'project.pre_mkproject', 218 | 'project.post_mkproject', 219 | 'project.template', 220 | ] 221 | pre_post_hooks = ( 222 | '_'.join(h) 223 | for h in itertools.product(['pre', 'post'], 224 | ['mkvirtualenv', 225 | 'rmvirtualenv', 226 | 'activate', 227 | 'deactivate', 228 | 'cpvirtualenv', 229 | ]) 230 | ) 231 | for hook in itertools.chain(static_names, pre_post_hooks): 232 | output.write(hook + '\n') 233 | 234 | 235 | if __name__ == '__main__': 236 | main() 237 | -------------------------------------------------------------------------------- /tests/test_mkvirtualenv.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | oneTimeSetUp() { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | } 11 | 12 | oneTimeTearDown() { 13 | rm -rf "$WORKON_HOME" 14 | } 15 | 16 | setUp () { 17 | echo 18 | rm -f "$TMPDIR/catch_output" 19 | } 20 | 21 | test_create() { 22 | mkvirtualenv "env1" >/dev/null 2>&1 23 | assertTrue "Environment directory was not created" "[ -d $WORKON_HOME/env1 ]" 24 | for hook in postactivate predeactivate postdeactivate 25 | do 26 | assertTrue "env1 $hook was not created" "[ -f $WORKON_HOME/env1/bin/$hook ]" 27 | assertFalse "env1 $hook is executable" "[ -x $WORKON_HOME/env1/bin/$hook ]" 28 | done 29 | } 30 | 31 | test_create_space_in_name() { 32 | # Only test with leading and internal spaces. Directory names with trailing spaces are legal, 33 | # and work with virtualenv on OSX, but error out on Linux. 34 | mkvirtualenv " env with space" >/dev/null 2>&1 35 | assertTrue "Environment directory was not created" "[ -d \"$WORKON_HOME/ env with space\" ]" 36 | for hook in postactivate predeactivate postdeactivate 37 | do 38 | assertTrue "$hook was not created" "[ -f \"$WORKON_HOME/ env with space/bin/$hook\" ]" 39 | assertFalse "$hook is executable" "[ -x \"$WORKON_HOME/ env with space/bin/$hook\" ]" 40 | done 41 | assertTrue virtualenvwrapper_verify_active_environment 42 | env_name=$(basename "$VIRTUAL_ENV") 43 | assertSame " env with space" "$env_name" 44 | } 45 | 46 | test_activates () { 47 | mkvirtualenv "env2" >/dev/null 2>&1 48 | assertTrue virtualenvwrapper_verify_active_environment 49 | assertSame "env2" $(basename "$VIRTUAL_ENV") 50 | } 51 | 52 | test_hooks () { 53 | echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" 54 | echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" 55 | chmod +x "$WORKON_HOME/premkvirtualenv" 56 | 57 | echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" 58 | mkvirtualenv "env3" >/dev/null 2>&1 59 | output=$(cat "$TMPDIR/catch_output") 60 | workon_home_as_pwd=$(cd $WORKON_HOME; pwd) 61 | expected="GLOBAL premkvirtualenv $workon_home_as_pwd env3 62 | GLOBAL postmkvirtualenv" 63 | assertSame "$expected" "$output" 64 | rm -f "$WORKON_HOME/premkvirtualenv" 65 | rm -f "$WORKON_HOME/postmkvirtualenv" 66 | deactivate 67 | rmvirtualenv "env3" >/dev/null 2>&1 68 | } 69 | 70 | test_no_virtualenv () { 71 | # Find "which" before we change the path 72 | which=$(which which) 73 | old_path="$PATH" 74 | PATH="/bin:/usr/sbin:/sbin" 75 | venv=$($which virtualenv 2>/dev/null) 76 | if [ ! -z "$venv" ] 77 | then 78 | echo "FOUND \"$venv\" in PATH so skipping this test" 79 | export PATH="$old_path" 80 | return 0 81 | fi 82 | mkvirtualenv should_not_be_created >/dev/null 2>&1 83 | RC=$? 84 | # Restore the path before testing because 85 | # the test script depends on commands in the 86 | # path. 87 | export PATH="$old_path" 88 | assertSame "$RC" "1" 89 | } 90 | 91 | test_no_args () { 92 | mkvirtualenv 2>/dev/null 1>&2 93 | RC=$? 94 | assertSame "2" "$RC" 95 | } 96 | 97 | test_no_workon_home () { 98 | old_home="$WORKON_HOME" 99 | export WORKON_HOME="$WORKON_HOME/not_there" 100 | mkvirtualenv should_be_created >"$old_home/output" 2>&1 101 | output=$(cat "$old_home/output") 102 | assertTrue "Did not see expected message in \"$output\"" "cat \"$old_home/output\" | grep 'does not exist'" 103 | assertTrue "Did not create environment" "[ -d \"$WORKON_HOME/should_be_created\" ]" 104 | WORKON_HOME="$old_home" 105 | } 106 | 107 | test_mkvirtualenv_hooks_system_site_packages () { 108 | # See issue #189 109 | 110 | echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" 111 | echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" 112 | chmod +x "$WORKON_HOME/premkvirtualenv" 113 | 114 | echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" 115 | mkvirtualenv --system-site-packages "env189" >/dev/null 2>&1 116 | output=$(cat "$TMPDIR/catch_output") 117 | workon_home_as_pwd=$(cd $WORKON_HOME; pwd) 118 | expected="GLOBAL premkvirtualenv $workon_home_as_pwd env189 119 | GLOBAL postmkvirtualenv" 120 | assertSame "$expected" "$output" 121 | rm -f "$WORKON_HOME/premkvirtualenv" 122 | rm -f "$WORKON_HOME/postmkvirtualenv" 123 | deactivate 124 | rmvirtualenv "env189" >/dev/null 2>&1 125 | } 126 | 127 | test_mkvirtualenv_args () { 128 | # See issue #102 129 | VIRTUALENVWRAPPER_VIRTUALENV_ARGS="--without-pip" 130 | # With the argument, verify that they are not copied. 131 | mkvirtualenv "without_pip" >/dev/null 2>&1 132 | local RC=$? 133 | assertTrue "mkvirtualenv failed" "[ $RC -eq 0 ]" 134 | contents="$(lssitepackages)" 135 | assertFalse "found pip in site-packages: ${contents}" "echo $contents | grep -q pip" 136 | rmvirtualenv "without_pip" >/dev/null 2>&1 137 | unset VIRTUALENVWRAPPER_VIRTUALENV_ARGS 138 | } 139 | 140 | test_no_such_virtualenv () { 141 | VIRTUALENVWRAPPER_VIRTUALENV=/path/to/missing/program 142 | 143 | echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" 144 | echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" 145 | chmod +x "$WORKON_HOME/premkvirtualenv" 146 | 147 | echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" 148 | mkvirtualenv "env3" >/dev/null 2>&1 149 | output=$(cat "$TMPDIR/catch_output" 2>/dev/null) 150 | workon_home_as_pwd=$(cd $WORKON_HOME; pwd) 151 | expected="" 152 | assertSame "$expected" "$output" 153 | rm -f "$WORKON_HOME/premkvirtualenv" 154 | rm -f "$WORKON_HOME/postmkvirtualenv" 155 | 156 | VIRTUALENVWRAPPER_VIRTUALENV=virtualenv 157 | } 158 | 159 | test_virtualenv_fails () { 160 | # Test to reproduce the conditions in issue #76 161 | # https://bitbucket.org/dhellmann/virtualenvwrapper/issue/76/ 162 | # 163 | # Should not run the premkvirtualenv or postmkvirtualenv hooks 164 | # because the environment is not created and even the 165 | # premkvirtualenv hooks are run *after* the environment exists 166 | # (but before it is activated). 167 | export pre_test_dir=$(cd "$test_dir"; pwd) 168 | 169 | VIRTUALENVWRAPPER_VIRTUALENV=false 170 | 171 | echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" 172 | echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" 173 | chmod +x "$WORKON_HOME/premkvirtualenv" 174 | 175 | echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" 176 | mkvirtualenv "env3" >/dev/null 2>&1 177 | output=$(cat "$TMPDIR/catch_output" 2>/dev/null) 178 | workon_home_as_pwd=$(cd $WORKON_HOME; pwd) 179 | expected="" 180 | assertSame "$expected" "$output" 181 | rm -f "$WORKON_HOME/premkvirtualenv" 182 | rm -f "$WORKON_HOME/postmkvirtualenv" 183 | 184 | VIRTUALENVWRAPPER_VIRTUALENV=virtualenv 185 | } 186 | 187 | test_mkvirtualenv_python_not_sticky () { 188 | typeset _save=$VIRTUALENVWRAPPER_VIRTUALENV 189 | VIRTUALENVWRAPPER_VIRTUALENV=true 190 | mkvirtualenv --python blah foo 191 | assertSame "" "$interpreter" 192 | VIRTUALENVWRAPPER_VIRTUALENV=$_save 193 | } 194 | 195 | test_mkvirtualenv_python_short_option () { 196 | typeset _save=$VIRTUALENVWRAPPER_VIRTUALENV 197 | VIRTUALENVWRAPPER_VIRTUALENV=echo 198 | output="$(mkvirtualenv -p python foo)" 199 | assertSame "--python=python foo" "$output" 200 | VIRTUALENVWRAPPER_VIRTUALENV=$_save 201 | } 202 | 203 | test_mkvirtualenv_python_long_option () { 204 | typeset _save=$VIRTUALENVWRAPPER_VIRTUALENV 205 | VIRTUALENVWRAPPER_VIRTUALENV=echo 206 | output="$(mkvirtualenv --python python foo)" 207 | assertSame "--python=python foo" "$output" 208 | VIRTUALENVWRAPPER_VIRTUALENV=$_save 209 | } 210 | 211 | test_mkvirtualenv_python_long_option_equal () { 212 | typeset _save=$VIRTUALENVWRAPPER_VIRTUALENV 213 | VIRTUALENVWRAPPER_VIRTUALENV=echo 214 | output="$(mkvirtualenv --python=python foo)" 215 | assertSame "--python=python foo" "$output" 216 | VIRTUALENVWRAPPER_VIRTUALENV=$_save 217 | } 218 | 219 | 220 | . "$test_dir/shunit2" 221 | -------------------------------------------------------------------------------- /docs/source/scripts.rst: -------------------------------------------------------------------------------- 1 | .. _scripts: 2 | 3 | ======================== 4 | Per-User Customization 5 | ======================== 6 | 7 | The end-user customization scripts are either *sourced* (allowing them 8 | to modify your shell environment) or *run* as an external program at 9 | the appropriate trigger time. 10 | 11 | The global scripts applied to all environments should be placed in the 12 | directory named by :ref:`VIRTUALENVWRAPPER_HOOK_DIR 13 | `, which by default will be equal 14 | to :ref:`WORKON_HOME `. The local scripts should be 15 | placed in the ``bin`` directory of the virtualenv. 16 | 17 | Example Usage 18 | =============== 19 | 20 | As a Django developer, you likely want DJANGO_SETTINGS_MODULE to be set, and 21 | if you work on multiple projects, you want it to be specific to the project 22 | you are currently working on. Wouldn't it be nice if it was set based on the 23 | active virtualenv? You can achieve this with :ref:`scripts` as follows. 24 | 25 | If your :ref:`WORKON_HOME ` is set to ~/.virtualenvs:: 26 | 27 | vim ~/.virtualenvs/premkvirtualenv 28 | 29 | Edit the file so it contains the following (for a default Django setup):: 30 | 31 | # Automatically set django settings for the virtualenv 32 | echo "export DJANGO_SETTINGS_MODULE=$1.settings" >> "$1/bin/activate" 33 | 34 | Create a new virtualenv, and you should see DJANGO_SETTINGS_MODULE in your env! 35 | 36 | .. _scripts-get_env_details: 37 | 38 | get_env_details 39 | =============== 40 | 41 | :Global/Local: both 42 | :Argument(s): env name 43 | :Sourced/Run: run 44 | 45 | ``$VIRTUALENVWRAPPER_HOOK_DIR/get_env_details`` is run when ``workon`` is run with no 46 | arguments and a list of the virtual environments is printed. The hook 47 | is run once for each environment, after the name is printed, and can 48 | print additional information about that environment. 49 | 50 | .. _scripts-initialize: 51 | 52 | initialize 53 | ========== 54 | 55 | :Global/Local: global 56 | :Argument(s): None 57 | :Sourced/Run: sourced 58 | 59 | ``$VIRTUALENVWRAPPER_HOOK_DIR/initialize`` is sourced when ``virtualenvwrapper.sh`` 60 | is loaded into your environment. Use it to adjust global settings 61 | when virtualenvwrapper is enabled. 62 | 63 | .. _scripts-premkvirtualenv: 64 | 65 | premkvirtualenv 66 | =============== 67 | 68 | :Global/Local: global 69 | :Argument(s): name of new environment 70 | :Sourced/Run: run 71 | 72 | ``$VIRTUALENVWRAPPER_HOOK_DIR/premkvirtualenv`` is run as an external program after 73 | the virtual environment is created but before the current environment 74 | is switched to point to the new env. The current working directory for 75 | the script is ``$WORKON_HOME`` and the name of the new environment is 76 | passed as an argument to the script. 77 | 78 | .. _scripts-postmkvirtualenv: 79 | 80 | postmkvirtualenv 81 | ================ 82 | 83 | :Global/Local: global 84 | :Argument(s): none 85 | :Sourced/Run: sourced 86 | 87 | ``$VIRTUALENVWRAPPER_HOOK_DIR/postmkvirtualenv`` is sourced after the new environment 88 | is created and activated. If the ``-a`` flag was used, 89 | the link to the project directory is set up before this script is sourced. 90 | 91 | .. _scripts-precpvirtualenv: 92 | 93 | precpvirtualenv 94 | =============== 95 | 96 | :Global/Local: global 97 | :Argument(s): name of original environment, name of new environment 98 | :Sourced/Run: run 99 | 100 | ``$VIRTUALENVWRAPPER_HOOK_DIR/precpvirtualenv`` is run as an external program after 101 | the source environment is duplicated and made relocatable, but before 102 | the ``premkvirtualenv`` hook is run or the current environment is 103 | switched to point to the new env. The current working directory for 104 | the script is ``$WORKON_HOME`` and the names of the source and new 105 | environments are passed as arguments to the script. 106 | 107 | .. _scripts-postcpvirtualenv: 108 | 109 | postcpvirtualenv 110 | ================ 111 | 112 | :Global/Local: global 113 | :Argument(s): none 114 | :Sourced/Run: sourced 115 | 116 | ``$VIRTUALENVWRAPPER_HOOK_DIR/postcpvirtualenv`` is sourced after the new environment 117 | is created and activated. 118 | 119 | .. _scripts-preactivate: 120 | 121 | preactivate 122 | =========== 123 | 124 | :Global/Local: global, local 125 | :Argument(s): environment name 126 | :Sourced/Run: run 127 | 128 | The global ``$VIRTUALENVWRAPPER_HOOK_DIR/preactivate`` script is run before the new 129 | environment is enabled. The environment name is passed as the first 130 | argument. 131 | 132 | The local ``$VIRTUAL_ENV/bin/preactivate`` hook is run before the new 133 | environment is enabled. The environment name is passed as the first 134 | argument. 135 | 136 | .. _scripts-postactivate: 137 | 138 | postactivate 139 | ============ 140 | 141 | :Global/Local: global, local 142 | :Argument(s): none 143 | :Sourced/Run: sourced 144 | 145 | The global ``$VIRTUALENVWRAPPER_HOOK_DIR/postactivate`` script is sourced after the 146 | new environment is enabled. ``$VIRTUAL_ENV`` refers to the new 147 | environment at the time the script runs. 148 | 149 | This example script adds a space between the virtual environment name 150 | and your old PS1 by making use of ``_OLD_VIRTUAL_PS1``. 151 | 152 | :: 153 | 154 | PS1="(`basename \"$VIRTUAL_ENV\"`) $_OLD_VIRTUAL_PS1" 155 | 156 | The local ``$VIRTUAL_ENV/bin/postactivate`` script is sourced after 157 | the new environment is enabled. ``$VIRTUAL_ENV`` refers to the new 158 | environment at the time the script runs. 159 | 160 | This example script for the PyMOTW environment changes the current 161 | working directory and the PATH variable to refer to the source tree 162 | containing the PyMOTW source. 163 | 164 | :: 165 | 166 | pymotw_root=/Users/dhellmann/Documents/PyMOTW 167 | cd $pymotw_root 168 | PATH=$pymotw_root/bin:$PATH 169 | 170 | .. _scripts-predeactivate: 171 | 172 | predeactivate 173 | ============= 174 | 175 | :Global/Local: local, global 176 | :Argument(s): none 177 | :Sourced/Run: sourced 178 | 179 | The local ``$VIRTUAL_ENV/bin/predeactivate`` script is sourced before the 180 | current environment is deactivated, and can be used to disable or 181 | clear settings in your environment. ``$VIRTUAL_ENV`` refers to the old 182 | environment at the time the script runs. 183 | 184 | The global ``$VIRTUALENVWRAPPER_HOOK_DIR/predeactivate`` script is sourced before the 185 | current environment is deactivated. ``$VIRTUAL_ENV`` refers to the 186 | old environment at the time the script runs. 187 | 188 | .. _scripts-postdeactivate: 189 | 190 | postdeactivate 191 | ============== 192 | 193 | :Global/Local: local, global 194 | :Argument(s): none 195 | :Sourced/Run: sourced 196 | 197 | The ``$VIRTUAL_ENV/bin/postdeactivate`` script is sourced after the 198 | current environment is deactivated, and can be used to disable or 199 | clear settings in your environment. The path to the environment just 200 | deactivated is available in ``$VIRTUALENVWRAPPER_LAST_VIRTUALENV``. 201 | 202 | .. _scripts-prermvirtualenv: 203 | 204 | prermvirtualenv 205 | =============== 206 | 207 | :Global/Local: global 208 | :Argument(s): environment name 209 | :Sourced/Run: run 210 | 211 | The ``$VIRTUALENVWRAPPER_HOOK_DIR/prermvirtualenv`` script is run as an external 212 | program before the environment is removed. The full path to the 213 | environment directory is passed as an argument to the script. 214 | 215 | .. _scripts-postrmvirtualenv: 216 | 217 | postrmvirtualenv 218 | ================ 219 | 220 | :Global/Local: global 221 | :Argument(s): environment name 222 | :Sourced/Run: run 223 | 224 | The ``$VIRTUALENVWRAPPER_HOOK_DIR/postrmvirtualenv`` script is run as an external 225 | program after the environment is removed. The full path to the 226 | environment directory is passed as an argument to the script. 227 | 228 | .. _scripts-premkproject: 229 | 230 | premkproject 231 | ============ 232 | 233 | :Global/Local: global 234 | :Argument(s): name of new project 235 | :Sourced/Run: run 236 | 237 | ``$WORKON_HOME/premkproject`` is run as an external program after the 238 | virtual environment is created and after the current environment is 239 | switched to point to the new env, but before the new project directory 240 | is created. The current working directory for the script is 241 | ``$PROJECT_HOME`` and the name of the new project is passed as an 242 | argument to the script. 243 | 244 | .. _scripts-postmkproject: 245 | 246 | postmkproject 247 | ============= 248 | 249 | :Global/Local: global 250 | :Argument(s): none 251 | :Sourced/Run: sourced 252 | 253 | ``$WORKON_HOME/postmkproject`` is sourced after the new environment 254 | and project directories are created and the virtualenv is activated. 255 | The current working directory is the project directory. 256 | -------------------------------------------------------------------------------- /docs/source/design.rst: -------------------------------------------------------------------------------- 1 | ========================================================= 2 | Why virtualenvwrapper is (Mostly) Not Written In Python 3 | ========================================================= 4 | 5 | If you look at the source code for virtualenvwrapper you will see that 6 | most of the interesting parts are implemented as shell functions in 7 | ``virtualenvwrapper.sh``. The hook loader is a Python app, but doesn't 8 | do much to manage the virtualenvs. Some of the most frequently asked 9 | questions about virtualenvwrapper are "Why didn't you write this as a 10 | set of Python programs?" or "Have you thought about rewriting it in 11 | Python?" For a long time these questions baffled me, because it was 12 | always obvious to me that it had to be implemented as it is. But they 13 | come up frequently enough that I feel the need to explain. 14 | 15 | tl;dr: POSIX Made Me Do It 16 | ========================== 17 | 18 | The choice of implementation language for virtualenvwrapper was made 19 | for pragmatic, rather than philosophical, reasons. The wrapper 20 | commands need to modify the state and environment of the user's 21 | *current shell process*, and the only way to do that is to have the 22 | commands run *inside that shell.* That resulted in me writing 23 | virtualenvwrapper as a set of shell functions, rather than separate 24 | shell scripts or even Python programs. 25 | 26 | Where Do POSIX Processes Come From? 27 | =================================== 28 | 29 | New POSIX processes are created when an existing process invokes the 30 | ``fork()`` system call. The invoking process becomes the "parent" of 31 | the new "child" process, and the child is a full clone of the 32 | parent. The *semantic* result of ``fork()`` is that an entire new copy 33 | of the parent process is created. In practice, optimizations are 34 | normally made to avoid copying more memory than is absolutely 35 | necessary (frequently via a copy-on-write system). But for the 36 | purposes of this explanation it is sufficient to think of the child as 37 | a full replica of the parent. 38 | 39 | The important parts of the parent process that are copied include 40 | dynamic memory (the stack and heap), static stuff (the program code), 41 | resources like open file descriptors, and the *environment variables* 42 | exported from the parent process. Inheriting environment variables is 43 | a fundamental aspect of the way POSIX programs pass state and 44 | configuration information to one another. A parent can establish a 45 | series of ``name=value`` pairs, which are then given to the child 46 | process. The child can access them through functions like 47 | ``getenv()``, ``setenv()`` (and in Python through ``os.environ``). 48 | 49 | The choice of the term *inherit* to describe the way the variables and 50 | their contents are passed from parent to child is 51 | significant. Although a child can change its own environment, it 52 | cannot directly change the environment settings of its parent 53 | because there is no system call to modify the parental environment 54 | settings. 55 | 56 | How the Shell Runs a Program 57 | ============================ 58 | 59 | When a shell receives a command to be executed, either interactively 60 | or by parsing a script file, and determines that the command is 61 | implemented in a separate program file, it uses ``fork()`` to create a 62 | new process and then inside that process it uses one of the ``exec`` 63 | functions to start the specified program. The language that program is 64 | written in doesn't make any difference in the decision about whether 65 | or not to ``fork()``, so even if the "program" is a shell script 66 | written in the language understood by the current shell, a new process 67 | is created. 68 | 69 | On the other hand, if the shell decides that the command is a 70 | *function*, then it looks at the definition and invokes it 71 | directly. Shell functions are made up of other commands, some of which 72 | may result in child processes being created, but the function itself 73 | runs in the original shell process and can therefore modify its state, 74 | for example by changing the working directory or the values of 75 | variables. 76 | 77 | It is possible to force the shell to run a script directly, and not in 78 | a child process, by *sourcing* it. The ``source`` command causes the 79 | shell to read the file and interpret it in the current process. Again, 80 | as with functions, the contents of the file may cause child processes 81 | to be spawned, but there is not a second shell process interpreting 82 | the series of commands. 83 | 84 | What Does This Mean for virtualenvwrapper? 85 | ========================================== 86 | 87 | The original and most important features of virtualenvwrapper are 88 | automatically activating a virtualenv when it is created by 89 | ``mkvirtualenv`` and using ``workon`` to deactivate one environment 90 | and activate another. Making these features work drove the 91 | implementation decisions for the other parts of virtualenvwrapper, 92 | too. 93 | 94 | Environments are activated interactively by sourcing ``bin/activate`` 95 | inside the virtualenv. The ``activate`` script does a few things, but 96 | the important parts are setting the ``VIRTUAL_ENV`` variable and 97 | modifying the shell's search path through the ``PATH`` variable to put 98 | the ``bin`` directory for the environment on the front of the 99 | path. Changing the path means that the programs installed in the 100 | environment, especially the python interpreter there, are found before 101 | other programs with the same name. 102 | 103 | Simply running ``bin/activate``, without using ``source`` doesn't work 104 | because it sets up the environment of the *child* process, without 105 | affecting the parent. In order to source the activate script in the 106 | interactive shell, both ``mkvirtualenv`` and ``workon`` also need to 107 | be run in that shell process. 108 | 109 | Why Choose One When You Can Have Both? 110 | ====================================== 111 | 112 | The hook loader is one part of virtualenvwrapper that *is* written in 113 | Python. Why? Again, because it was easier. Hooks are discovered using 114 | setuptools entry points, because after an entry point is installed the 115 | user doesn't have to take any other action to allow the loader to 116 | discover and use it. It's easy to imagine writing a hook to create new 117 | files on the filesystem (by installing a package, instantiating a 118 | template, etc.). 119 | 120 | How, then, do hooks running in a separate process (the Python 121 | interpreter) modify the shell environment to set variables or change 122 | the working directory? They cheat, of course. 123 | 124 | Each hook point defined by virtualenvwrapper actually represents two 125 | hooks. First, the hooks meant to be run in Python are executed. Then 126 | the "source" hooks are run, and they *print out* a series of shell 127 | commands. All of those commands are collected, saved to a temporary 128 | file, and then the shell is told to source the file. 129 | 130 | Starting up the hook loader turns out to be way more expensive than 131 | most of the other actions virtualenvwrapper takes, though, so I am 132 | considering making its use optional. Most users customize the hooks by 133 | using shell scripts (either globally or in the virtualenv). Finding 134 | and running those can be handled by the shell quite easily. 135 | 136 | Implications for Cross-Shell Compatibility 137 | ========================================== 138 | 139 | Other than requests for a full-Python implementation, the other most 140 | common request is to support additional shells. fish_ comes up a lot, 141 | as do various Windows-only shells. The officially 142 | :ref:`supported-shells` all have a common enough syntax that the same 143 | implementation works for each. Supporting other shells would require 144 | rewriting much, if not all, of the logic using an alternate syntax -- 145 | those other shells are basically different programming languages. So 146 | far I have dealt with the ports by encouraging other developers to 147 | handle them, and then trying to link to and otherwise promote the 148 | results. 149 | 150 | .. _fish: https://fishshell.com/ 151 | 152 | Not As Bad As It Seems 153 | ====================== 154 | 155 | Although there are some special challenges created by the the 156 | requirement that the commands run in a user's interactive shell (see 157 | the many bugs reported by users who alias common commands like ``rm`` 158 | and ``cd``), using the shell as a programming language holds up quite 159 | well. The shells are designed to make finding and executing other 160 | programs easy, and especially to make it easy to combine a series of 161 | smaller programs to perform more complicated operations. As that's 162 | what virtualenvwrapper is doing, it's a natural fit. 163 | 164 | .. seealso:: 165 | 166 | * *Advanced Programming in the UNIX Environment* by W. Richard 167 | Stevens & Stephen A. Rago 168 | * `Fork (operating system)`_ on Wikipedia 169 | * `Environment variable`_ on Wikipedia 170 | * `Linux implementation of fork()`_ 171 | 172 | .. _Fork (operating system): https://en.wikipedia.org/wiki/Fork_(operating_system) 173 | 174 | .. _Environment variable: https://en.wikipedia.org/wiki/Environment_variable 175 | 176 | .. _Linux implementation of fork(): https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c?id=refs/tags/v3.9-rc8#n1558 177 | -------------------------------------------------------------------------------- /tests/test_cpvirtualenv.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | 3 | test_dir=$(cd $(dirname $0) && pwd) 4 | source "$test_dir/setup.sh" 5 | 6 | setUp () { 7 | rm -rf "$WORKON_HOME" 8 | mkdir -p "$WORKON_HOME" 9 | load_wrappers 10 | echo 11 | } 12 | 13 | tearDown () { 14 | if type deactivate >/dev/null 2>&1 15 | then 16 | deactivate 17 | fi 18 | rm -rf "$WORKON_HOME" 19 | } 20 | 21 | test_no_arguments () { 22 | assertSame "Please provide a valid virtualenv to copy." "$(cpvirtualenv)" 23 | } 24 | 25 | test_venv_already_exists_in_workon () { 26 | mkvirtualenv "cpvenv_test" >/dev/null 2>&1 27 | assertSame "cpvenv_test virtualenv already exists." "$(cpvirtualenv 'cpvenv_test')" 28 | } 29 | 30 | test_bad_path () { 31 | assertSame "Please provide a valid virtualenv to copy." "$(cpvirtualenv '~/cpvenv_test')" 32 | } 33 | 34 | test_copy_venv () { 35 | # verify venvs don't exist 36 | assertTrue "Virtualenv to copy already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" 37 | assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" 38 | mkvirtualenv "cpvenv_test" >/dev/null 2>&1 39 | touch "$WORKON_HOME/cpvenv_test/mytestpackage" 40 | 41 | assertTrue "Virtualenv to copy didn't get created" "[ -d $WORKON_HOME/cpvenv_test ]" 42 | assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" 43 | 44 | cpvirtualenv "cpvenv_test" "copied_venv" >/dev/null 2>&1 45 | 46 | # verify copied venv exist 47 | assertTrue "Copied virtualenv doesn't exist" "[ -d $WORKON_HOME/copied_venv ]" 48 | # verify test package exists 49 | assertTrue "Test package is missing" "[ -f $WORKON_HOME/copied_venv/mytestpackage ]" 50 | } 51 | 52 | test_copy_venv_activate () { 53 | # verify venvs don't exist 54 | assertTrue "Virtualenv to copy already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" 55 | assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" 56 | mkvirtualenv "cpvenv_test" >/dev/null 2>&1 57 | 58 | assertTrue "Virtualenv to copy didn't get created" "[ -d $WORKON_HOME/cpvenv_test ]" 59 | assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" 60 | 61 | cpvirtualenv "cpvenv_test" "copied_venv" >/dev/null 2>&1 62 | 63 | # verify copied venv exist 64 | assertTrue "Copied virtualenv doesn't exist" "[ -d $WORKON_HOME/copied_venv ]" 65 | 66 | assertSame "copied_venv" "$(basename $VIRTUAL_ENV)" 67 | assertTrue "Virtualenv not active" virtualenvwrapper_verify_active_environment 68 | } 69 | 70 | test_copy_venv_is_listed () { 71 | # verify venvs don't exist 72 | assertTrue "Virtualenv to copy already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" 73 | assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" 74 | mkvirtualenv "cpvenv_test" >/dev/null 2>&1 75 | 76 | typeset listed=$(lsvirtualenv) 77 | [[ "$listed" != *copied_venv* ]] 78 | RC=$? 79 | assertTrue "Copied virtualenv found in virtualenv list" $RC 80 | 81 | cpvirtualenv "cpvenv_test" "copied_venv" >/dev/null 2>&1 82 | 83 | listed=$(lsvirtualenv -b) 84 | [[ "$listed" == *copied_venv* ]] 85 | RC=$? 86 | assertTrue "Copied virtualenv not found in virtualenv list" $RC 87 | } 88 | 89 | test_clone_venv () { 90 | typeset tmplocation=$(dirname $WORKON_HOME) 91 | 92 | # verify venvs don't exist 93 | assertTrue "Virtualenv to clone already exists" "[ ! -d $tmplocation/cpvenv_test ]" 94 | assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" 95 | 96 | $VIRTUALENVWRAPPER_VIRTUALENV "$tmplocation/cpvenv_test" >/dev/null 2>&1 97 | touch "$tmplocation/cpvenv_test/mytestpackage" 98 | 99 | assertTrue "Virtualenv to clone didn't get created" "[ -d $tmplocation/cpvenv_test ]" 100 | assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" 101 | 102 | cpvirtualenv "$tmplocation/cpvenv_test" "cloned_venv" >/dev/null 2>&1 103 | 104 | # verify cloned venv exist 105 | assertTrue "Cloned virtualenv doesn't exist" "[ -d $WORKON_HOME/cloned_venv ]" 106 | # verify test package exists 107 | assertTrue "Test package is missing" "[ -f $WORKON_HOME/cloned_venv/mytestpackage ]" 108 | 109 | rm -rf "$tmplocation/cpvenv_test" 110 | } 111 | 112 | test_clone_venv_activate () { 113 | typeset tmplocation=$(dirname $WORKON_HOME) 114 | 115 | # verify venvs don't exist 116 | assertTrue "Virtualenv to clone already exists" "[ ! -d $tmplocation/cpvenv_test ]" 117 | assertTrue "Virtualenv with same name as clone source already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" 118 | assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" 119 | 120 | $VIRTUALENVWRAPPER_VIRTUALENV "$tmplocation/cpvenv_test" >/dev/null 2>&1 121 | 122 | assertTrue "Virtualenv to clone didn't get created" "[ -d $tmplocation/cpvenv_test ]" 123 | assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" 124 | 125 | #cpvirtualenv "$tmplocation/cpvenv_test" "cloned_venv" >/dev/null 2>&1 126 | cpvirtualenv "$tmplocation/cpvenv_test" "cloned_venv" >/dev/null 2/>&1 127 | 128 | # verify cloned venv exist 129 | assertTrue "Cloned virtualenv doesn't exist" "[ -d $WORKON_HOME/cloned_venv ]" 130 | 131 | assertSame "cloned_venv" "$(basename $VIRTUAL_ENV)" 132 | assertTrue "Virtualenv not active" virtualenvwrapper_verify_active_environment 133 | 134 | rm -rf "$tmplocation/cpvenv_test" 135 | } 136 | 137 | test_clone_venv_is_listed (){ 138 | typeset tmplocation=$(dirname $WORKON_HOME) 139 | 140 | # verify venvs don't exist 141 | assertTrue "Virtualenv to clone already exists" "[ ! -d $tmplocation/cpvenv_test ]" 142 | assertTrue "Virtualenv with same name as clone source already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" 143 | assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" 144 | 145 | typeset listed=$(lsvirtualenv) 146 | [[ "$listed" != *cloned_venv* ]] 147 | RC=$? 148 | assertTrue "Cloned virtualenv found in virtualenv list" $RC 149 | 150 | $VIRTUALENVWRAPPER_VIRTUALENV "$tmplocation/cpvenv_test" >/dev/null 2>&1 151 | cpvirtualenv "$tmplocation/cpvenv_test" "cloned_venv" >/dev/null 2>&1 152 | 153 | listed=$(lsvirtualenv -b) 154 | [[ "$listed" == *cloned_venv* ]] 155 | RC=$? 156 | assertTrue "Cloned virtualenv not found in virtualenv list" $RC 157 | 158 | rm -rf "$tmplocation/cpvenv_test" 159 | 160 | } 161 | 162 | test_clone_venv_using_same_name () { 163 | typeset tmplocation=$(dirname $WORKON_HOME) 164 | 165 | # verify venvs don't exist 166 | assertTrue "Virtualenv to clone already exists" "[ ! -d $tmplocation/cpvenv_test ]" 167 | assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" 168 | 169 | $VIRTUALENVWRAPPER_VIRTUALENV "$tmplocation/cpvenv_test" >/dev/null 2>&1 170 | touch "$tmplocation/cpvenv_test/mytestpackage" 171 | 172 | assertTrue "Virtualenv to clone didn't get created" "[ -d $tmplocation/cpvenv_test ]" 173 | assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" 174 | 175 | typeset listed=$(lsvirtualenv) 176 | [[ "$listed" != *cpvenv_test* ]] 177 | RC=$? 178 | assertTrue "Cloned virtualenv found in virtualenv list" $RC 179 | 180 | cpvirtualenv "$tmplocation/cpvenv_test" >/dev/null 2>&1 >/dev/null 2>&1 181 | 182 | # verify cloned venv exist 183 | assertTrue "Cloned virtualenv doesn't exist" "[ -d $WORKON_HOME/cpvenv_test ]" 184 | assertTrue "Test package is missing" "[ -f $WORKON_HOME/cpvenv_test/mytestpackage ]" 185 | 186 | listed=$(lsvirtualenv -b) 187 | [[ "$listed" == *cpvenv_test* ]] 188 | RC=$? 189 | assertTrue "Cloned virtualenv not found in virtualenv list" $RC 190 | 191 | assertSame "cpvenv_test" "$(basename $VIRTUAL_ENV)" 192 | assertTrue "Virtualenv not active" virtualenvwrapper_verify_active_environment 193 | 194 | rm -rf "$tmplocation/cpvenv_test" 195 | } 196 | 197 | test_clone_venv_using_vars () { 198 | 199 | # verify venvs don't exist 200 | assertTrue "Virtualenv to clone already exists" "[ ! -d $TMPDIR/cpvenv_test ]" 201 | assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" 202 | 203 | $VIRTUALENVWRAPPER_VIRTUALENV "$TMPDIR/cpvenv_test" >/dev/null 2>&1 204 | touch "$TMPDIR/cpvenv_test/mytestpackage" 205 | 206 | assertTrue "Virtualenv to clone didn't get created" "[ -d $TMPDIR/cpvenv_test ]" 207 | assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" 208 | 209 | typeset listed=$(lsvirtualenv) 210 | [[ "$listed" != *cpvenv_test* ]] 211 | RC=$? 212 | assertTrue "Cloned virtualenv found in virtualenv list" $RC 213 | 214 | cpvirtualenv '$TMPDIR/cpvenv_test' >/dev/null 2>&1 >/dev/null 2>&1 215 | 216 | # verify cloned venv exist 217 | assertTrue "Cloned virtualenv doesn't exist" "[ -d $WORKON_HOME/cpvenv_test ]" 218 | assertTrue "Test package is missing" "[ -f $WORKON_HOME/cpvenv_test/mytestpackage ]" 219 | 220 | listed=$(lsvirtualenv -b) 221 | [[ "$listed" == *cpvenv_test* ]] 222 | RC=$? 223 | assertTrue "Cloned virtualenv not found in virtualenv list" $RC 224 | 225 | assertSame "cpvenv_test" "$(basename $VIRTUAL_ENV)" 226 | assertTrue "Virtualenv not active" virtualenvwrapper_verify_active_environment 227 | 228 | rm -rf "$TMPDIR/cpvenv_test" 229 | } 230 | 231 | source "$test_dir/shunit2" 232 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. virtualenvwrapper documentation master file, created by 2 | sphinx-quickstart on Thu May 28 22:35:13 2009. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ########################### 7 | virtualenvwrapper |release| 8 | ########################### 9 | 10 | virtualenvwrapper is a set of extensions to Ian Bicking's `virtualenv 11 | `_ tool. The extensions 12 | include wrappers for creating and deleting virtual environments and 13 | otherwise managing your development workflow, making it easier to work 14 | on more than one project at a time without introducing conflicts in 15 | their dependencies. 16 | 17 | ======== 18 | Features 19 | ======== 20 | 21 | 1. Organizes all of your virtual environments in one place. 22 | 2. Wrappers for managing your virtual environments (create, delete, 23 | copy). 24 | 3. Use a single command to switch between environments. 25 | 4. Tab completion for commands that take a virtual environment as 26 | argument. 27 | 5. User-configurable hooks for all operations (see :ref:`scripts`). 28 | 6. Plugin system for creating more sharable extensions (see 29 | :ref:`plugins`). 30 | 31 | ============ 32 | Introduction 33 | ============ 34 | 35 | The best way to explain the features virtualenvwrapper gives you is to 36 | show it in use. 37 | 38 | First, some initialization steps. Most of this only needs to be done 39 | one time. You will want to add the command to ``source 40 | /usr/local/bin/virtualenvwrapper.sh`` to your shell startup file, 41 | changing the path to virtualenvwrapper.sh depending on where it was 42 | installed by pip or your package manager. 43 | 44 | :: 45 | 46 | $ pip install virtualenvwrapper 47 | ... 48 | $ export WORKON_HOME=~/Envs 49 | $ mkdir -p $WORKON_HOME 50 | $ source /usr/local/bin/virtualenvwrapper.sh 51 | $ mkvirtualenv env1 52 | Installing 53 | setuptools.......................................... 54 | .................................................... 55 | .................................................... 56 | ...............................done. 57 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/predeactivate 58 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/postdeactivate 59 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/preactivate 60 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/postactivate New python executable in env1/bin/python 61 | (env1)$ ls $WORKON_HOME 62 | env1 hook.log 63 | 64 | Now we can install some software into the environment. 65 | 66 | :: 67 | 68 | (env1)$ pip install django 69 | Downloading/unpacking django 70 | Downloading Django-1.1.1.tar.gz (5.6Mb): 5.6Mb downloaded 71 | Running setup.py egg_info for package django 72 | Installing collected packages: django 73 | Running setup.py install for django 74 | changing mode of build/scripts-2.6/django-admin.py from 644 to 755 75 | changing mode of /Users/dhellmann/Envs/env1/bin/django-admin.py to 755 76 | Successfully installed django 77 | 78 | We can see the new package with ``lssitepackages``:: 79 | 80 | (env1)$ lssitepackages 81 | Django-1.1.1-py2.6.egg-info easy-install.pth 82 | setuptools-0.6.10-py2.6.egg pip-0.6.3-py2.6.egg 83 | django setuptools.pth 84 | 85 | Of course we are not limited to a single virtualenv:: 86 | 87 | (env1)$ ls $WORKON_HOME 88 | env1 hook.log 89 | (env1)$ mkvirtualenv env2 90 | Installing setuptools............................... 91 | .................................................... 92 | .................................................... 93 | ........... ...............................done. 94 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/predeactivate 95 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/postdeactivate 96 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/preactivate 97 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/postactivate New python executable in env2/bin/python 98 | (env2)$ ls $WORKON_HOME 99 | env1 env2 hook.log 100 | 101 | Switch between environments with ``workon``:: 102 | 103 | (env2)$ workon env1 104 | (env1)$ echo $VIRTUAL_ENV 105 | /Users/dhellmann/Envs/env1 106 | (env1)$ 107 | 108 | The ``workon`` command also includes tab completion for the 109 | environment names, and invokes customization scripts as an environment 110 | is activated or deactivated (see :ref:`scripts`). 111 | 112 | :: 113 | 114 | (env1)$ echo 'cd $VIRTUAL_ENV' >> $WORKON_HOME/postactivate 115 | (env1)$ workon env2 116 | (env2)$ pwd 117 | /Users/dhellmann/Envs/env2 118 | 119 | :ref:`scripts-postmkvirtualenv` is run when a new environment is 120 | created, letting you automatically install commonly-used tools. 121 | 122 | :: 123 | 124 | (env2)$ echo 'pip install sphinx' >> $WORKON_HOME/postmkvirtualenv 125 | (env3)$ mkvirtualenv env3 126 | New python executable in env3/bin/python 127 | Installing setuptools............................... 128 | .................................................... 129 | .................................................... 130 | ........... ...............................done. 131 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/predeactivate 132 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/postdeactivate 133 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/preactivate 134 | virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/postactivate 135 | Downloading/unpacking sphinx 136 | Downloading Sphinx-0.6.5.tar.gz (972Kb): 972Kb downloaded 137 | Running setup.py egg_info for package sphinx 138 | no previously-included directories found matching 'doc/_build' 139 | Downloading/unpacking Pygments>=0.8 (from sphinx) 140 | Downloading Pygments-1.3.1.tar.gz (1.1Mb): 1.1Mb downloaded 141 | Running setup.py egg_info for package Pygments 142 | Downloading/unpacking Jinja2>=2.1 (from sphinx) 143 | Downloading Jinja2-2.4.tar.gz (688Kb): 688Kb downloaded 144 | Running setup.py egg_info for package Jinja2 145 | warning: no previously-included files matching '*' found under directory 'docs/_build/doctrees' 146 | Downloading/unpacking docutils>=0.4 (from sphinx) 147 | Downloading docutils-0.6.tar.gz (1.4Mb): 1.4Mb downloaded 148 | Running setup.py egg_info for package docutils 149 | Installing collected packages: docutils, Jinja2, Pygments, sphinx 150 | Running setup.py install for docutils 151 | Running setup.py install for Jinja2 152 | Running setup.py install for Pygments 153 | Running setup.py install for sphinx 154 | no previously-included directories found matching 'doc/_build' 155 | Installing sphinx-build script to /Users/dhellmann/Envs/env3/bin 156 | Installing sphinx-quickstart script to /Users/dhellmann/Envs/env3/bin 157 | Installing sphinx-autogen script to /Users/dhellmann/Envs/env3/bin 158 | Successfully installed docutils Jinja2 Pygments sphinx (env3)$ 159 | (venv3)$ which sphinx-build 160 | /Users/dhellmann/Envs/env3/bin/sphinx-build 161 | 162 | Through a combination of the existing functions defined by the core 163 | package (see :ref:`command`), third-party plugins (see 164 | :ref:`plugins`), and user-defined scripts (see :ref:`scripts`) 165 | virtualenvwrapper gives you a wide variety of opportunities to 166 | automate repetitive operations. 167 | 168 | ======= 169 | Details 170 | ======= 171 | 172 | .. toctree:: 173 | :maxdepth: 2 174 | 175 | install 176 | command_ref 177 | hooks 178 | projects 179 | tips 180 | developers 181 | extensions 182 | design 183 | history 184 | glossary 185 | 186 | .. _references: 187 | 188 | ========== 189 | References 190 | ========== 191 | 192 | `virtualenv `_, from Ian 193 | Bicking, is a pre-requisite to using these extensions. 194 | 195 | For more details, refer to the column I wrote for the May 2008 issue 196 | of Python Magazine: `virtualenvwrapper | And Now For Something 197 | Completely Different 198 | `_. 199 | 200 | Manuel Kaufmann has `translated this documentation into Spanish 201 | `__. 202 | 203 | Tetsuya Morimoto has `translated this documentation into Japanese 204 | `__. 205 | 206 | ======= 207 | Support 208 | ======= 209 | 210 | Join the `virtualenvwrapper Google Group 211 | `__ to discuss 212 | issues and features. 213 | 214 | Report bugs via the `bug tracker on GitHub 215 | `__. 216 | 217 | Shell Aliases 218 | ============= 219 | 220 | Since virtualenvwrapper is largely a shell script, it uses shell 221 | commands for a lot of its actions. If your environment makes heavy 222 | use of shell aliases or other customizations, you may encounter 223 | issues. Before reporting bugs in the bug tracker, please test 224 | *without* your aliases enabled. If you can identify the alias causing 225 | the problem, that will help make virtualenvwrapper more robust. 226 | 227 | .. _license: 228 | 229 | ======= 230 | License 231 | ======= 232 | 233 | Copyright Doug Hellmann, All Rights Reserved 234 | 235 | Permission to use, copy, modify, and distribute this software and its 236 | documentation for any purpose and without fee is hereby granted, 237 | provided that the above copyright notice appear in all copies and that 238 | both that copyright notice and this permission notice appear in 239 | supporting documentation, and that the name of Doug Hellmann not be 240 | used in advertising or publicity pertaining to distribution of the 241 | software without specific, written prior permission. 242 | 243 | DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 244 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 245 | EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR 246 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 247 | USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 248 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 249 | PERFORMANCE OF THIS SOFTWARE. 250 | -------------------------------------------------------------------------------- /virtualenvwrapper/user_scripts.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 3 | # Copyright (c) 2010 Doug Hellmann. All rights reserved. 4 | # 5 | """Plugin to handle hooks in user-defined scripts. 6 | """ 7 | 8 | import logging 9 | import os 10 | import re 11 | import stat 12 | import subprocess 13 | import sys 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | # Are we running under msys 19 | if sys.platform == 'win32' and \ 20 | os.environ.get('OS') == 'Windows_NT' and \ 21 | os.environ.get('MSYSTEM') in ('MINGW32', 'MINGW64'): 22 | is_msys = True 23 | script_folder = 'Scripts' 24 | else: 25 | is_msys = False 26 | script_folder = 'bin' 27 | 28 | 29 | def _get_msys_shell(): 30 | if 'MSYS_HOME' in os.environ: 31 | return [get_path(os.environ['MSYS_HOME'], 'bin', 'sh.exe')] 32 | else: 33 | for path in os.environ['PATH'].split(';'): 34 | if os.path.exists(os.path.join(path, 'sh.exe')): 35 | return [get_path(path, 'sh.exe')] 36 | raise Exception('Could not find sh.exe') 37 | 38 | 39 | def run_script(script_path, *args): 40 | """Execute a script in a subshell. 41 | """ 42 | if os.path.exists(script_path): 43 | cmd = [script_path] + list(args) 44 | if is_msys: 45 | cmd = _get_msys_shell() + cmd 46 | log.debug('running %s', str(cmd)) 47 | try: 48 | subprocess.call(cmd) 49 | except OSError: 50 | _, msg, _ = sys.exc_info() 51 | log.error('could not run "%s": %s', script_path, str(msg)) 52 | # log.debug('Returned %s', return_code) 53 | return 54 | 55 | 56 | def run_global(script_name, *args): 57 | """Run a script from $VIRTUALENVWRAPPER_HOOK_DIR. 58 | """ 59 | script_path = get_path('$VIRTUALENVWRAPPER_HOOK_DIR', script_name) 60 | run_script(script_path, *args) 61 | return 62 | 63 | 64 | PERMISSIONS = ( 65 | stat.S_IRWXU # read/write/execute, user 66 | | stat.S_IRGRP # read, group 67 | | stat.S_IXGRP # execute, group 68 | | stat.S_IROTH # read, others 69 | | stat.S_IXOTH # execute, others 70 | ) 71 | PERMISSIONS_SOURCED = PERMISSIONS & ~( 72 | # remove executable bits for 73 | stat.S_IXUSR # ... user 74 | | stat.S_IXGRP # ... group 75 | | stat.S_IXOTH # ... others 76 | ) 77 | 78 | 79 | GLOBAL_HOOKS = [ 80 | # initialize 81 | ("initialize", 82 | "This hook is sourced during the startup phase " 83 | "when loading virtualenvwrapper.sh.", 84 | PERMISSIONS_SOURCED), 85 | 86 | # mkvirtualenv 87 | ("premkvirtualenv", 88 | "This hook is run after a new virtualenv is created " 89 | "and before it is activated.\n" 90 | "# argument: name of new environment", 91 | PERMISSIONS), 92 | ("postmkvirtualenv", 93 | "This hook is sourced after a new virtualenv is activated.", 94 | PERMISSIONS_SOURCED), 95 | 96 | # cpvirtualenv: 97 | # precpvirtualenv (run), 98 | # postcpvirtualenv (sourced) 99 | 100 | # rmvirtualenv 101 | ("prermvirtualenv", 102 | "This hook is run before a virtualenv is deleted.\n" 103 | "# argument: full path to environment directory", 104 | PERMISSIONS), 105 | ("postrmvirtualenv", 106 | "This hook is run after a virtualenv is deleted.\n" 107 | "# argument: full path to environment directory", 108 | PERMISSIONS), 109 | 110 | # deactivate 111 | ("predeactivate", 112 | "This hook is sourced before every virtualenv is deactivated.", 113 | PERMISSIONS_SOURCED), 114 | ("postdeactivate", 115 | "This hook is sourced after every virtualenv is deactivated.", 116 | PERMISSIONS_SOURCED), 117 | 118 | # activate 119 | ("preactivate", 120 | "This hook is run before every virtualenv is activated.\n" 121 | "# argument: environment name", 122 | PERMISSIONS), 123 | ("postactivate", 124 | "This hook is sourced after every virtualenv is activated.", 125 | PERMISSIONS_SOURCED), 126 | 127 | # mkproject: 128 | # premkproject (run), 129 | # postmkproject (sourced) 130 | 131 | # get_env_details 132 | ("get_env_details", 133 | "This hook is run when the list of virtualenvs is printed " 134 | "so each name can include details.\n" 135 | "# argument: environment name", 136 | PERMISSIONS), 137 | ] 138 | 139 | 140 | LOCAL_HOOKS = [ 141 | # deactivate 142 | ("predeactivate", 143 | "This hook is sourced before this virtualenv is deactivated.", 144 | PERMISSIONS_SOURCED), 145 | ("postdeactivate", 146 | "This hook is sourced after this virtualenv is deactivated.", 147 | PERMISSIONS_SOURCED), 148 | 149 | # activate 150 | ("preactivate", 151 | "This hook is run before this virtualenv is activated.", 152 | PERMISSIONS), 153 | ("postactivate", 154 | "This hook is sourced after this virtualenv is activated.", 155 | PERMISSIONS_SOURCED), 156 | 157 | # get_env_details 158 | ("get_env_details", 159 | "This hook is run when the list of virtualenvs is printed " 160 | "in 'long' mode so each name can include details.\n" 161 | "# argument: environment name", 162 | PERMISSIONS), 163 | ] 164 | 165 | 166 | def make_hook(filename, comment, permissions): 167 | """Create a hook script. 168 | 169 | :param filename: The name of the file to write. 170 | :param comment: The comment to insert into the file. 171 | """ 172 | filename = get_path(filename) 173 | if not os.path.exists(filename): 174 | log.info('creating %s', filename) 175 | f = open(filename, 'w') 176 | try: 177 | # for sourced scripts, the shebang line won't be used; 178 | # it is useful for editors to recognize the file type, though 179 | f.write("#!%(shell)s\n# %(comment)s\n\n" % { 180 | 'comment': comment, 181 | 'shell': os.environ.get('SHELL', '/bin/sh'), 182 | }) 183 | finally: 184 | f.close() 185 | os.chmod(filename, permissions) 186 | return 187 | 188 | 189 | # HOOKS 190 | 191 | 192 | def initialize(args): 193 | for filename, comment, permissions in GLOBAL_HOOKS: 194 | make_hook(get_path('$VIRTUALENVWRAPPER_HOOK_DIR', filename), 195 | comment, permissions) 196 | return 197 | 198 | 199 | def initialize_source(args): 200 | return """ 201 | # 202 | # Run user-provided scripts 203 | # 204 | [ -f "$VIRTUALENVWRAPPER_HOOK_DIR/initialize" ] && \ 205 | source "$VIRTUALENVWRAPPER_HOOK_DIR/initialize" 206 | """ 207 | 208 | 209 | def pre_mkvirtualenv(args): 210 | log.debug('pre_mkvirtualenv %s', str(args)) 211 | envname = args[0] 212 | for filename, comment, permissions in LOCAL_HOOKS: 213 | make_hook(get_path('$WORKON_HOME', envname, script_folder, filename), 214 | comment, permissions) 215 | run_global('premkvirtualenv', *args) 216 | return 217 | 218 | 219 | def post_mkvirtualenv_source(args): 220 | log.debug('post_mkvirtualenv_source %s', str(args)) 221 | return """ 222 | # 223 | # Run user-provided scripts 224 | # 225 | [ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postmkvirtualenv" ] && \ 226 | source "$VIRTUALENVWRAPPER_HOOK_DIR/postmkvirtualenv" 227 | """ 228 | 229 | 230 | def pre_cpvirtualenv(args): 231 | log.debug('pre_cpvirtualenv %s', str(args)) 232 | envname = args[0] 233 | for filename, comment, permissions in LOCAL_HOOKS: 234 | make_hook(get_path('$WORKON_HOME', envname, script_folder, filename), 235 | comment, permissions) 236 | run_global('precpvirtualenv', *args) 237 | return 238 | 239 | 240 | def post_cpvirtualenv_source(args): 241 | log.debug('post_cpvirtualenv_source %s', str(args)) 242 | return """ 243 | # 244 | # Run user-provided scripts 245 | # 246 | [ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postcpvirtualenv" ] && \ 247 | source "$VIRTUALENVWRAPPER_HOOK_DIR/postcpvirtualenv" 248 | """ 249 | 250 | 251 | def pre_rmvirtualenv(args): 252 | log.debug('pre_rmvirtualenv') 253 | run_global('prermvirtualenv', *args) 254 | return 255 | 256 | 257 | def post_rmvirtualenv(args): 258 | log.debug('post_rmvirtualenv') 259 | run_global('postrmvirtualenv', *args) 260 | return 261 | 262 | 263 | def pre_activate(args): 264 | log.debug('pre_activate') 265 | run_global('preactivate', *args) 266 | script_path = get_path('$WORKON_HOME', args[0], 267 | script_folder, 'preactivate') 268 | run_script(script_path, *args) 269 | return 270 | 271 | 272 | def post_activate_source(args): 273 | log.debug('post_activate_source') 274 | return """ 275 | # 276 | # Run user-provided scripts 277 | # 278 | [ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postactivate" ] && \ 279 | source "$VIRTUALENVWRAPPER_HOOK_DIR/postactivate" 280 | [ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/postactivate" ] && \ 281 | source "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/postactivate" 282 | """ 283 | 284 | 285 | def pre_deactivate_source(args): 286 | log.debug('pre_deactivate_source') 287 | return """ 288 | # 289 | # Run user-provided scripts 290 | # 291 | [ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/predeactivate" ] && \ 292 | source "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/predeactivate" 293 | [ -f "$VIRTUALENVWRAPPER_HOOK_DIR/predeactivate" ] && \ 294 | source "$VIRTUALENVWRAPPER_HOOK_DIR/predeactivate" 295 | """ 296 | 297 | 298 | def post_deactivate_source(args): 299 | log.debug('post_deactivate_source') 300 | return """ 301 | # 302 | # Run user-provided scripts 303 | # 304 | VIRTUALENVWRAPPER_LAST_VIRTUAL_ENV="$WORKON_HOME/%(env_name)s" 305 | [ -f "$WORKON_HOME/%(env_name)s/bin/postdeactivate" ] && \ 306 | source "$WORKON_HOME/%(env_name)s/bin/postdeactivate" 307 | [ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postdeactivate" ] && \ 308 | source "$VIRTUALENVWRAPPER_HOOK_DIR/postdeactivate" 309 | unset VIRTUALENVWRAPPER_LAST_VIRTUAL_ENV 310 | """ % {'env_name': args[0]} 311 | 312 | 313 | def get_env_details(args): 314 | log.debug('get_env_details') 315 | run_global('get_env_details', *args) 316 | script_path = get_path('$WORKON_HOME', args[0], 317 | script_folder, 'get_env_details') 318 | run_script(script_path, *args) 319 | return 320 | 321 | 322 | def get_path(*args): 323 | ''' 324 | Get a full path from args. 325 | 326 | Path separator is determined according to the os and the shell and 327 | allow to use is_msys. 328 | 329 | Variables and user are expanded during the process. 330 | ''' 331 | path = os.path.expanduser(os.path.expandvars(os.path.join(*args))) 332 | if is_msys: 333 | # MSYS accept unix or Win32 and sometimes 334 | # it drives to mixed style paths 335 | if re.match(r'^/[a-zA-Z](/|^)', path): 336 | # msys path could starts with '/c/'-form drive letter 337 | path = ''.join((path[1], ':', path[2:])) 338 | path = path.replace('/', os.sep) 339 | 340 | return os.path.abspath(path) 341 | --------------------------------------------------------------------------------