diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7a1392c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +# Via https://github.com/nedbat/scriv/blob/main/.github/dependabot.yml +# +# From: +# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/keeping-your-actions-up-to-date-with-dependabot +# Set update schedule for GitHub Actions + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions once a week + interval: "weekly" diff --git a/.github/workflows/python-publish.yaml b/.github/workflows/python-publish.yaml new file mode 100644 index 0000000..7012340 --- /dev/null +++ b/.github/workflows/python-publish.yaml @@ -0,0 +1,39 @@ +# This workflows will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: Upload Python Package + +on: + - push + +jobs: + build-n-publish: + name: Build and publish Python distributions to PyPI and TestPyPI + if: ${{ github.repository_owner == 'python-virtualenvwrapper' }} + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[build] + - name: Build sdist and wheel + run: | + python -m build + - name: Publish distribution to PyPI + # This condition prevents PRs from being published as part of + # the test job. + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@v1.12.4 + with: + password: ${{ secrets.PYPI_API_TOKEN }} + verbose: true + print_hash: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..74b0237 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,150 @@ +name: Test + +on: + - push + - pull_request + +jobs: + + # Test different python versions with bash on Ubuntu. + ubuntu: + name: Test Ubuntu + runs-on: ubuntu-latest + if: ${{ !startsWith(github.ref, 'refs/tags') }} + + strategy: + fail-fast: false + matrix: + python-version: + - 3.8 + - 3.9 + - "3.10" + - "3.11" + - "3.12" + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: python -m pip install tox + + - name: Run tests + run: tox -e py + + # Test the latest python version with zsh on macOS + zsh: + name: Test Zsh + runs-on: macos-latest + if: ${{ !startsWith(github.ref, 'refs/tags') }} + + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: python -m pip install tox + + - name: Run tests + run: tox -e zsh + + # Test different python versions with bash on macOS + macos: + name: Test macOS + runs-on: macos-latest + if: ${{ !startsWith(github.ref, 'refs/tags') }} + + strategy: + fail-fast: false + matrix: + python-version: + - 3.8 + - 3.9 + - "3.10" + - "3.11" + - "3.12" + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: python -m pip install tox + + - name: Run tests + run: tox -e py + + # Run various style checkers + style: + runs-on: ubuntu-latest + if: ${{ !startsWith(github.ref, 'refs/tags') }} + + strategy: + fail-fast: false + matrix: + tox-environment: + - docs + - style + - pkglint + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: python -m pip install tox + + - name: Run + run: tox -e ${{ matrix.tox-environment }} + + # Test build the docs + docs: + runs-on: ubuntu-latest + if: ${{ !startsWith(github.ref, 'refs/tags') }} + + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + # Pin this to the same version used on in .readthedocs.yaml + python-version: "3.11" + + - name: Install dependencies + run: python -m pip install tox + + - name: Run + run: tox -e docs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..df625c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +syntax: glob +*~ +*.py[co] +README.html +build +dist +*.egg +distribute*.tar.gz +docs/build +docs/html +docs/website +docs/en +docs/es +docs/ja +tests/catch_output +tests/testpackage/build +tests/testpackage/dist +tests/testpackage/testpackage.egg-info +tests/testtemplate/build +tests/testtemplate/dist +tests/testtemplate/testtemplate.egg-info +trace.txt +virtualenvwrapper.egg-info +virtualenvwrapper/docs +.tox +*.orig +TAGS +ChangeLog +AUTHORS +bin +include +lib + +syntax: re +.DS_Store +^web/ +.python-version +.eggs +/virtualenvwrapper/version.py diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 0000000..d898af4 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,98 @@ +pull_request_rules: + + - name: Add CI label + conditions: + - or: + - "title~=^tox:" + - "title~=^ci:" + - "files~=tox.ini" + - "files~=tests" + actions: + label: + add: + - ci + + - name: Add Mergify label + conditions: + - or: + - "title~=^mergify:" + - "files~=.mergify.yml$" + actions: + label: + add: + - mergify + + - name: Add Documentation label + conditions: + - or: + - "title~=^docs:" + - "files~=docs" + - "files~=.rst$" + - "files~=.readthedocs.yaml$" + actions: + label: + add: + - documentation + + - name: automatic approval for Dependabot pull requests + conditions: + - author=dependabot[bot] + actions: + review: + type: APPROVE + message: Automatically approving dependabot + + - name: Add breaking-change label + # https://docs.openstack.org/pbr/latest/user/features.html + conditions: + - "body~=Sem-Ver: api-break" + actions: + label: + add: + - "breaking change" + + - name: Add feature label + # https://docs.openstack.org/pbr/latest/user/features.html + conditions: + - "body~=Sem-Ver: feature" + actions: + label: + add: + - feature + + - name: Add automatic-merge label + conditions: + - or: + - "author=dhellmann" + - "author=jasonamyers" + - "label!=mergify" + actions: + label: + add: + - automatic-merge + + - name: Automatic merge on approval + conditions: + - and: + - "check-success=docs" + - "check-success=style (style)" + - "check-success=style (pkglint)" + - "check-success=Test macOS (3.8)" + - "check-success=Test macOS (3.9)" + - "check-success=Test macOS (3.10)" + - "check-success=Test macOS (3.11)" + - "check-success=Test macOS (3.12)" + - "check-success=Test Ubuntu (3.8)" + - "check-success=Test Ubuntu (3.9)" + - "check-success=Test Ubuntu (3.10)" + - "check-success=Test Ubuntu (3.11)" + - "check-success=Test Ubuntu (3.12)" + - "check-success=Test Zsh" + - "-draft" + - or: + - "#approved-reviews-by>=1" + - "author=dhellmann" + - "author=jasonamyers" + actions: + merge: + method: merge diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..fba7a7d --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,15 @@ +version: 2 + +sphinx: + configuration: docs/source/conf.py + +build: + os: "ubuntu-20.04" + tools: + python: "3.11" + +python: + # Install our python package before building the docs + install: + - method: pip + path: . diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c0dfc2e --- /dev/null +++ b/LICENSE @@ -0,0 +1,17 @@ +Copyright Doug Hellmann, All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Doug Hellmann not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in index 54a1c81..7634892 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include README.html +recursive-include docs *.rst *.py *.html *.css *.js *.png *.txt diff --git a/Makefile b/Makefile index 22e4cc7..253bab6 100644 --- a/Makefile +++ b/Makefile @@ -1,41 +1,46 @@ -# -# $Id$ -# - -SVNHOME=$(shell svn info | grep "^URL" | cut -f2- -d:) -PROJECT=virtualenvwrapper -VERSION=$(shell basename $(SVNHOME)) -RELEASE=$(PROJECT)-$(VERSION) - -info: - SVNHOME=$(SVNHOME) - PROJECT=$(PROJECT) - VERSION=$(VERSION) - RELEASE=$(RELEASE) - -package: - rm -f setup.py - $(MAKE) setup.py README.html - python setup.py sdist --force-manifest - mv dist/*.gz ~/Desktop/ - -register: setup.py +# Default target is to show help +help: + @echo "sdist - Source distribution" + @echo "html - HTML documentation" + @echo "docclean - Remove documentation build files" + @echo "upload - upload a new release to PyPI" + @echo "develop - install development version" + @echo "test - run the test suite" + @echo "test-quick - run the test suite for bash and one version of Python ($(PYTHON27))" + +.PHONY: sdist +sdist: html + rm -f dist/*.gz + rm -rf docs/website + python setup.py sdist + cp -v dist/*.gz ~/Desktop + +# Documentation +.PHONY: html +html: + tox -e docs + +.PHONY: docclean +docclean: + rm -rf docs/build docs/html + +# Register the new version on pypi +.PHONY: register +register: + echo "USE upload target" + exit 1 python setup.py register -README.html: README - rst2html.py $< $@ +.PHONY: upload +upload: + python setup.py sdist upload -%: %.in - cat $< | sed 's/VERSION/$(VERSION)/g' > $@ - chmod -w $@ +# Testing +test: + tox +test-quick: + tox -e py27 -dist: - mkdir -p dist - -# -# Dump a version that does not include .svn directories. -# -export: - rm -rf dist/$(RELEASE) - (cd dist; svn export $(SVNHOME) $(RELEASE); rm -f $(RELEASE)/Makefile) +develop: + python setup.py develop diff --git a/README b/README deleted file mode 100644 index 2ed0784..0000000 --- a/README +++ /dev/null @@ -1,24 +0,0 @@ -################# -virtualenvwrapper -################# - -=========== -Quick Setup -=========== - -1. Add a line like ``export WORKON_HOME=$HOME/.virtualenvs`` to your .bashrc. -2. Add a line like ``source /path/to/this/file/virtualenvwrapper_bashrc`` to your .bashrc. -3. Run: ``source ~/.bashrc`` -4. Run: ``workon`` -5. A list of environments, empty, is printed. -6. Run: ``mkvirtualenv temp`` -7. Run: ``workon`` -8. This time, the ``temp`` environment is included. -9. Run: ``workon temp`` -10. The virtual environment is activated. - -========== -References -========== - -For more details, refer to the column I wrote for the May 2008 issue of Python Magazine: `virtualenvwrapper | And Now For Something Completely Different `_. \ No newline at end of file diff --git a/README.es.rst b/README.es.rst new file mode 100644 index 0000000..15d6764 --- /dev/null +++ b/README.es.rst @@ -0,0 +1,83 @@ +.. -*- mode: rst -*- + +################# +virtualenvwrapper +################# + +virtualenvwrapper es un conjunto de extensiones de la herramienta de Ian +Bicking `virtualenv `_. Las extensiones +incluyen funciones para la creación y eliminación de entornos virtuales y por otro +lado administración de tu rutina de desarrollo, haciendo fácil trabajar en más +de un proyecto al mismo tiempo sin introducir conflictos entre sus dependencias. + +=============== +Características +=============== + +1. Organiza todos tus entornos virtuales en un sólo lugar. + +2. Funciones para administrar tus entornos virtuales (crear, eliminar, copiar). + +3. Usa un sólo comando para cambiar entre los entornos. + +4. Completa con Tab los comandos que toman un entorno virtual como argumento. + +5. Ganchos configurables para todas las operaciones. + +6. Sistema de plugins para la creación de extensiones compartibles. + +Rich Leland ha grabado un pequeño `screencast +`__ +mostrando las características de virtualenvwrapper. + + +=========== +Instalación +=========== + +Ve a la `documentación del proyecto `__ +para las instrucciones de instalación y configuración. + +Actualizar desde 1.x +==================== + +El script de shell que contiene las funciones ha sido renombrado en la serie +2.x para reflejar el hecho de que otros shells, además de bash, son soportados. En +tu archivo de inicio del shell, cambia ``source +/usr/local/bin/virtualenvwrapper_bashrc`` por ``source +/usr/local/bin/virtualenvwrapper.sh``. + +============== +Contribuciones +============== + +Antes de contribuir con nuevas características al *core* de virtualenvwrapper, +por favor considera, en vez, si no debe ser implementada como una extensión. + +Ve a la `documentación para desarrolladores +`__ +por trucos sobre parches. + +======== +Licencia +======== + +Copyright Doug Hellmann, All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Doug Hellmann not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +.. _github: https://github.com/python-virtualenvwrapper/virtualenvwrapper/ diff --git a/README.ja.rst b/README.ja.rst new file mode 100644 index 0000000..a3931e7 --- /dev/null +++ b/README.ja.rst @@ -0,0 +1,123 @@ +.. -*- mode: rst -*- + +################# +virtualenvwrapper +################# + +virtualenvwrapper は Ian Bicking の +`virtualenv `_ ツールの +拡張機能です。この拡張機能は仮想環境の作成・削除を行ったり、 +開発ワークフローを管理するラッパーを提供します。このラッパーを +使用することで、開発環境の依存による競合を発生させず、1つ以上の +プロジェクトで同時に作業し易くなります。 + +==== +機能 +==== + +1. 1つの開発環境で全ての仮想環境を構成する + +2. 仮想環境を管理(作成、削除、コピー)するラッパー + +3. たった1つのコマンドで仮想環境を切り替える + +4. コマンドの引数として仮想環境がタブ補完できる + +5. 全ての操作に対してユーザ設定でフックできる(:ref:`scripts` を参照) + +6. さらに共有可能な拡張機能を作成できるプラグインシステム(:ref:`plugins` を参照) + +Rich Leland は virtualenvwrapper の機能を誇示するために短い +`スクリーンキャスト `__ +を作成しました。 + +============ +インストール +============ + +インストールとインフラを設定するには +`プロジェクトのドキュメント `__ +を参照してください。 + +サポートシェル +============== + +virtualenvwrapper は Bourne シェル互換の構文で定義された +シェル *関数* のセットです。それは `bash`, `ksh` と `zsh` で +テストされています。その他のシェルでも動作するかもしれませんが、 +ここに記載されていないシェルで動作することを発見したら私に +教えてください。もしあなたがその他のシェルで動作させるために +virtualenvwrapper を完全に書き直すことなく修正できるなら、 +GitHub のプロジェクトページを通じて pull リクエストを +送ってください。あなたが非互換なシェル上で動作させるクローンを +作成するなら、このページでリンクを張るので私に連絡してください。 + +Python バージョン +================= + +virtualenvwrapper は Python 2.4 - 2.7 でテストされています。 + +1.x からのアップグレード +======================== + +ラッパー関数を含むシェルスクリプトは 2.x バージョンで bash +以外のシェルをサポートするためにその名前が変更されました。 +あなたの起動ファイルの ``source /usr/local/bin/virtualenvwrapper_bashrc`` を +``source /usr/local/bin/virtualenvwrapper.sh`` へ変更してください。 + +==== +貢献 +==== + +virtualenvwrapper のコアへ新しい機能を追加する前に、 +その代わりに機能拡張として実装すべきかどうかをよく考えてください。 + +パッチを提供するための tips は +`開発者ドキュメント `__ +を参照してください。 + +======== +サポート +======== + +問題や機能を議論するには +`virtualenvwrapper Google Group `__ +に参加してください。 + +`GitHub のバグトラッカー `__ +でバグを報告してください。 + +シェルエイリアス +================ + +virtualenvwrapper は大きなシェルスクリプトなので、 +多くのアクションはシェルコマンドを使用します。 +あなたの環境が多くのシェルエイリアスやその他の +カスタマイズを行っているなら、何かしら問題に +遭遇する可能性があります。バグトラッカーにバグを +報告する前に、そういったエイリアスを無効な *状態* で +テストしてください。あなたがその問題を引き起こす +エイリアスを判別できるなら virtualenvwrapper を +もっと堅牢なものにすることに役立つでしょう。 + +========== +ライセンス +========== + +Copyright Doug Hellmann, All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Doug Hellmann not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..6cd1554 --- /dev/null +++ b/README.txt @@ -0,0 +1,116 @@ +.. -*- mode: rst -*- + +################# +virtualenvwrapper +################# + +virtualenvwrapper is a set of extensions to Ian Bicking's `virtualenv +`_ tool. The extensions include +wrappers for creating and deleting virtual environments and otherwise +managing your development workflow, making it easier to work on more +than one project at a time without introducing conflicts in their +dependencies. + +**Warning:** The 5.x release requires virtualenv 20+ + +======== +Features +======== + +1. Organizes all of your virtual environments in one place. + +2. Wrappers for creating, copying and deleting environments, including + user-configurable hooks. + +3. Use a single command to switch between environments. + +4. Tab completion for commands that take a virtual environment as + argument. + +5. User-configurable hooks for all operations. + +6. Plugin system for more creating sharable extensions. + +Rich Leland has created a short `screencast +`__ showing off the features of +virtualenvwrapper. + +============ +Installation +============ + +See the `project documentation +`__ for +installation and setup instructions. + +Supported Shells +================ + +virtualenvwrapper is a set of shell *functions* defined in Bourne +shell compatible syntax. It is tested under ``bash`` and +``zsh``. It may work with other shells, so if you find that it does +work with a shell not listed here please let us know by opening a +`ticket on GitHub +`_. +If you can modify it to work with another shell, without completely +rewriting it, send a pull request through the `GitHub project page +`_. If +you write a clone to work with an incompatible shell, let us know and +we will link to it from this page. + +Python Versions +=============== + +virtualenvwrapper is tested under Python 3.8 - 3.12 on macOS and Linux. + +======= +Support +======= + +Join the `virtualenvwrapper Google Group +`__ to discuss +issues and features. + +Report bugs via the `bug tracker on GitHub +`__. + +Shell Aliases +============= + +Since virtualenvwrapper is largely a shell script, it uses shell +commands for a lot of its actions. If your environment makes heavy +use of shell aliases or other customizations, you may encounter +issues. Before reporting bugs in the bug tracker, please test +*without* your aliases enabled. If you can identify the alias causing +the problem, that will help make virtualenvwrapper more robust. + +========== +Change Log +========== + +The `release history`_ is part of the project documentation. + +.. _release history: https://virtualenvwrapper.readthedocs.io/en/latest/history.html + + +======= +License +======= + +Copyright Doug Hellmann, All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Doug Hellmann not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/TODO b/TODO deleted file mode 100644 index e34a424..0000000 --- a/TODO +++ /dev/null @@ -1,2 +0,0 @@ -o register on pypi -o setup.py for easy_install? diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..cc33f87 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/virtualenvwrapper.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/virtualenvwrapper.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/virtualenvwrapper" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/virtualenvwrapper" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..0fca33e --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +# sphinxcontrib-bitbucket +# sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 +sphinx diff --git a/docs/source/command_ref.rst b/docs/source/command_ref.rst new file mode 100644 index 0000000..4571703 --- /dev/null +++ b/docs/source/command_ref.rst @@ -0,0 +1,600 @@ +.. Quick reference documentation for virtualenvwrapper command line functions + Originally contributed Thursday, May 28, 2009 by Steve Steiner (ssteinerX@gmail.com) + +.. _command: + +################# +Command Reference +################# + +All of the commands below are to be used on the Terminal command line. + +===================== +Managing Environments +===================== + +.. _command-mkvirtualenv: + +mkvirtualenv +------------ + +Create a new environment, in the WORKON_HOME. + +Syntax:: + + mkvirtualenv [-a project_path] [-i package] [-r requirements_file] [virtualenv options] ENVNAME + +All command line options except ``-a``, ``-i``, ``-r``, and ``-h`` are passed +directly to ``virtualenv``. The new environment is automatically +activated after being initialized. + +:: + + $ workon + $ mkvirtualenv mynewenv + New python executable in mynewenv/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + (mynewenv)$ workon + mynewenv + (mynewenv)$ + +The ``-a`` option can be used to associate an existing project +directory with the new environment. + +The ``-i`` option can be used to install one or more packages (by +repeating the option) after the environment is created. + +The ``-r`` option can be used to specify a text file listing packages +to be installed. The argument value is passed to ``pip -r`` to be +installed. + +.. seealso:: + + * :ref:`scripts-premkvirtualenv` + * :ref:`scripts-postmkvirtualenv` + * `requirements file format`_ + +.. _requirements file format: https://pip.pypa.io/en/stable/reference/requirements-file-format/ + +.. _command-mktmpenv: + +mktmpenv +-------- + +Create a new virtualenv in the ``WORKON_HOME`` directory. + +Syntax:: + + mktmpenv [(-c|--cd)|(-n|--no-cd)] [VIRTUALENV_OPTIONS] + +A unique virtualenv name is generated. + +If ``-c`` or ``--cd`` is specified the working directory is changed to +the virtualenv directory during the post-activate phase, regardless of +the value of ``VIRTUALENVWRAPPER_WORKON_CD``. + +If ``-n`` or ``--no-cd`` is specified the working directory is **not** +changed to the virtualenv directory during the post-activate phase, +regardless of the value of ``VIRTUALENVWRAPPER_WORKON_CD``. + +:: + + $ mktmpenv + Using real prefix '/Library/Frameworks/Python.framework/Versions/2.7' + New python executable in 1e513ac6-616e-4d56-9aa5-9d0a3b305e20/bin/python + Overwriting 1e513ac6-616e-4d56-9aa5-9d0a3b305e20/lib/python2.7/distutils/__init__.py + with new content + Installing setuptools............................................... + .................................................................... + .................................................................done. + This is a temporary environment. It will be deleted when deactivated. + (1e513ac6-616e-4d56-9aa5-9d0a3b305e20) $ + +.. _command-lsvirtualenv: + +lsvirtualenv +------------ + +List all of the environments. + +Syntax:: + + lsvirtualenv [-b] [-l] [-h] + +-b + Brief mode, disables verbose output. +-l + Long mode, enables verbose output. Default. +-h + Print the help for lsvirtualenv. + +.. seealso:: + + * :ref:`scripts-get_env_details` + +.. _command-showvirtualenv: + +showvirtualenv +-------------- + +Show the details for a single virtualenv. + +Syntax:: + + showvirtualenv [env] + +.. seealso:: + + * :ref:`scripts-get_env_details` + +.. _command-rmvirtualenv: + +rmvirtualenv +------------ + +Remove an environment, in the WORKON_HOME. + +Syntax:: + + rmvirtualenv ENVNAME + +You must use :ref:`command-deactivate` before removing the current +environment. + +:: + + (mynewenv)$ deactivate + $ rmvirtualenv mynewenv + $ workon + $ + +.. seealso:: + + * :ref:`scripts-prermvirtualenv` + * :ref:`scripts-postrmvirtualenv` + +.. _command-cpvirtualenv: + +cpvirtualenv +------------ + +Duplicate an existing virtualenv environment. The source can be an +environment managed by virtualenvwrapper or an external environment +created elsewhere. + +.. warning:: + + Copying virtual environments is not well supported. Each virtualenv + has path information hard-coded into it, and there may be cases + where the copy code does not know it needs to update a particular + file. **Use with caution.** + +Syntax:: + + cpvirtualenv ENVNAME [TARGETENVNAME] + +.. note:: + + Target environment name is required for WORKON_HOME + duplications. However, target environment name can be ommited for + importing external environments. If omitted, the new environment is + given the same name as the original. + +:: + + $ workon + $ mkvirtualenv source + New python executable in source/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + (source)$ cpvirtualenv source dest + Making script /Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/easy_install relative + Making script /Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/easy_install-2.6 relative + Making script /Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/pip relative + Script /Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/postactivate cannot be made relative (it's not a normal script that starts with #!/Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/python) + Script /Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/postdeactivate cannot be made relative (it's not a normal script that starts with #!/Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/python) + Script /Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/preactivate cannot be made relative (it's not a normal script that starts with #!/Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/python) + Script /Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/predeactivate cannot be made relative (it's not a normal script that starts with #!/Users/dhellmann/Devel/virtualenvwrapper/tmp/dest/bin/python) + (dest)$ workon + dest + source + (dest)$ + +.. seealso:: + + * :ref:`scripts-precpvirtualenv` + * :ref:`scripts-postcpvirtualenv` + * :ref:`scripts-premkvirtualenv` + * :ref:`scripts-postmkvirtualenv` + +.. _command-allvirtualenv: + +allvirtualenv +------------- + +Run a command in all virtualenvs under WORKON_HOME. + +Syntax:: + + allvirtualenv command with arguments + +Each virtualenv is activated, bypassing activation hooks, the current +working directory is changed to the current virtualenv, and then the +command is run. Commands cannot modify the current shell state, but +can modify the virtualenv. + +:: + + $ allvirtualenv pip install -U pip + + +================================== +Controlling the Active Environment +================================== + +.. _command-workon: + +workon +------ + +List or change working virtual environments + +Syntax:: + + workon [(-c|--cd)|(-n|--no-cd)] [environment_name|"."] + +If no ``environment_name`` is given the list of available environments +is printed to stdout. + +If ``-c`` or ``--cd`` is specified the working directory is changed to +the project directory during the post-activate phase, regardless of +the value of ``VIRTUALENVWRAPPER_WORKON_CD``. + +If ``-n`` or ``--no-cd`` is specified the working directory is **not** +changed to the project directory during the post-activate phase, +regardless of the value of ``VIRTUALENVWRAPPER_WORKON_CD``. + +If ``"."`` is passed as the environment name, the name is derived from +the base name of the current working directory (contributed by Matias +Saguir). + +:: + + $ workon + $ mkvirtualenv env1 + New python executable in env1/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + (env1)$ mkvirtualenv env2 + New python executable in env2/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + (env2)$ workon + env1 + env2 + (env2)$ workon env1 + (env1)$ echo $VIRTUAL_ENV + /Users/dhellmann/Devel/virtualenvwrapper/tmp/env1 + (env1)$ workon env2 + (env2)$ echo $VIRTUAL_ENV + /Users/dhellmann/Devel/virtualenvwrapper/tmp/env2 + (env2)$ + + +.. seealso:: + + * :ref:`scripts-predeactivate` + * :ref:`scripts-postdeactivate` + * :ref:`scripts-preactivate` + * :ref:`scripts-postactivate` + * :ref:`variable-VIRTUALENVWRAPPER_WORKON_CD` + +.. _command-deactivate: + +deactivate +---------- + +Switch from a virtual environment to the system-installed version of +Python. + +Syntax:: + + deactivate + +.. note:: + + This command is actually part of virtualenv, but is wrapped to + provide before and after hooks, just as workon does for activate. + +:: + + $ workon + $ echo $VIRTUAL_ENV + + $ mkvirtualenv env1 + New python executable in env1/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + (env1)$ echo $VIRTUAL_ENV + /Users/dhellmann/Devel/virtualenvwrapper/tmp/env1 + (env1)$ deactivate + $ echo $VIRTUAL_ENV + + $ + +.. seealso:: + + * :ref:`scripts-predeactivate` + * :ref:`scripts-postdeactivate` + +================================== +Quickly Navigating to a virtualenv +================================== + +There are two functions to provide shortcuts to navigate into the +currently-active virtualenv. + +cdvirtualenv +------------ + +Change the current working directory to ``$VIRTUAL_ENV``. + +Syntax:: + + cdvirtualenv [subdir] + +Calling ``cdvirtualenv`` changes the current working directory to the +top of the virtualenv (``$VIRTUAL_ENV``). An optional argument is +appended to the path, allowing navigation directly into a +subdirectory. + +:: + + $ mkvirtualenv env1 + New python executable in env1/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + (env1)$ echo $VIRTUAL_ENV + /Users/dhellmann/Devel/virtualenvwrapper/tmp/env1 + (env1)$ cdvirtualenv + (env1)$ pwd + /Users/dhellmann/Devel/virtualenvwrapper/tmp/env1 + (env1)$ cdvirtualenv bin + (env1)$ pwd + /Users/dhellmann/Devel/virtualenvwrapper/tmp/env1/bin + +cdsitepackages +-------------- + +Change the current working directory to the ``site-packages`` for +``$VIRTUAL_ENV``. + +Syntax:: + + cdsitepackages [subdir] + +Because the exact path to the site-packages directory in the +virtualenv depends on the version of Python, ``cdsitepackages`` is +provided as a shortcut for ``cdvirtualenv +lib/python${pyvers}/site-packages``. An optional argument is also +allowed, to specify a directory hierarchy within the ``site-packages`` +directory to change into. + +:: + + $ mkvirtualenv env1 + New python executable in env1/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + (env1)$ echo $VIRTUAL_ENV + /Users/dhellmann/Devel/virtualenvwrapper/tmp/env1 + (env1)$ cdsitepackages PyMOTW/bisect/ + (env1)$ pwd + /Users/dhellmann/Devel/virtualenvwrapper/tmp/env1/lib/python2.6/site-packages/PyMOTW/bisect + +lssitepackages +-------------- + +Calling ``lssitepackages`` shows the content of the ``site-packages`` +directory of the currently-active virtualenv. + +Syntax:: + + lssitepackages + +:: + + $ mkvirtualenv env1 + New python executable in env1/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + (env1)$ $ workon env1 + (env1)$ lssitepackages + setuptools-0.6.10-py2.6.egg pip-0.6.3-py2.6.egg + easy-install.pth setuptools.pth + +=============== +Path Management +=============== + +.. _command-add2virtualenv: + +add2virtualenv +-------------- + +Adds the specified directories to the Python path for the +currently-active virtualenv. + +Syntax:: + + add2virtualenv directory1 directory2 ... + +Sometimes it is desirable to share installed packages that are not in +the system ``site-packages`` directory and which should not be +installed in each virtualenv. One possible solution is to symlink the +source into the environment ``site-packages`` directory, but it is +also easy to add extra directories to the PYTHONPATH by including them +in a ``.pth`` file inside ``site-packages`` using ``add2virtualenv``. + +1. Check out the source for a big project, such as Django. +2. Run: ``add2virtualenv path_to_source``. +3. Run: ``add2virtualenv``. +4. A usage message and list of current "extra" paths is printed. +5. Use option ``-d`` to remove the added path. + +The directory names are added to a path file named +``_virtualenv_path_extensions.pth`` inside the site-packages directory +for the environment. + +*Based on a contribution from James Bennett and Jannis Leidel.* + +============================ +Project Directory Management +============================ + +.. seealso:: + + :ref:`project-management` + +.. _command-mkproject: + +mkproject +--------- + +Create a new virtualenv in the WORKON_HOME and project directory in +PROJECT_HOME. + +Syntax:: + + mkproject [-f|--force] [-t template] [virtualenv_options] ENVNAME + +-f, --force Create the virtualenv even if the project directory + already exists + +The template option may be repeated to have several templates used to +create a new project. The templates are applied in the order named on +the command line. All other options are passed to ``mkvirtualenv`` to +create a virtual environment with the same name as the project. + +:: + + $ mkproject myproj + New python executable in myproj/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + Creating /Users/dhellmann/Devel/myproj + (myproj)$ pwd + /Users/dhellmann/Devel/myproj + (myproj)$ echo $VIRTUAL_ENV + /Users/dhellmann/Envs/myproj + (myproj)$ + +.. seealso:: + + * :ref:`scripts-premkproject` + * :ref:`scripts-postmkproject` + +.. _command-setvirtualenvproject: + +setvirtualenvproject +-------------------- + +Bind an existing virtualenv to an existing project. + +Syntax:: + + setvirtualenvproject [virtualenv_path project_path] + +The arguments to ``setvirtualenvproject`` are the full paths to the +virtualenv and project directory. An association is made so that when +``workon`` activates the virtualenv the project is also activated. + +:: + + $ mkproject myproj + New python executable in myproj/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + Creating /Users/dhellmann/Devel/myproj + (myproj)$ mkvirtualenv myproj_new_libs + New python executable in myproj/bin/python + Installing setuptools............................................. + .................................................................. + .................................................................. + done. + Creating /Users/dhellmann/Devel/myproj + (myproj_new_libs)$ setvirtualenvproject $VIRTUAL_ENV $(pwd) + +When no arguments are given, the current virtualenv and current +directory are assumed. + +Any number of virtualenvs can refer to the same project directory, +making it easy to switch between versions of Python or other +dependencies for testing. + +.. _command-cdproject: + +cdproject +--------- + +Change the current working directory to the one specified as the +project directory for the active virtualenv. + +Syntax:: + + cdproject + +=========================== +Managing Installed Packages +=========================== + +.. _command-wipeenv: + +wipeenv +------- + +Remove all of the installed third-party packages in the current +virtualenv. + +Syntax:: + + wipeenv + + +============== +Other Commands +============== + +.. _command-virtualenvwrapper: + +virtualenvwrapper +----------------- + +Print a list of commands, their descriptions, and some details about +the version and locations used by virtualenvwrapper as basic help +output. + +Syntax:: + + virtualenvwrapper diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..6d46571 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +# +# virtualenvwrapper documentation build configuration file, created by +# sphinx-quickstart on Thu May 28 22:35:13 2009. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import datetime + +import virtualenvwrapper.version + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.append(os.path.abspath('.')) + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +# extensions = ['sphinxcontrib.bitbucket'] + +bitbucket_project_url = 'https://bitbucket.org/virtualenvwrapper/virtualenvw' \ + 'rapper/' + +# Add any paths that contain templates here, relative to this directory. +# templates_path = ['pkg/templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'virtualenvwrapper' +copyright = u'2009-%s, Doug Hellmann' % datetime.datetime.today().year + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = virtualenvwrapper.version.version +# The full version, including alpha/beta/rc tags. +release = version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +# unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +html_use_modindex = False + +# If false, no index is generated. +html_use_index = False + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'virtualenvwrapperdoc' + + +# -- Options for LaTeX output ------------------------------------------------ + +# The paper size ('letter' or 'a4'). +# latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +# latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto/manual]). +latex_documents = [ + ('index', 'virtualenvwrapper.tex', u'virtualenvwrapper Documentation', + u'Doug Hellmann', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +# latex_preamble = '' + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_use_modindex = True diff --git a/docs/source/design.rst b/docs/source/design.rst new file mode 100644 index 0000000..c77e704 --- /dev/null +++ b/docs/source/design.rst @@ -0,0 +1,176 @@ +========================================================= + Why virtualenvwrapper is (Mostly) Not Written In Python +========================================================= + +If you look at the source code for virtualenvwrapper you will see that +most of the interesting parts are implemented as shell functions in +``virtualenvwrapper.sh``. The hook loader is a Python app, but doesn't +do much to manage the virtualenvs. Some of the most frequently asked +questions about virtualenvwrapper are "Why didn't you write this as a +set of Python programs?" or "Have you thought about rewriting it in +Python?" For a long time these questions baffled me, because it was +always obvious to me that it had to be implemented as it is. But they +come up frequently enough that I feel the need to explain. + +tl;dr: POSIX Made Me Do It +========================== + +The choice of implementation language for virtualenvwrapper was made +for pragmatic, rather than philosophical, reasons. The wrapper +commands need to modify the state and environment of the user's +*current shell process*, and the only way to do that is to have the +commands run *inside that shell.* That resulted in me writing +virtualenvwrapper as a set of shell functions, rather than separate +shell scripts or even Python programs. + +Where Do POSIX Processes Come From? +=================================== + +New POSIX processes are created when an existing process invokes the +``fork()`` system call. The invoking process becomes the "parent" of +the new "child" process, and the child is a full clone of the +parent. The *semantic* result of ``fork()`` is that an entire new copy +of the parent process is created. In practice, optimizations are +normally made to avoid copying more memory than is absolutely +necessary (frequently via a copy-on-write system). But for the +purposes of this explanation it is sufficient to think of the child as +a full replica of the parent. + +The important parts of the parent process that are copied include +dynamic memory (the stack and heap), static stuff (the program code), +resources like open file descriptors, and the *environment variables* +exported from the parent process. Inheriting environment variables is +a fundamental aspect of the way POSIX programs pass state and +configuration information to one another. A parent can establish a +series of ``name=value`` pairs, which are then given to the child +process. The child can access them through functions like +``getenv()``, ``setenv()`` (and in Python through ``os.environ``). + +The choice of the term *inherit* to describe the way the variables and +their contents are passed from parent to child is +significant. Although a child can change its own environment, it +cannot directly change the environment settings of its parent +because there is no system call to modify the parental environment +settings. + +How the Shell Runs a Program +============================ + +When a shell receives a command to be executed, either interactively +or by parsing a script file, and determines that the command is +implemented in a separate program file, it uses ``fork()`` to create a +new process and then inside that process it uses one of the ``exec`` +functions to start the specified program. The language that program is +written in doesn't make any difference in the decision about whether +or not to ``fork()``, so even if the "program" is a shell script +written in the language understood by the current shell, a new process +is created. + +On the other hand, if the shell decides that the command is a +*function*, then it looks at the definition and invokes it +directly. Shell functions are made up of other commands, some of which +may result in child processes being created, but the function itself +runs in the original shell process and can therefore modify its state, +for example by changing the working directory or the values of +variables. + +It is possible to force the shell to run a script directly, and not in +a child process, by *sourcing* it. The ``source`` command causes the +shell to read the file and interpret it in the current process. Again, +as with functions, the contents of the file may cause child processes +to be spawned, but there is not a second shell process interpreting +the series of commands. + +What Does This Mean for virtualenvwrapper? +========================================== + +The original and most important features of virtualenvwrapper are +automatically activating a virtualenv when it is created by +``mkvirtualenv`` and using ``workon`` to deactivate one environment +and activate another. Making these features work drove the +implementation decisions for the other parts of virtualenvwrapper, +too. + +Environments are activated interactively by sourcing ``bin/activate`` +inside the virtualenv. The ``activate`` script does a few things, but +the important parts are setting the ``VIRTUAL_ENV`` variable and +modifying the shell's search path through the ``PATH`` variable to put +the ``bin`` directory for the environment on the front of the +path. Changing the path means that the programs installed in the +environment, especially the python interpreter there, are found before +other programs with the same name. + +Simply running ``bin/activate``, without using ``source`` doesn't work +because it sets up the environment of the *child* process, without +affecting the parent. In order to source the activate script in the +interactive shell, both ``mkvirtualenv`` and ``workon`` also need to +be run in that shell process. + +Why Choose One When You Can Have Both? +====================================== + +The hook loader is one part of virtualenvwrapper that *is* written in +Python. Why? Again, because it was easier. Hooks are discovered using +setuptools entry points, because after an entry point is installed the +user doesn't have to take any other action to allow the loader to +discover and use it. It's easy to imagine writing a hook to create new +files on the filesystem (by installing a package, instantiating a +template, etc.). + +How, then, do hooks running in a separate process (the Python +interpreter) modify the shell environment to set variables or change +the working directory? They cheat, of course. + +Each hook point defined by virtualenvwrapper actually represents two +hooks. First, the hooks meant to be run in Python are executed. Then +the "source" hooks are run, and they *print out* a series of shell +commands. All of those commands are collected, saved to a temporary +file, and then the shell is told to source the file. + +Starting up the hook loader turns out to be way more expensive than +most of the other actions virtualenvwrapper takes, though, so I am +considering making its use optional. Most users customize the hooks by +using shell scripts (either globally or in the virtualenv). Finding +and running those can be handled by the shell quite easily. + +Implications for Cross-Shell Compatibility +========================================== + +Other than requests for a full-Python implementation, the other most +common request is to support additional shells. fish_ comes up a lot, +as do various Windows-only shells. The officially +:ref:`supported-shells` all have a common enough syntax that the same +implementation works for each. Supporting other shells would require +rewriting much, if not all, of the logic using an alternate syntax -- +those other shells are basically different programming languages. So +far I have dealt with the ports by encouraging other developers to +handle them, and then trying to link to and otherwise promote the +results. + +.. _fish: https://fishshell.com/ + +Not As Bad As It Seems +====================== + +Although there are some special challenges created by the the +requirement that the commands run in a user's interactive shell (see +the many bugs reported by users who alias common commands like ``rm`` +and ``cd``), using the shell as a programming language holds up quite +well. The shells are designed to make finding and executing other +programs easy, and especially to make it easy to combine a series of +smaller programs to perform more complicated operations. As that's +what virtualenvwrapper is doing, it's a natural fit. + +.. seealso:: + + * *Advanced Programming in the UNIX Environment* by W. Richard + Stevens & Stephen A. Rago + * `Fork (operating system)`_ on Wikipedia + * `Environment variable`_ on Wikipedia + * `Linux implementation of fork()`_ + +.. _Fork (operating system): https://en.wikipedia.org/wiki/Fork_(operating_system) + +.. _Environment variable: https://en.wikipedia.org/wiki/Environment_variable + +.. _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 diff --git a/docs/source/developers.rst b/docs/source/developers.rst new file mode 100644 index 0000000..1c6c7d7 --- /dev/null +++ b/docs/source/developers.rst @@ -0,0 +1,128 @@ +############## +For Developers +############## + +If you would like to contribute to virtualenvwrapper directly, these +instructions should help you get started. Patches, bug reports, and +feature requests are all welcome through the `GitHub site +`_. Contributions +in the form of patches or pull requests are easier to integrate and +will receive priority attention. + +.. note:: + + Before contributing new features to virtualenvwrapper core, please + consider whether they should be implemented as an extension instead. + +Building Documentation +====================== + +The documentation for virtualenvwrapper is written in reStructuredText +and converted to HTML using Sphinx. The build itself is driven by +make. You will need the following packages in order to build the +docs: + +- Sphinx +- docutils +- sphinxcontrib-bitbucket + +Once all of the tools are installed into a virtualenv using +pip, run ``make html`` to generate the HTML version of the +documentation:: + + $ make html + rm -rf virtualenvwrapper/docs + (cd docs && make html SPHINXOPTS="-c sphinx/pkg") + sphinx-build -b html -d build/doctrees -c sphinx/pkg source build/html + Running Sphinx v0.6.4 + loading pickled environment... done + building [html]: targets for 2 source files that are out of date + updating environment: 0 added, 2 changed, 0 removed + reading sources... [ 50%] command_ref + reading sources... [100%] developers + + looking for now-outdated files... none found + pickling environment... done + checking consistency... done + preparing documents... done + writing output... [ 33%] command_ref + writing output... [ 66%] developers + writing output... [100%] index + + writing additional files... search + copying static files... WARNING: static directory '/Users/dhellmann/Devel/virtualenvwrapper/plugins/docs/sphinx/pkg/static' does not exist + done + dumping search index... done + dumping object inventory... done + build succeeded, 1 warning. + + Build finished. The HTML pages are in build/html. + cp -r docs/build/html virtualenvwrapper/docs + +The output version of the documentation ends up in +``./virtualenvwrapper/docs`` inside your sandbox. + +Running Tests +============= + +The test suite for virtualenvwrapper uses shunit2_ and tox_. The +shunit2 source is included in the ``tests`` directory, but tox must be +installed separately (``pip install tox``). + +To run the tests under bash and zsh for the default Python, +run ``tox`` from the top level directory of the hg repository:: + + $ tox + +To run individual test scripts, use a command like:: + + $ tox -- tests/test_cd.sh + +To run tests under a single version of Python, specify the appropriate +environment when running tox:: + + $ tox -e py311 + +Combine the two modes to run specific tests with a single version of +Python:: + + $ tox -e py311 -- tests/test_cd.sh + +To stop the test suite as soon as any test fails, use the `fast` tox +target:: + + $ tox -e fast + +Add new tests by modifying an existing file or creating new script in +the ``tests`` directory. + +.. _shunit2: https://github.com/kward/shunit2 + +.. _tox: https://tox.wiki/ + +.. _developer-templates: + +Creating a New Template +======================= + +virtualenvwrapper.project templates work like `virtualenvwrapper +plugins +`__. +The *entry point* group name is +``virtualenvwrapper.project.template``. Configure your entry point to +refer to a function that will **run** (source hooks are not supported +for templates). + +The argument to the template function is the name of the project being +created. The current working directory is the directory created to +hold the project files (``$PROJECT_HOME/$envname``). + +Help Text +--------- + +One difference between project templates and other virtualenvwrapper +extensions is that only the templates specified by the user are run. +The ``mkproject`` command has a help option to give the user a list of +the available templates. The names are taken from the registered +entry point names, and the descriptions are taken from the docstrings +for the template functions. diff --git a/docs/source/extensions.rst b/docs/source/extensions.rst new file mode 100644 index 0000000..8519d24 --- /dev/null +++ b/docs/source/extensions.rst @@ -0,0 +1,78 @@ +===================== + Existing Extensions +===================== + +Below is a list of some of the extensions available for use with +virtualenvwrapper. + +emacs-desktop +============= + +Emacs desktop-mode_ lets you save the state of emacs (open buffers, +kill rings, buffer positions, etc.) between sessions. It can also be +used as a project file similar to other IDEs. The emacs-desktop_ +plugin adds a trigger to save the current desktop file and load a new +one when activating a new virtualenv using ``workon``. + +.. _desktop-mode: https://www.emacswiki.org/emacs/DeskTop + +.. _emacs-desktop: https://pypi.python.org/pypi/virtualenvwrapper-emacs-desktop + +.. _extensions-user_scripts: + +user_scripts +============ + +The ``user_scripts`` extension is delivered with virtualenvwrapper and +enabled by default. It implements the user customization script +features described in :ref:`scripts`. + +vim-virtualenv +============== + +`vim-virtualenv`_ is Jeremey Cantrell's plugin for controlling +virtualenvs from within vim. When used together with +virtualenvwrapper, vim-virtualenv identifies the virtualenv to +activate based on the name of the file being edited. + +.. _vim-virtualenv: https://github.com/jmcantrell/vim-virtualenv + +.. _extensions-templates: + +Templates +========= + +Below is a list of some of the templates available for use with +:ref:`command-mkproject`. + +.. _templates-bitbucket: + +bitbucket +--------- + +The bitbucket_ extension automatically clones a mercurial repository +from the specified Bitbucket project. + +.. _bitbucket: https://pypi.python.org/pypi/virtualenvwrapper.bitbucket + +.. _templates-django: + +django +------ + +The django_ extension automatically creates a new Django project. + +.. _django: https://pypi.python.org/pypi/virtualenvwrapper.django + +SublimeText +----------- + +Song Jin has created a template plugin for automatically generating +the project files used by SublimeText. See the +sublime_projectfile_maker_ page for details. + +.. _sublime_projectfile_maker: https://github.com/SongGithub/sublime_projectfile_maker + +.. seealso:: + + * :ref:`developer-templates` diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst new file mode 100644 index 0000000..677c8f0 --- /dev/null +++ b/docs/source/glossary.rst @@ -0,0 +1,16 @@ +========== + Glossary +========== + +.. glossary:: + + project directory + + Directory associated with a virtualenv, usually located elsewhere + and containing more permanent development artifacts such as local + source files, test data, etc. (see :ref:`variable-PROJECT_HOME`) + + template + + Input to :ref:`command-mkproject` that configures the *project + directory* to contain default files. (see :ref:`plugins`) diff --git a/docs/source/history.rst b/docs/source/history.rst new file mode 100644 index 0000000..df6b5cf --- /dev/null +++ b/docs/source/history.rst @@ -0,0 +1,1315 @@ +CHANGES +======= + +6.1.0 +----- + +* Look for `./.virtualenvwrapper/postactivate` and + `./.virtualenvwrapper/predeactivate` hook scripts. Same effect as + :ref:`scripts-postactivate` and :ref:`scripts-predeactivate`. + +6.0.0.0a5 +--------- + +* add a --version option to the hook loader +* modernize packaging +* docs: update requirements for build on rtd +* docs: add read the docs configuration file +* Changed workon's env switching to use OR not $? by @Nealium (pull request #59) +* Fix issues for python 3.12 by @parona-source (pull request #68) +* Fix shell completion on FreeBSD by @bendikro (pull request #86) +* add python 3.12 to test matrix +* Improve error message for IOErrors by @kerel-fs (pull request #81) + +6.0.0.0a1 +--------- + +* update pypi publishing action +* switch to implicit namespaces +* add github action for publishing packages +* Updated tested Python versions in README +* docs: fix or remove broken links +* packaging: fix indentation of trove classifier for audience +* docs: remove broken link from tips list +* tox: update doc build commands +* mergify: add rules to label PRs based on pbr sem-ver data +* remove python2 from startup logic for finding the python interpreter +* drop ksh support +* docs: clean up trailing whitespace +* docs: update language that implies there is only 1 maintainer +* Fixing bitbucket in projects +* Updating template docs +* missed ones +* Updating references to bitbucket +* require at least one reviewer to approve PRs +* ci(Mergify): configuration update +* pass user and home through from tox instead of using id to derive them +* fix wipeenv for editable packages +* skip some tempfile tests on macos +* adjust error message detection in hook tests +* update lssitepackages tests to not need easy\_install +* remove tests and features relying on --no-site-packages +* remove the test for making virtualenvs relocatable +* update tox config to not set basepython for zsh +* update tox to only use default python version +* update test runner to use python3 +* add a "fast" environment in tox to exit as soon as any test fails +* missed ones +* run all of the tests and accumulate errors +* expand the relative path for envdir to the full path +* Updating references to bitbucket +* update trove classifiers with more modern python versions +* add pkglint test and fix some warnings +* set python version for zsh job in ci +* update tox config for tox 4 +* do not specify python version for docs env in tox settings +* set the version of python to use for linter jobs +* fix linter action config +* add github action configuration for test jobs +* Merged in fix/space\_in\_mkvenv\_project\_path (pull request #50) +* Merged in Stephan-Sokolow/improve-zsh-prompt-tip-closes-332-1574470574700 (pull request #75) +* Merged in master (pull request #72) +* Merged in no-more-egrep-its-deprecated (pull request #84) +* replace deprecated \`egrep\` with \`grep -E\` +* Revert "Merged in 334 (pull request #78)" +* Fixing readme.txt +* Merged in 334 (pull request #78) +* Updating to support virtualenv 20+ +* Merged in readme-updates (pull request #76) +* Merged in fix-screencast-link (pull request #77) +* fix link to screencast +* improve some of the wording in the readme +* Improve Zsh prompt tip +* Merged in Shailesh-Vashishth/indexrst-edited-online-with-bitbucket-1566725355529 (pull request #74) +* index.rst edited online with Bitbucket +* Merged in master (pull request #73) +* fixup! Find the highest Python version with installed virtualenvwrapper +* Find the highest Python version with installed virtualenvwrapper + +4.8.4 +----- + +* Formatting change + +4.8.3 +----- + +* Upgrade sphinx, fix docs +* Merged in ukch/virtualenvwrapper/ukch/allow-building-docs-on-python-3-1529536003674 (pull request #71) +* Merged in techtonik/virtualenvwrapper/techtonik/toxini-edited-online-with-bitbucket-1525341850929 (pull request #69) +* Make mkvirtualenv work with interpreters whose paths have spaces +* Allow building docs on Python 3 +* Merged in hjkatz/virtualenvwrapper/fix/workon\_deactivate\_and\_tests (pull request #70) +* Fix bug with workon deactivate typeset -f ; Add test\_workon\_deactivate\_hooks +* Merged in JakobGM/virtualenvwrapper-1/JakobGM/use-code-blocks-in-order-to-allow-easier-1508879869188 (pull request #66) +* Use code blocks in order to allow easier copy-pasting +* Merged in JakobGM/virtualenvwrapper/JakobGM/fix-formatting-error-on-read-the-docs-t-1508876093482 (pull request #65) +* Fix formatting error + +4.8.2 +----- + +* Merged in jeffwidman/virtualenvwrapper-2/jeffwidman/update-rtd-url-they-now-use-io-rather-t-1505539237232 (pull request #63) +* Merged in jeffwidman/virtualenvwrapper-1/jeffwidman/add-python-36-to-pypi-trove-classifiers-1505539102243 (pull request #62) +* Merged in jeffwidman/virtualenvwrapper/jeffwidman/update-readme-with-current-test-status--1505538852189 (pull request #61) +* Update RTD url +* Add python 3.6 to Pypi trove classifiers +* Update readme with current test status + +4.8.1 +----- + +* New PBR doesn't like provides\_dist + +4.8.0 +----- + +* Merged in fix/263 (pull request #60) +* Merged in fix/296 (pull request #59) +* Fixing Documentation +* Update supported versions +* Adding python 3.6 +* Fixing run\_hook and tab\_completion +* First shot at Fixing #263 +* Adding a note about package managers +* Merged in zmwangx/virtualenvwrapper/always-export-virtualenvwrapper\_hook\_dir (pull request #55) +* Typo fix +* Merged in lendenmc/virtualenvwrapper (pull request #51) +* Merged in SpotlightKid/virtualenvwrapper/bugfix/distutils-sysconfig (pull request #56) +* Merged in dougharris/virtualenvwrapper (pull request #53) +* Merged in kk6/virtualenvwrapper/fix/wipeenv\_ignore\_setuptools\_dependencies (pull request #57) +* Merged in erickmk/virtualenvwrapper/erickmk/command\_refrst-edited-online-with-bitbuc-1491225971803 (pull request #58) +* Update sentence to make it more clear +* command\_ref.rst edited online with Bitbucket +* Fixes Issue #291 wipeenv ignore setuptools’s dependencies +* Import distutils.sysconfig properly (fixes #167) +* virtualenvwrapper.sh: always export VIRTUALENVWRAPPER\_HOOK\_DIR +* Fixed case where alternate deactivate didn't exist +* Makes workon more selective in its search for \`deactivate\` #285 +* Merged in sambrightman/virtualenvwrapper (pull request #52) +* Fix spelling mistake in error message +* Fix .kshrc sourcing error "'&>file' is nonstandard" +* Fixes Issues #248 +* Merged in lonetwin/virtualenvwrapper (pull request #48) + +4.7.2 +----- + +* Baseline testing to python27 +* Fixing naming in tests +* Merged in phd/virtualenvwrapper (pull request #46) +* Ignore \*.pyo byte-code files +* Fix docs: fix URLs whenever possible, change protocol to https +* Add wipeenv and allvirtualenv for lazy loading +* Remove one-time functions from the environment +* Fix the problem with lazy completion for bash +* Last set of docs +* Docs fixes +* Updating to virtualenvwrapper +* Last set of docs +* Docs fixes +* Merged in fix/issue-282-link-to-virtualenvwrapper (pull request #49) +* Updating to virtualenvwrapper +* Unset previously defined cd function rather than redefine it +* Merged in ismailsunni/virtualenvwrapper/ismailsunni/command\_refrst-edited-online-with-bitbuc-1454377958615 (pull request #44) +* command\_ref.rst edited online with Bitbucket Adding -d for remove extra path +* use a ref instead of hard-coded link in new tip +* Merged in kojiromike/virtualenvwrapper/deactivate-on-logout-tip (pull request #43) +* Add Deactivate-on-Logout Tip +* update REAMDE with new bug tracker URL +* more dir fixes for El Capitan +* add testing for python 3.5 +* temporary dir fixes for OS X El Capitan (10.11) +* update to work with tox 2.1.1 +* Merged in jveatch/virtualenvwrapper/fix-py26-logging (pull request #41) +* Pass stream as arg rather than kwarg to avoid py26 conflict. Fixes issue #274. StreamHandler arg was named strm in python 2.6 +* enhance verbose output of hook loader +* Merged in erilem/virtualenvwrapper/user-scheme-installation (pull request #38) +* Change install docs to use --user + +4.7.0 +----- + +* Merged in gnawybol/virtualenvwrapper/support\_MINGW64 (pull request #36) +* Detect MSYS if MSYSTEM is MINGW64 +* Merged in kdeldycke/virtualenvwrapper/kdeldycke/restore-overridden-cd-command-to-its-def-1435073839852 (pull request #34) +* Restore overridden cd command to its default builtin behaviour + +4.6.0 +----- + +* remove some explicit tox environments +* Merged in jessamynsmith/virtualenvwrapper/py34 (pull request #30) +* quiet some of the lsvirtualenv tests +* add test for previous patch +* Merged in robsonpeixoto/virtualenvwrapper/bug/265 (pull request #33) +* Removes empty when list all virtualenvs +* Merged in justinabrahms/virtualenvwrapper/justinabrahms/update-links-and-name-for-venv-post-1431982402822 (pull request #32) +* Update links and name for venv post +* Added testing and updated docs for python 3.4 +* Merged in jessamynsmith/virtualenvwrapper/env\_with\_space (pull request #28) +* Changes as per code review +* Added tests to verify that cpvirtualenv, lsvirtualenv, and mkproject work with spaces in env names +* Made rmvirtualenv work with spaces +* Added tests for leading spaces (trailing spaces don't work in Linux, so don't test them) +* Made lsvirtualenv and allvirtualenv work with spaces in env names +* Made cd command work with space in virtualenv name +* Fixed ordering in asserts for workon tests +* Made workon fully support virtualenvs with spaces in names +* fix default for VIRTUALENVWRAPPER\_WORKON\_CD + +4.5.0 +----- + +* Add -c/-n options to mktmpenv +* update mktmpenv test to assert changed directory +* Add test for creating venv with space in name + +4.4.1 +----- + +* Touch temporary file after a name is created +* document 'workon .' and give attribution +* Support "workon ." +* fix pep8 error +* make cd after workon optional +* fix sphinx build +* Merged in hjwp/virtualenvwrapper (pull request #25) +* Stop mangling the python argument to virtualenv +* ignore -f lines in pip freeze output +* Merged in bittner/virtualenvwrapper (pull request #22) +* hacked attempt to get round MSYS\_HOME environ dependency on windows/git-bash/msys +* Change "distribute" to "setuptools" in docs +* Merged in jessamynsmith/virtualenvwrapper (pull request #23) +* Override tox's desire to install pre-releases +* Reworded the documentation around user scripts vs plugin creation, to make it more clear which one you need. Also added a simple example of user scripts +* do not install distribute in test environments +* Correct spelling of "Bitbucket" +* Update issue tracker URL + +4.3.2 +----- + +* build universal wheels +* Merged in das\_g/virtualenvwrapper/das\_g/removed-gratuitous-preposition-1413208408920 (pull request #19) +* removed gratuitous preposition +* Fix test invocation for zsh +* add -q option to cd for zsh +* make run\_tests use the SHELL var to run test script + +4.3.1 +----- + +* pep8 and test updates for previous commit +* Make postmkproject use VIRTUALENVWRAPPER\_HOOK\_DIR +* Tell tox it is ok to run shells not installed in the virtualenv +* Set VIRTUALENVWRAPPER\_SCRIPT correctly for different shells +* Merged in nishikar/virtualenvwrapper (pull request #14) +* changed phrasing of environment not found message +* Add tests for wipenv with editable packages +* Remove obsolete information about pip environment vars +* Replace manually maintained history with ChangeLog +* Update doc build to fail if there are warnings + +4.3 +--- + +* remove announce.rst; move to blogging repository +* Merged in erikb85/virtualenvwrapper/erikb85/run-user-postactivate-after-changing-dir-1401272364804 (pull request #15) +* Run User Postactivate after changing dirs +* add link to sublimetext extension +* moved environment exists check below active environment check +* added no such environment prompt to rmvirtualenv if it does not exist +* updated pep8 +* clean up script mode changes +* forgotten comment +* trailing whitespace removed +* tabs expanded; mode difference +* changed comments and mode for sourced scripts +* ignore bin, include, lib +* mode constant for sourced-only files +* do not specify a version for pbr +* Merged in mjbrooks/virtualenvwrapper (pull request #12) +* use VIRTUALENVWRAPPER\_ENV\_BIN\_DIR throughout +* Extract basic help text from the script +* Add list of commands as basic help output +* update author email +* clean up comment about zsh behavior in lazy +* Fix syntax error (empty \`if\` block) + +4.2 +--- + +* update docs for 4.2 release +* update history for previous change; fix syntax issue in previous change +* update history +* Do not create hooks for rmproject +* make setvirtualenvproject honor relative paths +* Ensure hook directory exists +* fix indentation in virtualenvwrapper\_lazy.sh +* use valid syntax for creating tmpdir under linux +* stop python 3.2 tests +* stop using distribute for packaging the test templates +* fix merge issue from previous commit +* Fix mkvirtualenv -a relative paths +* minor: tabs to spaces +* Fix zsh crash caused by lazily loading the completions +* Fix hint in error message, when virtualenvwrapper\_run\_hook failed +* changed spelling of proj\_name calculation +* Fix \`which\` with virtualenvwrapper\_lazy.sh +* use virtualenvwrapper\_cd in project plugin +* document new force option in history +* Merged in claymcclure/virtualenvwrapper (pull request #2) +* update history for doc fix from dirn +* Merged in dirn/virtualenvwrapper/dirn/fix-documentation-for-allvirtualenv-the-1375587964876 (pull request #4) +* update history for cd command fix +* consolidate 'ls' tests +* update test to handle change easy\_install +* ignore any egg directories created while packaging +* add tests to make sure we override cd properly +* Merged in isbadawi/virtualenvwrapper (pull request #5) +* Always use virtualenvwrapper\_cd instead of cd +* Fix documentation for allvirtualenv +* Document \`mkproject --force\` usage +* Mention sphinxcontrib-bitbucket requirement +* Merged in mrdbr/virtualenvwrapper (pull request #3) +* add tmp- prefix to temporary envs +* Preserve quoting for allvirtualenv command arguments +* Add \`mkproject --force\` option +* Remove extraneous punctuation + +4.1.1 +----- + +* update history for 4.1.1 +* Merged in mordred/virtualenvwrapper (pull request #1) +* Take advantage of pbr 0.5.19 +* Working on packaging issue with 4.1 release + +4.1 +--- + +* prep for 4.1 release +* fix pep8 issue in user\_scripts.py +* quiet cdproject test +* one more parallel test issue +* use pbr for packaging +* Allow tests to run in parallel +* Fix virtualenv detection with spaces in WORKON\_HOME +* add license file +* Fix problem lsvirtualenv after previous commit +* Add allvirtualenv command +* Ensure that -p and --python options are consistent +* quiet tests +* add test for mkvirtualenv w/ site-packages +* ignore emacs TAGS file +* Provide a way to extend the lazy-loader +* Add wipeenv command +* Update ignore file +* remove trailing whitespace in tox.ini +* Quote paths +* Skip pushd/popd test under ksh +* Run the cdproject test in a subshell +* Show more details when running under zsh +* add doc explaining implementation choices +* add a warning to cpvirtualenv command docs +* fix rst in announcement file +* fix home page url +* add python 3.3 classifier +* Added tag 4.0 for changeset 2ba65a13f804 + +4.0 +--- + +* Prepare for 4.0 release +* Update Python 3 compatibility +* assume setuptools is available during the installation +* update tested-under version lists +* add attribution for previous fix to the history file +* Correct script name in error message +* reorg test runner to remove redundant test runs +* flake8 fixes for setup.py +* Prep 3.7.1 release +* Make --python option to mkvirtualenv not sticky +* Fix project template listing when none installed +* note change in the history file +* better prefix and fix for other help functions +* prevent workon\_help from polluting the global namespace +* Fixed broken screencast link +* Merged in dasevilla/virtualenvwrapper/link-fix (pull request #33) +* Update link to requirements docs +* Added tag 3.7 for changeset 303ff1485acb + +3.7 +--- + +* update version number +* Apply style to sphinx config file +* add link to flake8 in history +* use flake8 instead of pep8 for style checking +* Turn off logging by default +* Add help option to workon +* Add --help option to mkproject +* merge readme filename change +* Merged in jeffbyrnes/virtualenvwrapper (pull request #32) +* merge Add complete-time load to lazy loader +* Merged in upsuper/virtualenvwrapper (pull request #29) +* fix issue with toggleglobalsitepackages tests that was hidden by old test virtualenv +* show which virtualenv is used in tests +* do not check in test output +* Use $\_VIRTUALENVWRAPPER\_API instead of listing functions +* merge exclusion rules for doc build artifacts +* Added tag 3.6.1 for changeset c180ccae77b4 + +3.6.1 +----- + +* prepare 3.6.1 release +* Rename READMEs to be RST +* Added exclusion for docs/en, docs/es, and docs/ja to .hgignore +* Add complete-time load to lazy loader +* Fix link to setvirtualenvproject command +* merge fix for relative python interpreter option to mkvirtualenv +* Replace realpath with a more portable way of converting a relative path to an absolute path +* Fix typo in documentation +* Fix --python switch for virtualenv +* fix markup typo in announcement +* Added tag 3.6 for changeset 002a0ccdcc7a + +3.6 +--- + +* update version number before release, 2 +* update version number before release +* fix pep8 issues with setup.py +* fix pep8 issues with sphinx conf file +* Fix virtualenvwrapper\_show\_workon\_options under zsh with chpwd +* update history for previous change +* Update documentation to point to the real file where add2virtualenv command adds directories to PYTHONPATH +* update the links to the translated versions of the documentation +* change to the default theme for readthedocs.org +* move es and ja versions of docs to their own repositories +* add attribution to history file for previous patch +* fix issue with add2virtualenv and noclobber setting in shell; fixes #137 +* pep8 cleanup +* fix lazy-loader function definitions under zsh; fixes #144 +* use the right virtualenv binary to get help; fixes #148 +* convert hook loader to use stevedore +* fix reference in announcement +* Added tag 3.5 for changeset c93b81815391 + +3.5 +--- + +* bump version number and update announcement text +* fix whitespace and rename a few worker functions to be consistent with the rest +* document previous changes +* Use "command" to avoid aliases or functions that mask common utilities. fixes #119 +* quiet some test operations and check for error codes before continuing +* allow the caller to control which shells are used for tests; unset variables that might be inherited and give the wrong idea about what the current shell is for a test; export SHELL to point to the current shell +* add test for lazy loading via workon; addresses #144 +* update docs with link to virtualenvwrapper-win port; fixes #140 +* clean up cpvirtualenv documentation +* if cpvirtualenv fails to create the target directory, return an error code +* document cpvirtualenv addition +* merged upstream +* Forgot to uncommit the remove workon\_home in teardown +* update README with supported python versions +* Did not mean to commit isitepackages +* Update cpvirtualenv utilizing virtualenv-clone and allowing for external virutalenvs to be added to WORKON\_HOME +* fix xref endpoint used in install.rst +* Added tag 3.4 for changeset 07905d9135ac + +3.4 +--- + +* bump version +* update announcement +* clarify warning on tab completion +* add lazy loader +* move error reporting for bad python interpreter closer to where the error occurs +* Invoke the initialization hooks directly when testing for error with Python +* hide error messages +* fix section heading in announce blog post so the version number does not appear twice +* update announcement file for 3.3 release +* fix the requirement name +* remove old copy of requirements file +* add requirements file to try readthedocs again +* Added tag 3.3 for changeset 45877370548e + +3.3 +--- + +* prepare 3.3 release +* attribution for previous merge +* Merged in agriffis/virtualenvwrapper (pull request #22) +* clean up RST formatting +* attribution for previous merge +* Merged in barberj/vew/fix\_installing\_requirements\_after\_cd (pull request #21) +* Use spaces for indentation consistently instead of mixed spaces/tabs. No functional changes +* Quoting arguments to expandpath to allow for spaces in the arguments +* Update to get fully qualified path of requirments in case a directory change occurs before pip is called +* Clean up the temporary file in the virtualenvwrapper\_run\_hook error returns +* attribution for previous merge +* Merged in agriffis/virtualenvwrapper (pull request #20) +* Fix error handling in virtualenvwrapper\_tempfile; the typeset builtin will return success even if the command-substitution fails, so put them on separate lines +* catch --help option to mkvirtualenv; fixes #136 +* Remove the trap from virtualenvwrapper\_tempfile; the function is called in a command substitution, so the trap fires immediately to remove the file. There are ways to accomplish this, but they're complex and the caller is already explicitly rm'ing the file +* attribution for merging pull request 17 +* merge in hook listing and pep8 fixes +* pep8 changes +* Merged in bwanamarko/virtualenvwrapper (pull request #17) +* print the list of core hooks if no hook name is given in list mode +* attribution for previous merges +* Check that required test shells are available ahead of running tests. This avoids accidentally running tests with /bin/sh (dash) on Debian, which eventually deletes the ~/.virtualenvs directory. (Whoops.) +* Enforce running run\_tests under tox by setting/checking an env var +* another fix for msys users \* using lssitepackages \* keep $site\_packages in quotes in case of spaces +* fix bug for MSYS users - makes several folders, fails on shell startup \* if $WORKON\_HOME not defined, or folder missing, then when mkdir called must \* pass $WORKON\_HOME in double-quotes "$WORKON\_HOME" because there might be \* spaces that will be interpretted separately \* e.g. C:\Documents and Settings\.virtualenv makes 3 folders: \* "C:\Documents", "~/and" & "~/Settings/.virtualenv" +* update shell function virtualenvwrapper\_get\_site\_packages\_dir \* let MSYS users use lssitepackages & cdsitepackages \* replace $VIRTUAL\_ENV/bin with $VIRTUAL\_ENV/$VIRTUALENVWRAPPER\_ENV\_BIN\_DIR +* attribution for documentation work +* reset the default language +* revised the Japanese translation in plugins.rst +* revised the Japanese translation in index.rst +* merged the changes (r369:550) in extensions.rst +* merged the changes (r369:550) for Japanese translation in projects.rst +* merged the changes (r369:550) for Japanese translation in extensions.rst +* merged the changes (r369:550) for Japanese translation in developers.rst +* merged the changes (r369:550) for Japanese translation in tips.rst +* merged the changes (r369:550) for Japanese translation in scripts.rst +* merged the changes (r369:550) for Japanese translation +* merged the changes (r369:550) for Japanese translation +* changed LANGUAGE settings "en" to "ja" +* merged from original +* add attribution to history file for ralphbean's changes +* merge in permission changes from ralphbean +* Bypass the test for missing virtualenv if the user has it installed to the subset of the path needed for the shunit2 framework to function properly. Add a test for having VIRTUALENVWRAPER\_VIRTUALENV set to a program that does not exist +* Removed shebangs from scripts non-executable site-packages files +* Removed execution bit on virtualenvwrapper.sh +* update announcement blog post for 3.2 +* Added tag 3.2 for changeset dccf1a1abf4e + +3.2 +--- + +* bump version number +* Add a link target name for the rmvritualenv command +* Use distutils to get the site-packages directory. Fixes #112 +* more global test header cleanup +* Centralize setup of variables for tests. Change WORKON\_HOME and PROJECT\_HOME for tests to make them unique across runs, allowing simultaneous test runs in different sandboxes +* update history for previous merge +* Merged in ciberglo/virtualenvwrapper (pull request #13) +* add history details about license classification change +* Merged in ralphbean/virtualenvwrapper (pull request #14) +* attribution for previous commit +* Fix typo in documentation reported by Nick Martin +* Changed trove classifiers from BSD to MIT (like the README indicates.) +* add test for removing several environments +* changing rmvirtualenv message: Erasing --> Removing +* support to remove several environments at once +* remove blank spaces +* use typeset instead of local and provide attribution for the original fix +* Make project\_dir local so it doesn't clobber other variables +* Added tag 3.1 for changeset ebbb3ba81687 + +3.1 +--- + +* prepare release 3.1 +* quote the path as we are editing the pth file; fixes #132 +* update history file for previous change +* associate project before enabling the new virtualenv; fixes #122 +* add tags to announce.rst +* add a couple of debugging lines to the generated scripts +* Added tag 3.0.1 for changeset 14cf7e58d321 + +3.0.1 +----- + +* package release 3.0.1; fixes #126 +* Add test files to the sdist package. Addresses #126 +* Remove /usr/bin since apparently there are times when virtualenv is installed there due to vendor packages. Fixes #127 +* Added tag 3.0 for changeset 434b87ebc24a + +3.0 +--- + +* fix version info in trove classifiers, take 2 +* fix version info in trove classifiers +* use the version of python in the current virtualenv to install the template project into the tox virtualenv during the test +* merge in support for python 3.2 +* bump version number, update history, prepare announcement +* remove redundant test +* use the version of python in the virtualenv instead of depending on the PATH +* use packages available for python 3 +* use packages that can be installed under python 3 to test the -i option to mkvirtualenv +* get the output in a way that makes it work properly with grep +* include virtualenv in the test dependencies +* fix shell expression to get the python version +* fix indentation +* py3k compatibility +* py3k compatibility +* py3k compatibility +* Added tag 2.11.1 for changeset 12a1e0b65313 + +2.11.1 +------ + +* update history and version number for bug release +* Skiping re-initialization in subshells breaks tab completion, so go ahead and take the performance hit. Closes #121 +* quiet some tests +* announcement for 2.11 release +* Added tag 2.11 for changeset ff4d492c873c + +2.11 +---- + +* bump version number for release +* add VIRTUALENVWRAPPER\_PROJECT\_FILENAME; resolves issue 120 +* make log files group writable; resolves #62 +* shortcut initialization if it has run before +* Remove support for Python 2.4 and 2.5. Update tests to work with virtualenv 1.7, where --no-site-packages is now the default +* Add note about -a option to history file and clarify its description in the docs a bit +* documentation for -a flag +* test for 'mkvirtualenv -a ' +* add -a project\_path to mkvirtualenv usage summary +* associate a project with a venv at creation +* fix link to Justin Lily's helper post +* Added tag 2.10.1 for changeset 9e10c201a500 + +2.10.1 +------ + +* bump version to 2.10.1; closes #114 +* improve test for mktmpenv with options; addresses #114 +* change mktmpenv to always create an env name for the user; addresses #114 +* update announcement text +* bump version number +* document previous fix in history +* strip spaces from template names; fixes #111 +* fix template listing for python 2.4, which does not support the -m option with namespace packages +* if uuid is not available, use random to generate a name for the new environment +* Use old style string formatting instead of the format method to retain python 2.4 and 2.5 support +* add test to ensure templates are applied correctly +* get the version number from the packaging scripts +* add mktmpenv command from virtualenvwrapper.tmpenv +* add -i option to mkvirtualenv +* more test quieting +* quiet tests and add intermediate check for delete +* fix use of sed in add2virtualenv to be more portable +* quiet test +* Merged in miracle2k/virtualenvwrapper (pull request #6) +* merge in linux changes +* ignore temporary files created by editor +* variable name changes and other cleanup so the script does not bomb under ksh on ubunutu 11.04 +* run each test script in every shell before moving to the next script +* Make add2virtualenv tests work again, add new test code for new features +* Update lssitepackages to work with new pth filename +* Merged upstream +* add link to changelog in readme +* fix version number in history +* update announcement file +* Upgrade instructions +* Clean up help functions. Add documentation for new -r option to mkvirtualenv +* Add -r option to mkvirtualenv to install base requirements after the environment is created. Fix argument processing in mkproject so the correct template names are preserved +* merge virtualenvwrapper.project features into virtualenvwrapper +* convert function definition format so typeset works under ksh +* Merged upstream +* add link to powershell port +* Added tag 2.8 for changeset 279244c0fa41 + +2.8 +--- + +* set version in history and update announcement +* Added tag 2.8 for changeset 7e0abe005937 +* bump version number +* merge in patches from noirbizarre to add support for MSYS environment; clean up doc addition; fix resulting problem is lsvirtualenv +* Identify another --no-site-packages test and add one for cpvirtualenv using the default args variable; addresses #102 +* add test for --no-site-packages flag after cpvirtualenv; addresses #102 +* Escape uses of cd in case it is aliased. addresses #101 +* add a test to verify pushd/popd behavior; addresses #101 +* Set is\_msys to False when not in MSYS shell +* Avoid declaring the 'command\_exists' function for a one shot use +* Replaced all remaining 'bin' occurences by $VIRTUALENVWRAPPER\_ENV\_BIN\_DIR +* Use VIRTUALENVWRAPPER\_VIRTUALENV in cpvirtualenv. fixes #104 +* Merged in sharat87/virtualenvwrapper (pull request #1) +* Update documentation about mktemp +* VIRTUALENVWRAPPER\_VIRTUALENV\_ARGS not working with >1 args on zsh +* User scripts should be called based on new $VIRTUALENVWRAPPER\_ENV\_BIN\_DIR variable +* add some debugging and a test to try to reproduce problem with log directory variable; addresses #95 +* move tab completion initialization; expand support for tab completion in zsh (fixes #97) +* Added support for getopts with fallback on getopt +* Improved variable name: VIRTUALENVWRAPPER\_ENV\_BIN\_DIR instead of script\_folder and is\_msys instead of msys +* Document MSys installation +* Allow Win32 and Unix paths for MSYS\_HOME variable +* Added msys paths support +* update announce file +* Added tag 2.7.1 for changeset b20cf787d8e1 + +2.7.1 +----- + +* bump version number for bug release +* set log dir and hook dir variables after WORKON\_HOME is set; fixes #94 +* link to documentation about installing into user directory +* further installation doc clarification +* add a warning about installing into a virtualenv +* clarify instructions for running tests; fixes #92 +* report an error if there are no test scripts +* Added tag 2.7 for changeset ea378ef00313 + +2.7 +--- + +* update version and draft announcement +* add grep fix to history +* remove -e option from all calls to grep for better portability; fixes #85 +* nicer titles for configuration section +* reorg install docs to separate the customization stuff; add some comments about site-wide installation; fixes #87 +* make it possible to remove a virtualenv while inside it; fixes #83 +* pass VIRTUALENVWRAPPER\_VIRTUALENV\_ARGS when calling VIRTUALENVWRAPPER\_VIRTUALENV; fixes #89; fixes #87 +* add link to vim-virtualenv +* enable tab completion for showvirtualenv; fixes #78 +* clean up test instructions for developers; fixes #75 +* clear configuration variables before running tests +* fix typo in cpvirtualenv; fixes #71 +* Add VIRTUALENVWRAPPER\_LOG\_DIR variable +* Use VIRTUALENVWRAPPER\_HOOK\_DIR to control where the hooks are defined +* doc updates for VIRTUALENVWRAPPER\_VIRTUALENV +* fix tests to work under ksh on ubuntu 10.10 by using alternate syntax for capturing messages sent to stderr +* fix tempdir tests to work on ubuntu 10.10 +* merge pmclanahan's test changes and toggleglobalsitepackages +* Add attribution for recent patches to the history file +* fix tests for changes to virtualenvwrapper\_verify\_workon\_home +* suppress hook loader messages in tests +* change verbosity level when creating hook scripts so the messages can be suppressed in tests +* Added docs for the toggleglobalsitepackages command +* Added "toggleglobalsitepackages" command. Added tests for the new command +* Modified the test runner to reliably use the intended shells +* fix arg handling for lsvirtualenv under zsh - fixes issue #86 +* remove the custom functions from the sphinx config, since rtd does not support them +* trying readthedocs again +* ignore .orig files created by hg +* fix lsvirtualenv to read args in zsh +* remove the download url since I upload packages to pypi now +* translated 2.6.2/2.6.3 history into Japanese +* fixes issue 79 by enclosing WORKON\_HOME in quotes +* merged from original +* Added tag 2.6.3 for changeset 246ce68795ea + +2.6.3 +----- + +* tweak history +* Added tag 2.6.3 for changeset e7582879df06 +* more doc build changes +* add upload target +* Added tag 2.6.2 for changeset 625d85d3136f + +2.6.2 +----- + +* fix doc build for readthedocs.org +* add test for space in WORKON\_HOME to address #79 +* add a test to verify that when virtualenv fails to create an environment the hook scripts are not run. see #76 +* merged a few fixes and updated history +* update history +* merge in japanese translation of documentation, with a few markup fixes; disable spelling extension until there is a python 2.7 installer for it +* add spelling extension +* Added Japanese translation for the documentation Added to make html/website for the Japanese documentation Added the Japanese documentation link in original English index.rst +* restore download url +* Added tag 2.6.1 for changeset 445a58d5a05a + +2.6.1 +----- + +* version 2.6.1 +* fixes issue #73 by changing virtualenvwrapper\_get\_python\_version to only include the major and minor numbers +* add supported version info to readme so it appears on pypi page +* Added tag 2.6 for changeset b0f27c65fa64 + +2.6 +--- + +* bump version to 2.6 and document updates +* avoid specifying text mode when creating hook scripts (fixes #68) +* closes #70 by adding a list of supported shells and python versions to documentation and trove classifiers +* fix #60 by setting install\_requires instead of requires +* change the way we determine the python version +* convert test scripts to use tox instead of home-grown multi-version system in the Makefile +* create the WORKON\_HOME dir if it doesn't exist +* fix platforms definition so upload to pypi will work +* Added tag 2.5.3 for changeset dc74f106d8d2 + +2.5.3 +----- + +* point release before uploading sdist +* Added tag 2.5.2 for changeset f71ffbb996c4 + +2.5.2 +----- + +* Make lsvirtualenv work under zsh using patch from Zach Voase. Fixes #64 +* Added tag 2.5.1 for changeset 2ab678413a29 + +2.5.1 +----- + +* fix workon to list in brief mode +* Added tag 2.5 for changeset 80e2fcda77ac + +2.5 +--- + +* bump version +* add docs for showvirtualenv +* add showvirtualenv and re-implement lsvirtualenv with it +* Added tag 2.4 for changeset a85d80e88996 + +2.4 +--- + +* tweak history file +* Added tag 2.4 for changeset 64f858d461d4 +* add lsvirtualenv command with -l option +* Added tag 2.3 for changeset b9d4591458bb + +2.3 +--- + +* add get\_env\_details hook +* Added tag 2.2.2 for changeset 266a166f80da + +2.2.2 +----- + +* bump version to 2.2.2 +* check exit code of virtualenv before proceeding (fixes #56) +* use single quotes around regex with $ (see #55) +* update history with changes (see #57) +* escape more commands (see #57) +* incorporate patch from fredpalmer to escape grep calls (fixes #57) +* Added tag 2.2.1 for changeset 87d60f20a715 + +2.2.1 +----- + +* fix #50 by escaping rm before calling it +* Added tag 2.2.1 for changeset 66a89d019905 +* bump version to 2.2.1 +* convert path deriving code in startup of script to function so it is easier to test +* escape dollar sign in regex to resolve #53 +* add tests for GREP\_OPTIONS problem (ref #51) +* unset GREP\_OPTIONS before to use grep +* add support and bug tracker link to readme and docs +* ignore missing files in trap cleanup (see #38) +* address #37 with wording change in docs +* update history +* address issue #46 by escaping the calls to which +* Added tag 2.2 for changeset d5c5faecc92d + +2.2 +--- + +* bump version number +* more test refinements +* add trap to remove temporary file, see #38 +* more tempfile fixes +* changes to make the tests run on my linux host +* mention changes to address ticket 35 in history +* addresses ticket 35 by adding debugging instrumentation +* since we always use the same config dir, set it once +* unify sphinx config files +* use the sphinxcontrib.bitbucket extension for links to the issues and changesets in history.rst +* update history with recent changes +* fix tests; clean up contributed changes +* Fixing a bug in the call to mktemp +* Some cleanup after talking with dhellmann +* First pass at speeding things up by making fewer calls into Python. Needs review +* review for text added by Doug about the translation +* show python version in test progress messages +* fix #44 by updating the tests to run with python 2.7b1 +* fix #43 by switching the way the hook loader is run +* Added tag 2.1.1 for changeset 7540fc7d8e63 + +2.1.1 +----- + +* setting up for a release +* fix #42 by quieting the errors/warnings +* fix #41 by using the cached python where the wrappers are installed +* fix formatting of seealso block +* link to Manuel's home page instead of just the translation +* add link back to english docs +* add attribution for Manuel +* add link from english to spanish docs; update history +* shift output directory for html build so the sdist package looks nicer +* merge in spanish translation +* another attempt to address #35 +* added italic to deactivation +* announce translation +* english paragraph removed +* README translated +* first revision +* index revision +* markup fix +* aspell to plugins and fix some paragraphs +* aspell for script +* aspell to install +* aspell to index +* aspell to hooks +* aspell for extensions +* aspell for developers +* aspell for command\_ref +* another paragraph +* almost done for plugins.rst +* continue the translation +* continue the translation of plugins.rst +* remove the option that copy the static files: we don't have file to copy and it generate a WARNING in the sphinx compilation +* markup fixed +* remove translation from the toctree +* scripts.rst tranlated to spanish +* remove old version of translations.rst, we don't need this file anymore +* I don't think that we need to translate the ChangeLog +* extensions.rst translated +* tips.rst translated +* rst markup fixed +* typo fixed on english documentation +* fix the Makefile to generate the website documentation for 'en' and 'es' languages +* reorder the documents files in docs/LANGUAGE folders and modify the rules in the Makefile to build the documentation +* merge from Doug commit. Added the base.html template to make the website documentation +* Makefile modified to build "es" documentation +* put the base template in the repository +* developers.rst translated +* typo fixed +* continue plugins.rst translation +* Fix typo found by humitos +* starting with "Defining an Extension" +* start to translate plugins.rst +* hooks translated +* translations in the index page +* added some translated topics +* added the translation for install.rst +* index.rst translated to spanish +* fixed the right bug :) +* update announcement for 2.1 +* add emacs directive to readme +* Added tag 2.1 for changeset 241df6c36860 + +2.1 +--- + +* bump version +* rotate log file when it grows too big +* do not include website html in sdist +* do not include html docs inside virtualenvwrapper dir to avoid conflicts with other packages using that namespace +* fix mkvirtualenv -h +* doc updates +* add references to new extensions +* add -n and -l options to hook loader +* update docs with examples +* handle empty workon\_home dir properly +* support nondescructive argument to deactivate +* include a date value in the filename +* fix #34 by using python's tempfile module instead of a shell command +* add hooks for cpvirtualenv; make deactivate work better under ksh +* Update docs for mkvirtualenv to fix #30 +* fix #33 with improved installation instructions and a better error message +* use tempfile to create temporary files instead of the process id so the filenames are less predictable +* update contributing info +* add attribution for research work for ksh port +* add support for ksh (fixes #25) +* copy dist file to desktop after building +* Added tag 2.0.2 for changeset 6a51a81454ae + +2.0.2 +----- + +* update version and history +* fix #32 by removing use of 'with' ; add tests for python 2.6 and 2.5 +* sort ignore lines and add build directory +* Added tag 2.0.1 for changeset 91e1124c6831 + +2.0.1 +----- + +* update version and history +* add documentation about temp files +* fix #29 by checking TMPDIR and using a default if no value is found +* save draft of email for announcing new releases on python-announce +* Added tag 2.0 for changeset 54713c4552c2 + +2.0 +--- + +* fix install dir for web docs +* Added tag 2.0 for changeset 485e1999adf0 +* move todo list out of hg repo +* add namespace package declaration +* include more motivational background +* add help to Makefile +* merge 2.0 changes into tip +* status update +* even more doc cleanup +* doc restructuring +* remove rudundant 'source' from cli +* more doc cleanup +* more doc cleanup +* update extension entry point docs +* move make\_hooks functionality into user\_scripts, since they are related +* start overhauling doc content +* test cleanup and enhancement +* add VIRTUALENVWRAPPER\_LAST\_VIRTUAL\_ENV variable for postdeactivate scripts +* use the user's current shell as the default interpreter in the hook script +* quiet hook loader +* minor doc updates and formatting changes +* comment out debug logging +* all existing tests are passing again +* convert more hooks; stop running tests when we see a failure or error +* implement initialize hooks +* start implementing hook loader and a couple of sample hooks +* rename wrapper script +* add register rule +* update installation test +* don't need pavement.py any more +* add rules for updating website +* set version in Makefile before building html +* more tasks +* add test rules +* start moving from paver back to make and distribute +* reorg todo list +* add todo list and design notes for hook scripts +* Added tag 1.27 for changeset d64869519c2e + +1.27 +---- + +* add explicit check for virtualenv in the test +* Added tag 1.27 for changeset 3edf5f224815 +* bump version; pre-release code cleanup +* add note about relocatable side-effect +* undo merge, tests moved to separate files +* touch up tests +* flush formatting prints +* quiet tests +* ignore build files created by tests +* added test that copied virtualenv exists +* resolve conflict on tests dispatch +* added script to setup.py +* add testpackage setup.py +* Added tag 1.26 for changeset 51eef82a39d4 + +1.26 +---- + +* preparing version 1.26 for release +* fix #26 by quieting the error message during init and only showing it when an action is explicitly taken by the user +* remove directories likely to contain a site-wide virtualenv installation and hide the error message because we expect mkvirtualenv to fail +* break up the tests to make it easier to run only part of them +* run all tests on all shells +* Added tag 1.25 for changeset 06229877a640 + +1.25 +---- + +* add cdsitepackages arg handling from William McVey +* Added test for cdsitepackages with argument +* Updated with expanded capability of cdsitepackages to cd to a subdir +* Added tab completion and pathname argument handling to cdsitepackages +* I didn't know about 'sed -i', makes this a lot easier +* When echoing the current list of paths, do not include the 'import' lines +* Test for existance of path file was broken, used the wrong test +* New -d option to 'add2virtualenv' which allows removal of a path previously added +* Make sure that paths added via 'add2virtualenv' always end up being listed \*before\* regularily installed packages in sys.path. This ensures that you can always use the command to replace an installed package with a out-of-virtualenv version +* Added tag 1.24.2 for changeset f31869779141 + +1.24.2 +------ + +* update history and bump version +* update history +* add user-provided tips to the docs +* switch doc theme for packaged docs; add link to Rich Leland's screencast +* Added tag 1.24.1 for changeset 4a8870326d84 + +1.24.1 +------ + +* bump version num before new release +* add license and home page info to top of script +* Added tag 1.24 for changeset b243d023094b + +1.24 +---- + +* bump version and update history +* fix preactivate scripts; warn for existing scripts that need to be executable but are not +* Added tag 1.23 for changeset e55e8a54de7b + +1.23 +---- + +* prep for release +* test both mkvirtualenv hooks +* fix the postmkvirtualenv hook +* Added tag 1.22 for changeset c50385e9c99b + +1.22 +---- + +* bump version +* Added tag 1.22 for changeset eddb2921783c +* automatically create hook scripts +* add mode specification for emacs +* update README instructions +* Added tag 1.21 for changeset 2190584becc7 + +1.21 +---- + +* update version for new release +* Added tag 1.21 for changeset c11ee7913230 +* verify that virtualenv is installed; correct use of python to fix the WORKON\_HOME value; more tests +* improve handling for missing WORKON\_HOME variable or directory; add test for #18 - can't reproduce +* Added tag 1.20 for changeset ed873ac408ff + +1.20 +---- + +* prepare release +* minor code cleanup +* added simple lssitepackages test +* lssitepackages now also shows contents of virtualenv\_path\_extensions.pth, if that file exists +* added a white-line at the end +* added lssitepackages info +* added lssitepackages command +* moved main website source files +* Added tag 1.19 for changeset 8af191bfa3c8 + +1.19 +---- + +* fix for ticket #14: relative paths don't work with add2virtualenv +* incorporate patch from Sascha Brossmann to fix #15 +* Applying my own ridiculous formatting to the README file. Give me 72 characters or give me death! +* Added tag 1.18 for changeset 24190e878fa8 + +1.18 +---- + +* bump version number +* don't forget the destdir info +* add basic developer info to the documentation +* add docs for deactivate to resolve issue #12 +* fix issue #10 by removing warning and using an error at runtime +* Added tag 1.17.1 for changeset 10fbaab7da41 + +1.17.1 +------ + +* update pavement to use sphinxcontrib.paverutils +* Added tag 1.17 for changeset 749030a692a0 + +1.17 +---- + +* add installation test task +* incorporate personal site templates into a build that lets me generate hosted docs +* formatting tweaks +* add feature list; clean up hook list; fix bug in warning message generation +* cannot run package from command line, so just warn on import +* create a simple python package and include the documentation in it so it is installed by default +* clean up and update docs, reduce size of readme, start working on packaging changes +* import documentation contribution from Steve Steiner +* run the tests under zsh as well as explicitly invoking bash +* Added tag 1.16 for changeset 7d9dbc84f25d + +1.16 +---- + +* bump version +* remove todo list +* Redirect all error messages from stdout to stderr Added directory completion for cdvirtualenv +* Allow cdvirtualenv to take an argument which is a directory under the virtualenv root to change into +* Added tag 1.15 for changeset bddfac3c8fde + +1.15 +---- + +* prep release 1.15 +* error handling in mkvirtualenv +* add tests to sdist package +* Added tag 1.14 for changeset 6e54ea32a9d1 + +1.14 +---- + +* use dist\_dir option for sdist command +* Added tag 1.14 for changeset caf3f2a31fdd +* update version # +* Added tag 1.14 for changeset e31542a0d946 +* update change list +* fix virtualenvwrapper\_show\_workon\_options to use find again +* rewrite tests using shutil2 +* experimental version of deactivate wrapper +* Added tag 1.13 for changeset 7c40caf6ce6f + +1.13 +---- + +* add test.sh to manifest +* Added tag 1.13 for changeset 8e73805a97e1 +* fix for issue #5 +* Added tag 1.12 for changeset dda0e4d36a91 + +1.12 +---- + +* fix verification in navigation functions and add tests +* Add a couple of quick-navigation helper functions +* add attribution +* check return code from virtualenvwrapper\_verify\_workon\_home everywhere and return an error code if validation fails +* Update quick setup instructions to make them a little easier to follow and to fix a mistake in the order of some of the steps +* Added tag 1.11 for changeset 511994f15d58 + +1.11 +---- + +* run global postactivatehook before local; move release not to the correct version +* merge ChrisHas35's postactivatehook changes +* start 1.11 with optimization suggestion from ChrisHas35 +* Added tag 1.10 for changeset 274d4576d606 +* add global postactivate hook. related to #3 +* remove unnecssary egrep calls on show\_workon\_options. fixes #4 + +1.10 +---- + +* update change history +* Updated 'workon' to use find, to avoid problems with colorized 'ls' output +* Added tag 1.9 for changeset d8112e52eadc + +1.9 +--- + +* add more hooks based on suggestion from Chris Hasenpflug; add documentation +* Added tag 1.8.1 for changeset 8417344df8ff + +1.8.1 +----- + +* bump version number +* Added tag 1.8.1 for changeset dca76424222e +* fix argument processing in mkvirtualenv +* Added tag 1.8 for changeset ea5f27af83bb + +1.8 +--- + +* Fix for processing the argument list in mkvirtualenv from jorgevargas (#1) +* Added tag 1.7 for changeset 32f2a081d649 + +1.7 +--- + +* Clean up TODO list and svn keywords. Add license section to README +* Added tag 1.7 for changeset 54aa96a1c09f +* Ignore files generated by paver and the build process. Use a fixed version string in the pavement.py file +* update tags +* convert from make to paver 1.0 +* patches to rmvirtualenv to make it work with zsh from Byron Clark +* add note about zsh completion support +* add zsh completion support, courtesy of Ted Leung +* add docs; fix space issues +* remove premature release +* add path management feature contributed by James Bennett +* fix another typo, TEST, then add another useful message when the user tries to remove an active environment +* fix spelling mistake + +1.6.1 +----- + +* bug fix from John Shimek +* Add tab completion based on Arthur Koziel's version at http://arthurkoziel.com/2008/10/11/virtualenvwrapper-bash-completion/ +* fix the download url + +1.3 +--- + +* add setup.py and related pieces, including minimal docs +* usability patches from Alex Satrapa +* notes about what I still need to do +* cleanup +* predeactivate and postactivate hooks +* go ahead and change to the environment after creating it +* look for the workdir script and run it if we find it +* update comments +* add attribution +* keywords + +1.0 +--- + +* first copy +* start new project diff --git a/docs/source/hooks.rst b/docs/source/hooks.rst new file mode 100644 index 0000000..6cd0cef --- /dev/null +++ b/docs/source/hooks.rst @@ -0,0 +1,17 @@ +=============================== + Customizing Virtualenvwrapper +=============================== + +virtualenvwrapper adds several hook points you can use to change your +settings, shell environment, or other configuration values when +creating, deleting, or moving between environments. These hooks are +exposed in two ways. :ref:`scripts` allows a user to perform generic actions +for every virtualenv in your environment, including customization of +virtualenv creation. :ref:`plugins` makes it possible to share common +behaviors between systems and developers. + +.. toctree:: + :maxdepth: 1 + + scripts + plugins diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..f0fc0e1 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,249 @@ +.. virtualenvwrapper documentation master file, created by + sphinx-quickstart on Thu May 28 22:35:13 2009. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +########################### +virtualenvwrapper |release| +########################### + +virtualenvwrapper is a set of extensions to Ian Bicking's `virtualenv +`_ tool. The extensions +include wrappers for creating and deleting virtual environments and +otherwise managing your development workflow, making it easier to work +on more than one project at a time without introducing conflicts in +their dependencies. + +======== +Features +======== + +1. Organizes all of your virtual environments in one place. +2. Wrappers for managing your virtual environments (create, delete, + copy). +3. Use a single command to switch between environments. +4. Tab completion for commands that take a virtual environment as + argument. +5. User-configurable hooks for all operations (see :ref:`scripts`). +6. Plugin system for creating more sharable extensions (see + :ref:`plugins`). + +============ +Introduction +============ + +The best way to explain the features virtualenvwrapper gives you is to +show it in use. + +First, some initialization steps. Most of this only needs to be done +one time. You will want to add the command to ``source +/usr/local/bin/virtualenvwrapper.sh`` to your shell startup file, +changing the path to virtualenvwrapper.sh depending on where it was +installed by pip or your package manager. + +:: + + $ pip install virtualenvwrapper + ... + $ export WORKON_HOME=~/Envs + $ mkdir -p $WORKON_HOME + $ source /usr/local/bin/virtualenvwrapper.sh + $ mkvirtualenv env1 + Installing + setuptools.......................................... + .................................................... + .................................................... + ...............................done. + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/predeactivate + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/postdeactivate + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/preactivate + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/postactivate New python executable in env1/bin/python + (env1)$ ls $WORKON_HOME + env1 hook.log + +Now we can install some software into the environment. + +:: + + (env1)$ pip install django + Downloading/unpacking django + Downloading Django-1.1.1.tar.gz (5.6Mb): 5.6Mb downloaded + Running setup.py egg_info for package django + Installing collected packages: django + Running setup.py install for django + changing mode of build/scripts-2.6/django-admin.py from 644 to 755 + changing mode of /Users/dhellmann/Envs/env1/bin/django-admin.py to 755 + Successfully installed django + +We can see the new package with ``lssitepackages``:: + + (env1)$ lssitepackages + Django-1.1.1-py2.6.egg-info easy-install.pth + setuptools-0.6.10-py2.6.egg pip-0.6.3-py2.6.egg + django setuptools.pth + +Of course we are not limited to a single virtualenv:: + + (env1)$ ls $WORKON_HOME + env1 hook.log + (env1)$ mkvirtualenv env2 + Installing setuptools............................... + .................................................... + .................................................... + ........... ...............................done. + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/predeactivate + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/postdeactivate + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/preactivate + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/postactivate New python executable in env2/bin/python + (env2)$ ls $WORKON_HOME + env1 env2 hook.log + +Switch between environments with ``workon``:: + + (env2)$ workon env1 + (env1)$ echo $VIRTUAL_ENV + /Users/dhellmann/Envs/env1 + (env1)$ + +The ``workon`` command also includes tab completion for the +environment names, and invokes customization scripts as an environment +is activated or deactivated (see :ref:`scripts`). + +:: + + (env1)$ echo 'cd $VIRTUAL_ENV' >> $WORKON_HOME/postactivate + (env1)$ workon env2 + (env2)$ pwd + /Users/dhellmann/Envs/env2 + +:ref:`scripts-postmkvirtualenv` is run when a new environment is +created, letting you automatically install commonly-used tools. + +:: + + (env2)$ echo 'pip install sphinx' >> $WORKON_HOME/postmkvirtualenv + (env3)$ mkvirtualenv env3 + New python executable in env3/bin/python + Installing setuptools............................... + .................................................... + .................................................... + ........... ...............................done. + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/predeactivate + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/postdeactivate + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/preactivate + virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/postactivate + Downloading/unpacking sphinx + Downloading Sphinx-0.6.5.tar.gz (972Kb): 972Kb downloaded + Running setup.py egg_info for package sphinx + no previously-included directories found matching 'doc/_build' + Downloading/unpacking Pygments>=0.8 (from sphinx) + Downloading Pygments-1.3.1.tar.gz (1.1Mb): 1.1Mb downloaded + Running setup.py egg_info for package Pygments + Downloading/unpacking Jinja2>=2.1 (from sphinx) + Downloading Jinja2-2.4.tar.gz (688Kb): 688Kb downloaded + Running setup.py egg_info for package Jinja2 + warning: no previously-included files matching '*' found under directory 'docs/_build/doctrees' + Downloading/unpacking docutils>=0.4 (from sphinx) + Downloading docutils-0.6.tar.gz (1.4Mb): 1.4Mb downloaded + Running setup.py egg_info for package docutils + Installing collected packages: docutils, Jinja2, Pygments, sphinx + Running setup.py install for docutils + Running setup.py install for Jinja2 + Running setup.py install for Pygments + Running setup.py install for sphinx + no previously-included directories found matching 'doc/_build' + Installing sphinx-build script to /Users/dhellmann/Envs/env3/bin + Installing sphinx-quickstart script to /Users/dhellmann/Envs/env3/bin + Installing sphinx-autogen script to /Users/dhellmann/Envs/env3/bin + Successfully installed docutils Jinja2 Pygments sphinx (env3)$ + (venv3)$ which sphinx-build + /Users/dhellmann/Envs/env3/bin/sphinx-build + +Through a combination of the existing functions defined by the core +package (see :ref:`command`), third-party plugins (see +:ref:`plugins`), and user-defined scripts (see :ref:`scripts`) +virtualenvwrapper gives you a wide variety of opportunities to +automate repetitive operations. + +======= +Details +======= + +.. toctree:: + :maxdepth: 2 + + install + command_ref + hooks + projects + tips + developers + extensions + design + history + glossary + +.. _references: + +========== +References +========== + +`virtualenv `_, from Ian +Bicking, is a pre-requisite to using these extensions. + +For more details, refer to the column I wrote for the May 2008 issue +of Python Magazine: `virtualenvwrapper | And Now For Something +Completely Different +`_. + +Manuel Kaufmann has `translated this documentation into Spanish +`__. + +Tetsuya Morimoto has `translated this documentation into Japanese +`__. + +======= +Support +======= + +Join the `virtualenvwrapper Google Group +`__ to discuss +issues and features. + +Report bugs via the `bug tracker on GitHub +`__. + +Shell Aliases +============= + +Since virtualenvwrapper is largely a shell script, it uses shell +commands for a lot of its actions. If your environment makes heavy +use of shell aliases or other customizations, you may encounter +issues. Before reporting bugs in the bug tracker, please test +*without* your aliases enabled. If you can identify the alias causing +the problem, that will help make virtualenvwrapper more robust. + +.. _license: + +======= +License +======= + +Copyright Doug Hellmann, All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Doug Hellmann not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/docs/source/install.rst b/docs/source/install.rst new file mode 100644 index 0000000..4af1f06 --- /dev/null +++ b/docs/source/install.rst @@ -0,0 +1,333 @@ +============ +Installation +============ + +.. _supported-shells: + +Supported Shells +================ + +virtualenvwrapper is a set of shell *functions* defined in Bourne +shell compatible syntax. Its automated tests run under these +shells on OS X and Linux: + +* ``bash`` +* ``zsh`` + +It may work with other shells, so if you find that it does work with a +shell not listed here please let us know. If you can modify it to +work with another shell without completely rewriting it, then send a pull +request through the `GitHub project page`_. If you write a clone to +work with an incompatible shell, let us know and we will link to it +from this page. + +.. _GitHub project page: https://GitHub.com/python-virtualenvwrapper/virtualenvwrapper/ + +Windows Command Prompt +---------------------- + +David Marble has ported virtualenvwrapper to Windows batch scripts, +which can be run under Microsoft Windows Command Prompt. This is also +a separately distributed re-implementation. You can download +`virtualenvwrapper-win`_ from PyPI. + +.. _virtualenvwrapper-win: https://pypi.python.org/pypi/virtualenvwrapper-win + +MSYS +---- + +It is possible to use virtualenv wrapper under `MSYS +`_ with a native Windows Python +installation. In order to make it work, you need to define an extra +environment variable named ``MSYS_HOME`` containing the root path to +the MSYS installation. + +:: + + export WORKON_HOME=$HOME/.virtualenvs + export MSYS_HOME=/c/msys/1.0 + source /usr/local/bin/virtualenvwrapper.sh + +or:: + + export WORKON_HOME=$HOME/.virtualenvs + export MSYS_HOME=C:\msys\1.0 + source /usr/local/bin/virtualenvwrapper.sh + +Depending on your MSYS setup, you may need to install the `MSYS mktemp +binary`_ in the ``MSYS_HOME/bin`` folder. + +.. _MSYS mktemp binary: https://sourceforge.net/projects/mingw/files/MSYS/Extension/mktemp/ + +PowerShell +---------- + +Guillermo López-Anglada has ported virtualenvwrapper to run under +Microsoft's PowerShell. We have agreed that since it is not compatible +with the rest of the extensions, and is largely a re-implementation +(rather than an adaptation), it should be distributed separately. You +can download virtualenvwrapper-powershell_ from PyPI. + +.. _virtualenvwrapper-powershell: https://pypi.python.org/pypi/virtualenvwrapper-powershell + +.. _supported-versions: + +Python Versions +=============== + +virtualenvwrapper is tested under Python 3.8 - 3.11. + +.. _install-basic: + +Basic Installation +================== + +virtualenvwrapper should be installed into the same global +site-packages area where virtualenv is installed. You may need +administrative privileges to do that. The easiest way to install it +is using pip_:: + + $ pip install virtualenvwrapper + +or:: + + $ sudo pip install virtualenvwrapper + +.. warning:: + + virtualenv lets you create many different Python environments. You + should only ever install virtualenv and virtualenvwrapper on your + base Python installation (i.e. NOT while a virtualenv is active) + so that the same release is shared by all Python environments that + depend on it. + +An alternative to installing it into the global site-packages is to +add it to `your user local directory +`__ +(usually `~/.local`). + +:: + + $ pip install --user virtualenvwrapper + +.. _install-shell-config: + +Shell Startup File +================== + +Add three lines to your shell startup file (``.bashrc``, ``.profile``, +etc.) to set the location where the virtual environments should live, +the location of your development project directories, and the location +of the script installed with this package:: + + export WORKON_HOME=$HOME/.virtualenvs + export PROJECT_HOME=$HOME/Devel + source /usr/local/bin/virtualenvwrapper.sh + +After editing it, reload the startup file (e.g., run ``source +~/.bashrc``). + +.. _install-lazy-loader: + +Lazy Loading +------------ + +An alternative initialization script is provided for loading +virtualenvwrapper lazily. Instead of sourcing ``virtualenvwrapper.sh`` +directly, use ``virtualenvwrapper_lazy.sh``. If +``virtualenvwrapper.sh`` is not on your ``$PATH``, set +``VIRTUALENVWRAPPER_SCRIPT`` to point to it. + +:: + + export WORKON_HOME=$HOME/.virtualenvs + export PROJECT_HOME=$HOME/Devel + export VIRTUALENVWRAPPER_SCRIPT=/usr/local/bin/virtualenvwrapper.sh + source /usr/local/bin/virtualenvwrapper_lazy.sh + +.. warning:: + + When the lazy-loading version of the startup script is used, + tab-completion of arguments to virtualenvwrapper commands (such as + environment names) is not enabled until after the first command has + been run. For example, tab completion of environments does not work + for the first instance of :ref:`command-workon`. + +Quick-Start +=========== + +1. Run: ``workon`` +2. A list of environments, empty, is printed. +3. Run: ``mkvirtualenv temp`` +4. A new environment, ``temp`` is created and activated. +5. Run: ``workon`` +6. This time, the ``temp`` environment is included. + +Configuration +============= + +virtualenvwrapper can be customized by changing environment +variables. Set the variables in your shell startup file *before* +loading ``virtualenvwrapper.sh``. + +.. _variable-WORKON_HOME: + +Location of Environments +------------------------ + +The variable ``WORKON_HOME`` tells virtualenvwrapper where to place +your virtual environments. The default is ``$HOME/.virtualenvs``. If +the directory does not exist when virtualenvwrapper is loaded, it will +be created automatically. + +.. _variable-PROJECT_HOME: + +Location of Project Directories +------------------------------- + +The variable ``PROJECT_HOME`` tells virtualenvwrapper where to place +your project working directories. The variable must be set and the +directory created before :ref:`command-mkproject` is used. + +.. seealso:: + + * :ref:`project-management` + +.. _variable-VIRTUALENVWRAPPER_PROJECT_FILENAME: + +Project Linkage Filename +------------------------ + +The variable ``VIRTUALENVWRAPPER_PROJECT_FILENAME`` tells +virtualenvwrapper how to name the file linking a virtualenv to a +project working directory. The default is ``.project``. + +.. seealso:: + + * :ref:`project-management` + +.. _variable-VIRTUALENVWRAPPER_WORKON_CD: + +Enable Project Directory Switching +---------------------------------- + +The variable ``VIRTUALENVWRAPPER_WORKON_CD`` controls whether the +working directory is changed during the post activate phase. The +default is ``1``, to enable changing directories. Set the value to +``0`` to disable this behavior for all invocations of ``workon``. + +.. seealso:: + + * :ref:`command-workon` + +.. _variable-VIRTUALENVWRAPPER_HOOK_DIR: + +Location of Hook Scripts +------------------------ + +The variable ``VIRTUALENVWRAPPER_HOOK_DIR`` tells virtualenvwrapper +where the :ref:`user-defined hooks ` should be placed. The +default is ``$WORKON_HOME``. + +.. seealso:: + + * :ref:`scripts` + +.. _variable-VIRTUALENVWRAPPER_LOG_FILE: + +Location of Hook Logs +--------------------- + +The variable ``VIRTUALENVWRAPPER_LOG_FILE`` tells virtualenvwrapper +where the logs for the hook loader should be written. The default is +to not log from the hooks. + +.. _variable-VIRTUALENVWRAPPER_VIRTUALENV: + +.. _variable-VIRTUALENVWRAPPER_VIRTUALENV_ARGS: + +.. _variable-VIRTUALENVWRAPPER_PYTHON: + +Python Interpreter, virtualenv, and $PATH +----------------------------------------- + +During startup, ``virtualenvwrapper.sh`` finds the first ``python`` +and ``virtualenv`` programs on the ``$PATH`` and remembers them to use +later. This eliminates any conflict as the ``$PATH`` changes, +enabling interpreters inside virtual environments where +virtualenvwrapper is not installed or where different versions of +virtualenv are installed. Because of this behavior, it is important +for the ``$PATH`` to be set **before** sourcing +``virtualenvwrapper.sh``. For example:: + + export PATH=/usr/local/bin:$PATH + source /usr/local/bin/virtualenvwrapper.sh + +To override the ``$PATH`` search, set the variable +``VIRTUALENVWRAPPER_PYTHON`` to the full path of the interpreter to +use and ``VIRTUALENVWRAPPER_VIRTUALENV`` to the full path of the +``virtualenv`` binary to use. Both variables *must* be set before +sourcing ``virtualenvwrapper.sh``. For example:: + + export VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python + export VIRTUALENVWRAPPER_VIRTUALENV=/usr/local/bin/virtualenv + source /usr/local/bin/virtualenvwrapper.sh + +Default Arguments for virtualenv +-------------------------------- + +If the application identified by ``VIRTUALENVWRAPPER_VIRTUALENV`` +needs arguments, they can be set in +``VIRTUALENVWRAPPER_VIRTUALENV_ARGS``. The same variable can be used +to set default arguments to be passed to ``virtualenv``. For example, +set the value to ``--no-site-packages`` to ensure that all new +environments are isolated from the system ``site-packages`` directory. + +:: + + export VIRTUALENVWRAPPER_VIRTUALENV_ARGS='--no-site-packages' + +Temporary Files +--------------- + +virtualenvwrapper creates temporary files in ``$TMPDIR``. If the +variable is not set, it uses ``/tmp``. To change the location of +temporary files just for virtualenvwrapper, set +``VIRTUALENVWRAPPER_TMPDIR``. + +Site-wide Configuration +----------------------- + +Most UNIX systems include the ability to change the configuration for +all users. This typically takes one of two forms: editing the +*skeleton* files for new accounts or editing the global startup file +for a shell. + +Editing the skeleton files for new accounts means that each new user +will have their private startup files preconfigured to load +virtualenvwrapper. They can disable it by commenting out or removing +those lines. Refer to the documentation for the shell and operating +system to identify the appropriate file to edit. + +Modifying the global startup file for a given shell means that all +users of that shell will have virtualenvwrapper enabled, and they +cannot disable it. Refer to the documentation for the shell to +identify the appropriate file to edit. + +Upgrading to 2.9 +================ + +Version 2.9 includes the features previously delivered separately by +``virtualenvwrapper.project``. If you have an older verison of the +project extensions installed, remove them before upgrading. + +Upgrading from 1.x +================== + +The shell script containing the wrapper functions has been renamed in +the 2.x series to reflect the fact that shells other than bash are +supported. In your startup file, change ``source +/usr/local/bin/virtualenvwrapper_bashrc`` to ``source +/usr/local/bin/virtualenvwrapper.sh``. + +.. _pip: https://pypi.python.org/pypi/pip diff --git a/docs/source/plugins.rst b/docs/source/plugins.rst new file mode 100644 index 0000000..4587355 --- /dev/null +++ b/docs/source/plugins.rst @@ -0,0 +1,403 @@ +.. _plugins: + +=========================== +Extending Virtualenvwrapper +=========================== + +Long experience with home-grown solutions for customizing a +development environment has proven how valuable it can be to have the +ability to automate common tasks and eliminate persistent annoyances. +Carpenters build jigs, software developers write shell scripts. +virtualenvwrapper continues the tradition of encouraging a craftsman +to modify their tools to work the way they want, rather than the other +way around. + +There are two ways to attach your code so that virtualenvwrapper will +run it: End-users can use shell scripts or other programs for personal +customization, e.g. automatically performing an action on every new +virtualenv (see :ref:`scripts`). Extensions can also be +implemented in Python by using `Setuptools entry points`_, making it +possible to share common behaviors between systems and developers. + +Use the hooks provided to eliminate repetitive manual operations and +streamline your development workflow. For example, set up the +:ref:`plugins-pre_activate` and :ref:`plugins-post_activate` hooks to +trigger an IDE to load a project file to reload files from the last +editing session, manage time-tracking records, or start and stop +development versions of an application server. Use the +:ref:`plugins-initialize` hook to add entirely new commands and hooks +to virtualenvwrapper. And the :ref:`plugins-pre_mkvirtualenv` and +:ref:`plugins-post_mkvirtualenv` hooks give you an opportunity to +install basic requirements into each new development environment, +initialize a source code control repository, or otherwise set up a new +project. + +Defining an Extension +===================== + +.. note:: + + Virtualenvwrapper is delivered with a plugin for creating and + running the user customization scripts + (:ref:`extensions-user_scripts`). The examples below are taken from + the implementation of that plugin. + +Code Organization +----------------- + +The Python package for ``virtualenvwrapper`` is a *namespace package*. +That means multiple libraries can install code into the package, even +if they are not distributed together or installed into the same +directory. Extensions can (optionally) use the ``virtualenvwrapper`` +namespace by setting up their source tree like: + +* virtualenvwrapper/ + + * __init__.py + * user_scripts.py + +And placing the following code in ``__init__.py``:: + + """virtualenvwrapper module + """ + + __import__('pkg_resources').declare_namespace(__name__) + +.. note:: + + Extensions can be loaded from any package, so using the + ``virtualenvwrapper`` namespace is not required. + +Extension API +------------- + +After the package is established, the next step is to create a module +to hold the extension code. For example, +``virtualenvwrapper/user_scripts.py``. The module should contain the +actual extension entry points. Supporting code can be included, or +imported from elsewhere using standard Python code organization +techniques. + +The API is the same for every extension point. Each uses a Python +function that takes a single argument, a list of strings passed to the +hook loader on the command line. + +:: + + def function_name(args): + # args is a list of strings passed to the hook loader + +The contents of the argument list are defined for each extension point +below (see :ref:`plugins-extension-points`). + +Extension Invocation +-------------------- + +Direct Action +~~~~~~~~~~~~~ + +Plugins can attach to each hook in two different ways. The default is +to have a function run and do some work directly. For example, the +``initialize()`` function for the user scripts plugin creates default +user scripts when ``virtualenvwrapper.sh`` is loaded. + +:: + + def initialize(args): + for filename, comment in GLOBAL_HOOKS: + make_hook(os.path.join('$WORKON_HOME', filename), comment) + return + +.. _plugins-user-env: + +Modifying the User Environment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are cases where the extension needs to update the user's +environment (e.g., changing the current working directory or setting +environment variables). Modifications to the user environment must be +made within the user's current shell, and cannot be run in a separate +process. To have code run in the user's shell process, extensions can +define hook functions to return the text of the shell statements to be +executed. These *source* hooks are run after the regular hooks with +the same name, and should not do any work of their own. + +The ``initialize_source()`` hook for the user scripts plugin looks for +a global initialize script and causes it to be run in the current +shell process. + +:: + + def initialize_source(args): + return """ + # + # Run user-provided scripts + # + [ -f "$WORKON_HOME/initialize" ] && source "$WORKON_HOME/initialize" + """ + +.. warning:: + + Because the extension is modifying the user's working shell, care + must be taken not to corrupt the environment by overwriting + existing variable values unexpectedly. Avoid creating temporary + variables where possible, and use unique names where variables + cannot be avoided. Prefixing variables with the extension name is + a good way to manage the namespace. For example, instead of + ``temp_file`` use ``user_scripts_temp_file``. Use ``unset`` to + release temporary variable names when they are no longer needed. + +.. warning:: + + virtualenvwrapper works under several shells with slightly + different syntax (bash, sh, zsh). Take this portability into + account when defining source hooks. Sticking to the simplest + possible syntax usually avoids problems, but there may be cases + where examining the ``SHELL`` environment variable to generate + different syntax for each case is the only way to achieve the + desired result. + +Registering Entry Points +------------------------ + +The functions defined in the plugin need to be registered as *entry +points* in order for virtualenvwrapper's hook loader to find them. +Entry points are configured in the packaging instructions for your +package by mapping the entry point name to the function in the package +that implements it. + +This partial copy of virtualenvwrapper's ``pyproject.toml`` illustrates how +the ``initialize()`` and ``initialize_source()`` entry points are +configured. + +:: + + [project.entry-points."virtualenvwrapper.initialize"] + user_scripts = "virtualenvwrapper.user_scripts:initialize" + project = "virtualenvwrapper.project:initialize" + + [project.entry-points."virtualenvwrapper.initialize_source"] + user_scripts = "virtualenvwrapper.user_scripts:initialize_source" + +Each entry points section maps the *group names* to lists of entry +point specifiers. A different group name is defined by +virtualenvwrapper for each extension point (see +:ref:`plugins-extension-points`). + +The entry point specifiers are strings with the syntax ``name = +"package.module:function"``. By convention, the *name* of each entry +point is the plugin name, but that is not required (the names are not +used). + +.. seealso:: + + * `namespace packages `__ + +The Hook Loader +--------------- + +Extensions are run through a command line application implemented in +``virtualenvwrapper.hook_loader``. Because ``virtualenvwrapper.sh`` +is the primary caller and users do not typically need to run the app +directly, no separate script is installed. Instead, to run the +application, use the ``-m`` option to the interpreter:: + + $ python -m virtualenvwrapper.hook_loader -h + Usage: virtualenvwrapper.hook_loader [options] [] + + Manage hooks for virtualenvwrapper + + Options: + -h, --help show this help message and exit + -s, --source Print the shell commands to be run in the current + shell + -l, --list Print a list of the plugins available for the given + hook + -v, --verbose Show more information on the console + -q, --quiet Show less information on the console + -n NAMES, --name=NAMES + Only run the hook from the named plugin + +To run the extensions for the initialize hook:: + + $ python -m virtualenvwrapper.hook_loader -v initialize + +To get the shell commands for the initialize hook:: + + $ python -m virtualenvwrapper.hook_loader --source initialize + +In practice, rather than invoking the hook loader directly it is more +convenient to use the shell function, ``virtualenvwrapper_run_hook`` +to run the hooks in both modes.:: + + $ virtualenvwrapper_run_hook initialize + +All of the arguments given to shell function are passed directly to +the hook loader. + +Logging +------- + +The hook loader configures logging so that messages are written to +``$WORKON_HOME/hook.log``. Messages also may be written to stderr, +depending on the verbosity flag. The default is for messages at *info* +or higher levels to be written to stderr, and *debug* or higher to go to +the log file. Using logging in this way provides a convenient +mechanism for users to control the verbosity of extensions. + +To use logging from within your extension, simply instantiate a logger +and call its ``info()``, ``debug()`` and other methods with the +messages. + +:: + + import logging + log = logging.getLogger(__name__) + + def pre_mkvirtualenv(args): + log.debug('pre_mkvirtualenv %s', str(args)) + # ... + +.. seealso:: + + * `Standard library documentation for logging `__ + * `PyMOTW for logging `__ + +.. _plugins-extension-points: + +Extension Points +================ + +The extension point names for native plugins follow a naming +convention with several parts: +``virtualenvwrapper.(pre|post)_[_source]``. The ** is +the action taken by the user or virtualenvwrapper that triggers the +extension. ``(pre|post)`` indicates whether to call the extension +before or after the event. The suffix ``_source`` is added for +extensions that return shell code instead of taking action directly +(see :ref:`plugins-user-env`). + +.. _plugins-get_env_details: + +get_env_details +=============== + +The ``virtualenvwrapper.get_env_details`` hooks are run when +``workon`` is run with no arguments and a list of the virtual +environments is printed. The hook is run once for each environment, +after the name is printed, and can be used to show additional +information about that environment. + +.. _plugins-initialize: + +initialize +---------- + +The ``virtualenvwrapper.initialize`` hooks are run each time +``virtualenvwrapper.sh`` is loaded into the user's environment. The +initialize hook can be used to install templates for configuration +files or otherwise prepare the system for proper plugin operation. + +.. _plugins-pre_mkvirtualenv: + +pre_mkvirtualenv +---------------- + +The ``virtualenvwrapper.pre_mkvirtualenv`` hooks are run after the +virtual environment is created, but before the new environment is +activated. The current working directory for when the hook is run is +``$WORKON_HOME`` and the name of the new environment is passed as an +argument. + +.. _plugins-post_mkvirtualenv: + +post_mkvirtualenv +----------------- + +The ``virtualenvwrapper.post_mkvirtualenv`` hooks are run after a new +virtual environment is created and activated. ``$VIRTUAL_ENV`` is set +to point to the new environment. + +.. _plugins-pre_activate: + +pre_activate +------------ + +The ``virtualenvwrapper.pre_activate`` hooks are run just before an +environment is enabled. The environment name is passed as the first +argument. + +.. _plugins-post_activate: + +post_activate +------------- + +The ``virtualenvwrapper.post_activate`` hooks are run just after an +environment is enabled. ``$VIRTUAL_ENV`` is set to point to the +current environment. + +.. _plugins-pre_deactivate: + +pre_deactivate +-------------- + +The ``virtualenvwrapper.pre_deactivate`` hooks are run just before an +environment is disabled. ``$VIRTUAL_ENV`` is set to point to the +current environment. + +.. _plugins-post_deactivate: + +post_deactivate +--------------- + +The ``virtualenvwrapper.post_deactivate`` hooks are run just after an +environment is disabled. The name of the environment just deactivated +is passed as the first argument. + +.. _plugins-pre_rmvirtualenv: + +pre_rmvirtualenv +---------------- + +The ``virtualenvwrapper.pre_rmvirtualenv`` hooks are run just before +an environment is deleted. The name of the environment being deleted +is passed as the first argument. + +.. _plugins-post_rmvirtualenv: + +post_rmvirtualenv +----------------- + +The ``virtualenvwrapper.post_rmvirtualenv`` hooks are run just after +an environment is deleted. The name of the environment being deleted +is passed as the first argument. + +Adding New Extension Points +=========================== + +Plugins that define new operations can also define new extension +points. No setup needs to be done to allow the hook loader to find +the extensions; documenting the names and adding calls to +``virtualenvwrapper_run_hook`` is sufficient to cause them to be +invoked. + +The hook loader assumes all extension point names start with +``virtualenvwrapper.`` and new plugins will want to use their own +namespace qualifier to append to that. For example, the project_ +extension defines new events around creating project directories (pre +and post). These are called +``virtualenvwrapper.project.pre_mkproject`` and +``virtualenvwrapper.project.post_mkproject``. These are invoked +with:: + + virtualenvwrapper_run_hook project.pre_mkproject $project_name + +and:: + + virtualenvwrapper_run_hook project.post_mkproject + +respectively. + +.. _Setuptools entry points: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points + +.. _project: https://github.com/python-virtualenvwrapper/virtualenvwrapper diff --git a/docs/source/projects.rst b/docs/source/projects.rst new file mode 100644 index 0000000..d8e5c84 --- /dev/null +++ b/docs/source/projects.rst @@ -0,0 +1,55 @@ +.. _project-management: + +==================== + Project Management +==================== + +A :term:`project directory` is associated with a virtualenv, but +usually contains the source code under active development rather than +the installed components needed to support the development. For +example, the project directory may contain the source code checked out +from a version control system, temporary artifacts created by testing, +experimental files not committed to version control, etc. + +A project directory is created and bound to a virtualenv when +:ref:`command-mkproject` is run instead of +:ref:`command-mkvirtualenv`. To bind an existing project directory to +a virtualenv, use :ref:`command-setvirtualenvproject`. + +Using Templates +=============== + +A new project directory can be created empty, or populated using one +or more :term:`template` plugins. Templates should be specified as +arguments to :ref:`command-mkproject`. Multiple values can be provided +to apply more than one template. For example, to check out a Mercurial +repository from a project on BitBucket and create a new Django +site, combine the :ref:`templates-bitbucket` and +:ref:`templates-django` templates. + +:: + + $ mkproject -t bitbucket -t django my_site + +Project Hook Files +================== + +The project directory can include additional hook files for the +`postactivate` and `predeactivate` hooks. Placing hook scripts in the +project hook directory, `.virtualenvwrapper`, allows them to be +checked into version control and shared more easily. + +When the :ref:`scripts-postactivate` hook runs, it looks for +`.virtualenvwrapper/postactivate` within the project directory and if +it is found it sources the file. + +When the :ref:`scripts-predeactivate` hook runs, it looks for +`.virtualenvwrapper/predeactivate` within the project directory and if +it is found it sources the file. + +.. seealso:: + + * :ref:`extensions-templates` + * :ref:`variable-PROJECT_HOME` + * :ref:`variable-VIRTUALENVWRAPPER_PROJECT_FILENAME` + * :ref:`variable-VIRTUALENVWRAPPER_WORKON_CD` diff --git a/docs/source/scripts.rst b/docs/source/scripts.rst new file mode 100644 index 0000000..30f4863 --- /dev/null +++ b/docs/source/scripts.rst @@ -0,0 +1,255 @@ +.. _scripts: + +======================== + Per-User Customization +======================== + +The end-user customization scripts are either *sourced* (allowing them +to modify your shell environment) or *run* as an external program at +the appropriate trigger time. + +The global scripts applied to all environments should be placed in the +directory named by :ref:`VIRTUALENVWRAPPER_HOOK_DIR +`, which by default will be equal +to :ref:`WORKON_HOME `. The local scripts should be +placed in the ``bin`` directory of the virtualenv. + +Example Usage +=============== + +As a Django developer, you likely want DJANGO_SETTINGS_MODULE to be set, and +if you work on multiple projects, you want it to be specific to the project +you are currently working on. Wouldn't it be nice if it was set based on the +active virtualenv? You can achieve this with :ref:`scripts` as follows. + +If your :ref:`WORKON_HOME ` is set to ~/.virtualenvs:: + + vim ~/.virtualenvs/premkvirtualenv + +Edit the file so it contains the following (for a default Django setup):: + + # Automatically set django settings for the virtualenv + echo "export DJANGO_SETTINGS_MODULE=$1.settings" >> "$1/bin/activate" + +Create a new virtualenv, and you should see DJANGO_SETTINGS_MODULE in your env! + +.. _scripts-get_env_details: + +get_env_details +=============== + + :Global/Local: both + :Argument(s): env name + :Sourced/Run: run + +``$VIRTUALENVWRAPPER_HOOK_DIR/get_env_details`` is run when ``workon`` is run with no +arguments and a list of the virtual environments is printed. The hook +is run once for each environment, after the name is printed, and can +print additional information about that environment. + +.. _scripts-initialize: + +initialize +========== + + :Global/Local: global + :Argument(s): None + :Sourced/Run: sourced + +``$VIRTUALENVWRAPPER_HOOK_DIR/initialize`` is sourced when ``virtualenvwrapper.sh`` +is loaded into your environment. Use it to adjust global settings +when virtualenvwrapper is enabled. + +.. _scripts-premkvirtualenv: + +premkvirtualenv +=============== + + :Global/Local: global + :Argument(s): name of new environment + :Sourced/Run: run + +``$VIRTUALENVWRAPPER_HOOK_DIR/premkvirtualenv`` is run as an external program after +the virtual environment is created but before the current environment +is switched to point to the new env. The current working directory for +the script is ``$WORKON_HOME`` and the name of the new environment is +passed as an argument to the script. + +.. _scripts-postmkvirtualenv: + +postmkvirtualenv +================ + + :Global/Local: global + :Argument(s): none + :Sourced/Run: sourced + +``$VIRTUALENVWRAPPER_HOOK_DIR/postmkvirtualenv`` is sourced after the new environment +is created and activated. If the ``-a`` flag was used, +the link to the project directory is set up before this script is sourced. + +.. _scripts-precpvirtualenv: + +precpvirtualenv +=============== + + :Global/Local: global + :Argument(s): name of original environment, name of new environment + :Sourced/Run: run + +``$VIRTUALENVWRAPPER_HOOK_DIR/precpvirtualenv`` is run as an external program after +the source environment is duplicated and made relocatable, but before +the ``premkvirtualenv`` hook is run or the current environment is +switched to point to the new env. The current working directory for +the script is ``$WORKON_HOME`` and the names of the source and new +environments are passed as arguments to the script. + +.. _scripts-postcpvirtualenv: + +postcpvirtualenv +================ + + :Global/Local: global + :Argument(s): none + :Sourced/Run: sourced + +``$VIRTUALENVWRAPPER_HOOK_DIR/postcpvirtualenv`` is sourced after the new environment +is created and activated. + +.. _scripts-preactivate: + +preactivate +=========== + + :Global/Local: global, local + :Argument(s): environment name + :Sourced/Run: run + +The global ``$VIRTUALENVWRAPPER_HOOK_DIR/preactivate`` script is run before the new +environment is enabled. The environment name is passed as the first +argument. + +The local ``$VIRTUAL_ENV/bin/preactivate`` hook is run before the new +environment is enabled. The environment name is passed as the first +argument. + +.. _scripts-postactivate: + +postactivate +============ + + :Global/Local: global, local + :Argument(s): none + :Sourced/Run: sourced + +The global ``$VIRTUALENVWRAPPER_HOOK_DIR/postactivate`` script is sourced after the +new environment is enabled. ``$VIRTUAL_ENV`` refers to the new +environment at the time the script runs. + +This example script adds a space between the virtual environment name +and your old PS1 by making use of ``_OLD_VIRTUAL_PS1``. + +:: + + PS1="(`basename \"$VIRTUAL_ENV\"`) $_OLD_VIRTUAL_PS1" + +The local ``$VIRTUAL_ENV/bin/postactivate`` script is sourced after +the new environment is enabled. ``$VIRTUAL_ENV`` refers to the new +environment at the time the script runs. + +This example script for the PyMOTW environment changes the current +working directory and the PATH variable to refer to the source tree +containing the PyMOTW source. + +:: + + pymotw_root=/Users/dhellmann/Documents/PyMOTW + cd $pymotw_root + PATH=$pymotw_root/bin:$PATH + +.. _scripts-predeactivate: + +predeactivate +============= + + :Global/Local: local, global + :Argument(s): none + :Sourced/Run: sourced + +The local ``$VIRTUAL_ENV/bin/predeactivate`` script is sourced before the +current environment is deactivated, and can be used to disable or +clear settings in your environment. ``$VIRTUAL_ENV`` refers to the old +environment at the time the script runs. + +The global ``$VIRTUALENVWRAPPER_HOOK_DIR/predeactivate`` script is sourced before the +current environment is deactivated. ``$VIRTUAL_ENV`` refers to the +old environment at the time the script runs. + +.. _scripts-postdeactivate: + +postdeactivate +============== + + :Global/Local: local, global + :Argument(s): none + :Sourced/Run: sourced + +The ``$VIRTUAL_ENV/bin/postdeactivate`` script is sourced after the +current environment is deactivated, and can be used to disable or +clear settings in your environment. The path to the environment just +deactivated is available in ``$VIRTUALENVWRAPPER_LAST_VIRTUALENV``. + +.. _scripts-prermvirtualenv: + +prermvirtualenv +=============== + + :Global/Local: global + :Argument(s): environment name + :Sourced/Run: run + +The ``$VIRTUALENVWRAPPER_HOOK_DIR/prermvirtualenv`` script is run as an external +program before the environment is removed. The full path to the +environment directory is passed as an argument to the script. + +.. _scripts-postrmvirtualenv: + +postrmvirtualenv +================ + + :Global/Local: global + :Argument(s): environment name + :Sourced/Run: run + +The ``$VIRTUALENVWRAPPER_HOOK_DIR/postrmvirtualenv`` script is run as an external +program after the environment is removed. The full path to the +environment directory is passed as an argument to the script. + +.. _scripts-premkproject: + +premkproject +============ + + :Global/Local: global + :Argument(s): name of new project + :Sourced/Run: run + +``$WORKON_HOME/premkproject`` is run as an external program after the +virtual environment is created and after the current environment is +switched to point to the new env, but before the new project directory +is created. The current working directory for the script is +``$PROJECT_HOME`` and the name of the new project is passed as an +argument to the script. + +.. _scripts-postmkproject: + +postmkproject +============= + + :Global/Local: global + :Argument(s): none + :Sourced/Run: sourced + +``$WORKON_HOME/postmkproject`` is sourced after the new environment +and project directories are created and the virtualenv is activated. +The current working directory is the project directory. diff --git a/docs/source/tips.rst b/docs/source/tips.rst new file mode 100644 index 0000000..aec51f5 --- /dev/null +++ b/docs/source/tips.rst @@ -0,0 +1,196 @@ +.. _tips-and-tricks: + +================= + Tips and Tricks +================= + +This is a list of user-contributed tips for making virtualenv and +virtualenvwrapper even more useful. If you have tip to share, drop me +an email or post a comment on `this blog post +`__ +and I'll add it here. + +Enhanced bash/zsh Prompt +======================== + +Via `Stephan Sokolow `_ + +While the virtualenv ``activate`` script does attempt to provide +an indicator in the prompt, it has various shortcomings, and +cannot be customized. + +However, it does also set a shell variable named +``VIRTUAL_ENV`` which can be used as the basis for disabling the +built-in prompt indicator and substituting an improved one, +as a customization to ``.bashrc`` or ``.zshrc``:: + + virtualenv_prompt() { + # If not in a virtualenv, print nothing + [[ "$VIRTUAL_ENV" == "" ]] && return + + # Distinguish between the shell where the virtualenv was activated + # and its children + local venv_name="${VIRTUAL_ENV##*/}" + if typeset -f deactivate >/dev/null; then + echo "[${venv_name}] " + else + echo "<${venv_name}> " + fi + } + + # Display a "we are in a virtualenv" indicator that works in child shells too + VIRTUAL_ENV_DISABLE_PROMPT=1 + PS1='$(virtualenv_prompt)'"$PS1" + +This basic example works in both bash and zsh and has the following +advantages: + +1. It will also display in sub-shells, because it works by having the + shell detect an active virtualenv, rather than by having the ``activate`` + script modify the prompt for just the current shell instance. +2. It will clearly indicate if you're in a subshell, where the + virtualenv will still apply, but the ``deactivate`` command will be + missing. + +However, if you are using zsh, a better example of what the design +is capable of can be constructed by taking advantage of zsh's built-in +support for easily adding color and right-aligned segments to prompts:: + + zsh_virtualenv_prompt() { + # If not in a virtualenv, print nothing + [[ "$VIRTUAL_ENV" == "" ]] && return + + # Distinguish between the shell where the virtualenv was activated + # and its children + local venv_name="${VIRTUAL_ENV##*/}" + if typeset -f deactivate >/dev/null; then + echo "[%F{green}${venv_name}%f] " + else + echo "<%F{green}${venv_name}%f> " + fi + } + + setopt PROMPT_SUBST PROMPT_PERCENT + + # Display a "we are in a virtualenv" indicator that works in child shells too + VIRTUAL_ENV_DISABLE_PROMPT=1 + RPS1='$(zsh_virtualenv_prompt)' + +Updating cached ``$PATH`` entries +================================= + +From Nat (was blogger.com/profile/16779944428406910187): + +I also added the command 'rehash' to ``$WORKON_HOME/postactivate`` and +``$WORKON_HOME/postdeactivate`` as I was having some problems with zsh +not picking up the new paths immediately. + +Creating Project Work Directories +================================= + +Via `James `_: + +In the ``postmkvirtualenv`` script I have the following to create a +directory based on the project name, add that directory to the python +path and then cd into it:: + + proj_name=$(basename $VIRTUAL_ENV) + mkdir $HOME/projects/$proj_name + add2virtualenv $HOME/projects/$proj_name + cd $HOME/projects/$proj_name + + +In the ``postactivate`` script I have it set to automatically change +to the project directory when I use the workon command:: + + proj_name=$(basename $VIRTUAL_ENV) + cd ~/projects/$proj_name + +Automatically Run workon When Entering a Directory +================================================== + +`Justin Abrahms posted +`__ +about some code he added to his shell environment to look at the +directory each time he runs ``cd``. If it finds a ``.venv`` file, it +activates the environment named within. On leaving that directory, +the current virtualenv is automatically deactivated. + +Installing Common Tools Automatically in New Environments +========================================================= + +Via rizumu (was rizumu.myopenid.com): + +I have this ``postmkvirtualenv`` to install the get a basic setup. + +:: + + $ cat postmkvirtualenv + #!/usr/bin/env bash + curl -O http://python-distribute.org/distribute_setup.p... />python distribute_setup.py + rm distribute_setup.py + easy_install pip==dev + pip install Mercurial + +Then I have a pip requirement file with my dev tools. + +:: + + $ cat developer_requirements.txt + ipdb + ipython + pastescript + nose + http://douglatornell.ca/software/python/Nosy-1.0.tar.gz + coverage + sphinx + grin + pyflakes + pep8 + +Then each project has it's own pip requirement file for things like +PIL, psycopg2, django-apps, numpy, etc. + +Changing the Default Behavior of ``cd`` +======================================= + +Via `mae `__: + +This is supposed to be executed after workon, that is as a +``postactivate`` hook. It basically overrides ``cd`` to know about the +VENV so instead of doing ``cd`` to go to ``~`` you will go to the venv +root, IMO very handy and I can't live without it anymore. If you pass +it a proper path then it will do the right thing. + +:: + + cd () { + if (( $# == 0 )) + then + builtin cd $VIRTUAL_ENV + else + builtin cd "$@" + fi + } + + cd + +And to finally restore the default behaviour of ``cd`` once you +bailout of a VENV via a ``deactivate`` command, you need to add this +as a ``postdeactivate`` hook:: + + unset -f cd + +Clean up environments on exit +======================================= + +Via `Michael `__: + +When you use a temporary virtualenv via ``mktmpenv`` or if you have a +:ref:`plugins-post_deactivate` hook, you have to actually run +``deactivate`` to clean up the temporary environment or run the hook, +respectively. It's easy to forget and just exit the shell. Put the +following in ``~/.bash_logout`` (or your shell's equivalent file) to +always deactivate environments before exiting the shell:: + + [ "$VIRTUAL_ENV" ] && deactivate diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt new file mode 100644 index 0000000..89ea196 --- /dev/null +++ b/docs/spelling_wordlist.txt @@ -0,0 +1,7 @@ +Hellmann +Bicking +virtualenvwrapper +mkvirtualenv +rmvirtualenv +virtualenv +stderr diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8223276 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,103 @@ +[build-system] +requires = ["setuptools", "setuptools_scm[toml]>=6.2"] +build-backend = "setuptools.build_meta" + +[project] +authors = [ + {name = "Doug Hellmann", email = "doug@doughellmann.com"}, + {name = "Jason Myers", email = "jason@mailthemyers.com"}, +] + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Intended Audience :: Developers", + "Environment :: Console", +] + +name = "virtualenvwrapper" +description = "" +dynamic = ["version"] +keywords = ["virtualenv"] +license = {text = "MIT"} +readme = "README.txt" +requires-python = ">=3.8" + +dependencies = [ + "virtualenv", + "virtualenv-clone", + "stevedore", +] + +[project.optional-dependencies] +linter = [ + "flake8", +] +build = [ + "build", + "twine", + "check-python-versions", +] + +# https://github.com/pypa/setuptools_scm/ +[tool.setuptools_scm] +write_to = "virtualenvwrapper/version.py" + +[project.urls] +homepage = "https://virtualenvwrapper.readthedocs.io/" +repository = "https://github.com/python-virtualenvwrapper/virtualenvwrapper" + +[project.entry-points."virtualenvwrapper.initialize"] +user_scripts = "virtualenvwrapper.user_scripts:initialize" +project = "virtualenvwrapper.project:initialize" + +[project.entry-points."virtualenvwrapper.initialize_source"] +user_scripts = "virtualenvwrapper.user_scripts:initialize_source" + +[project.entry-points."virtualenvwrapper.pre_mkvirtualenv"] +user_scripts = "virtualenvwrapper.user_scripts:pre_mkvirtualenv" + +[project.entry-points."virtualenvwrapper.post_mkvirtualenv_source"] +user_scripts = "virtualenvwrapper.user_scripts:post_mkvirtualenv_source" + +[project.entry-points."virtualenvwrapper.pre_cpvirtualenv"] +user_scripts = "virtualenvwrapper.user_scripts:pre_cpvirtualenv" + +[project.entry-points."virtualenvwrapper.post_cpvirtualenv_source"] +user_scripts = "virtualenvwrapper.user_scripts:post_cpvirtualenv_source" + +[project.entry-points."virtualenvwrapper.pre_rmvirtualenv"] +user_scripts = "virtualenvwrapper.user_scripts:pre_rmvirtualenv" + +[project.entry-points."virtualenvwrapper.post_rmvirtualenv"] +user_scripts = "virtualenvwrapper.user_scripts:post_rmvirtualenv" + +[project.entry-points."virtualenvwrapper.project.pre_mkproject"] +project = "virtualenvwrapper.project:pre_mkproject" + +[project.entry-points."virtualenvwrapper.project.post_mkproject_source"] +project = "virtualenvwrapper.project:post_mkproject_source" + +[project.entry-points."virtualenvwrapper.pre_activate"] +user_scripts = "virtualenvwrapper.user_scripts:pre_activate" + +[project.entry-points."virtualenvwrapper.post_activate_source"] +project = "virtualenvwrapper.project:post_activate_source" +user_scripts = "virtualenvwrapper.user_scripts:post_activate_source" + +[project.entry-points."virtualenvwrapper.pre_deactivate_source"] +project = "virtualenvwrapper.project:pre_deactivate_source" +user_scripts = "virtualenvwrapper.user_scripts:pre_deactivate_source" + +[project.entry-points."virtualenvwrapper.post_deactivate_source"] +user_scripts = "virtualenvwrapper.user_scripts:post_deactivate_source" + +[project.entry-points."virtualenvwrapper.get_env_details"] +user_scripts = "virtualenvwrapper.user_scripts:get_env_details" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..bb5e197 --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +from setuptools import setup + +setup( + # Listing the scripts in pyproject.toml requires them to be python + # entry points for console scripts, but they are shell scripts. + scripts=[ + "virtualenvwrapper.sh", + "virtualenvwrapper_lazy.sh", + ], +) diff --git a/setup.py.in b/setup.py.in deleted file mode 100644 index 992f50d..0000000 --- a/setup.py.in +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python -# -# $Id: setup.py 120 2003-04-23 12:18:54Z doughellmann $ -# -# Time-stamp: <06/12/31 12:03:26 dhellmann> -# -# Copyright 2001 Doug Hellmann. -# -# -# All Rights Reserved -# -# Permission to use, copy, modify, and distribute this software and -# its documentation for any purpose and without fee is hereby -# granted, provided that the above copyright notice appear in all -# copies and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of Doug -# Hellmann not be used in advertising or publicity pertaining to -# distribution of the software without specific, written prior -# permission. -# -# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, -# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN -# NO EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR -# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -"""Distutils setup file for Proctor - -""" - -# -# Import system modules -# -from distutils.core import setup -import os - -# -# Import Local modules -# - -# -# Module -# - -long_description = open('README', 'rt').read() - -setup ( - name = 'virtualenvwrapper', - version = 'VERSION', - - description = 'Enhancements to virtualenv', - long_description = long_description, - - author = 'Doug Hellmann', - author_email = 'doug.hellmann@gmail.com', - - url = 'http://www.doughellmann.com/projects/virtualenvwrapper/', - download_url = 'http://www.doughellmann.com/downloads/Proctor-VERSION.tar.gz', - - classifiers = [ 'Development Status :: 5 - Production/Stable', - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python', - 'Intended Audience :: Developers', - 'Environment :: Console', - ], - - platforms = ('Any',), - - scripts = ['virtualenvwrapper_bashrc', - ], - - provides=['virtualenvwrapper', - ], - requires=['virtualenv'], - - data_files=[('docs', ['README.html']), - ], - ) - diff --git a/tests/manual_test_install.sh b/tests/manual_test_install.sh new file mode 100755 index 0000000..317d958 --- /dev/null +++ b/tests/manual_test_install.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# +# Test installation of virtualenvwrapper in a new virtualenv. +# + +test_dir=$(dirname $0) +source "$test_dir/../virtualenvwrapper.sh" + +export WORKON_HOME="${TMPDIR:-/tmp}/WORKON_HOME" + +VERSION=$(python setup.py --version) + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +test_build_ok () { + (cd "$test_dir/.." && make sdist) + outcome=$? + assertSame "0" "$outcome" +} + +test_install () { + dist_dir=$(dirname $test_dir)/dist + pip install "$dist_dir/virtualenvwrapper-$VERSION.tar.gz" + RC=$? + assertTrue "Error code $RC" "[ $RC -eq 0 ]" + assertTrue "Missing wrapper script" "[ -f $WORKON_HOME/installtest/bin/virtualenvwrapper.sh ]" +} + +. "$test_dir/shunit2" diff --git a/tests/run_tests b/tests/run_tests new file mode 100755 index 0000000..a29179b --- /dev/null +++ b/tests/run_tests @@ -0,0 +1,82 @@ +# -*- mode: shell-script -*- +#set -x + +envdir="$(cd $1 && pwd)" +shift +scripts="$*" +if [ -z "$scripts" ] +then + scripts=$(ls tests/test*.sh) + if [ -z "$scripts" ] + then + echo "Could not find any test scripts to run" 1>&2 + exit 1 + fi +fi + +# Override FAIL_FAST to true to exit as soon as any test fails +FAIL_FAST=${FAIL_FAST:-false} + +# Force the tox virtualenv to be active. +# +# Since this script runs from within a separate shell created by tox, +# the name of the virtualenv (in $VIRTUAL_ENV) is inherited, but the +# shell functions and other settings created by the activate script +# are *not* inherited. +# +source "$envdir/bin/activate" +TMPDIR="$envdir/tmp" +export TMPDIR +mkdir -p "$TMPDIR" + +# Set up virtualenvwrapper.hook_loader to print more details than usual for debugging. +#export HOOK_VERBOSE_OPTION=-vvv +HOOK_VERBOSE_OPTION="-q" +export HOOK_VERBOSE_OPTION + +# Force virtualenvwrapper to use the python interpreter in the +# tox-created virtualenv. +VIRTUALENVWRAPPER_PYTHON="$envdir/bin/python3" +export VIRTUALENVWRAPPER_PYTHON + +# Clear any user settings for the hook directory or log directory +unset VIRTUALENVWRAPPER_HOOK_DIR +unset VIRTUALENVWRAPPER_LOG_DIR +unset VIRTUALENVWRAPPER_VIRTUALENV +unset VIRTUALENVWRAPPER_VIRTUALENV_ARGS + +# Run the test scripts with a little formatting around them to make it +# easier to find where each script output starts. +RC=0 +for test_script in $scripts +do + + echo + echo '********************************************************************************' + echo "Running $SHELL $test_shell_opts $test_script" + echo " VIRTUAL_ENV=$VIRTUAL_ENV" + echo " VIRTUALENVWRAPPER_PYTHON=$VIRTUALENVWRAPPER_PYTHON" + echo " $($VIRTUALENVWRAPPER_PYTHON -V 2>&1)" + echo " PYTHONPATH=$PYTHONPATH" + echo " SHELL=$SHELL" + echo " BASH_VERSION=$BASH_VERSION" + echo " ZSH_VERSION=$ZSH_VERSION" + echo " virtualenv=$(which virtualenv)" + echo " test_shell_opts=$test_shell_opts" + echo " ZSH=$ZSH_NAME $ZSH_EVAL_CONTEXT" + echo " TMPDIR=$TMPDIR" + + echo + SHUNIT_PARENT="$test_script" + export SHUNIT_PARENT + if ! $SHELL $test_shell_opts $test_script; then + RC=1 + if $FAIL_FAST; then + exit $RC + fi + fi + echo + +done + +exit $RC diff --git a/tests/setup.sh b/tests/setup.sh new file mode 100755 index 0000000..6ecd404 --- /dev/null +++ b/tests/setup.sh @@ -0,0 +1,28 @@ +# Setup globals used by the tests + +#set -x + +# tmplocation=${TMPDIR:-/tmp} +# export WORKON_HOME="$(echo ${tmplocation}/WORKON_HOME.$$ | sed 's|//|/|g')" +# export PROJECT_HOME="$(echo ${tmplocation}/PROJECT_HOME.$$ | sed 's|//|/|g')" + +export WORKON_HOME=$(mktemp -d -t "WORKON_HOME.XXXX.$$") +export PROJECT_HOME=$(mktemp -d -t "PROJECT_HOME.XXXX.$$") + +#unset HOOK_VERBOSE_OPTION + +SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# This should point to VIRTUAL_ENV/bin when running under tox. +TEST_BIN_DIR=$(dirname $(which python)) + +load_wrappers() { + if [ "$USING_TOX" = "1" ]; then + # Use the version of the scripts installed as part of the + # package. + source "$TEST_BIN_DIR/virtualenvwrapper.sh" + else + echo "USING SOURCE VERSION OF SCRIPT" + source "$SCRIPTDIR/../virtualenvwrapper.sh" + fi +} diff --git a/tests/shunit2 b/tests/shunit2 new file mode 100644 index 0000000..d900a70 --- /dev/null +++ b/tests/shunit2 @@ -0,0 +1,1116 @@ +# $Id: shunit2 277 2008-10-29 21:20:22Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# vim:foldmethod=marker:foldmarker=/**,*/ +# +#/** +# +# +# +# shUnit 2.1.5 +# Shell Unit Test Framework +# +# http://shunit2.googlecode.com/ +# +# written by Kate Ward <kate.ward@forestent.com> +# released under the LGPL +# +# This module implements a xUnit based unit test framework similar to JUnit. +# +#*/ + +SHUNIT_VERSION='2.1.5' + +SHUNIT_TRUE=0 +SHUNIT_FALSE=1 +SHUNIT_ERROR=2 + +_shunit_warn() { echo "shunit2:WARN $@" >&2; } +_shunit_error() { echo "shunit2:ERROR $@" >&2; } +_shunit_fatal() { echo "shunit2:FATAL $@" >&2; } + +# specific shell checks +if [ -n "${ZSH_VERSION:-}" ]; then + setopt |grep "^shwordsplit$" >/dev/null + if [ $? -ne ${SHUNIT_TRUE} ]; then + _shunit_fatal 'zsh shwordsplit option is required for proper operation' + exit ${SHUNIT_ERROR} + fi + if [ -z "${SHUNIT_PARENT:-}" ]; then + _shunit_fatal "zsh does not pass \$0 through properly. please declare \ +\"SHUNIT_PARENT=\$0\" before calling shUnit2" + exit ${SHUNIT_ERROR} + fi +fi + +# +# constants +# + +__SHUNIT_ASSERT_MSG_PREFIX='ASSERT:' +__SHUNIT_PARENT=${SHUNIT_PARENT:-$0} + +# set the constants readonly +shunit_constants_=`set |grep '^__SHUNIT_' |cut -d= -f1` +echo "${shunit_constants_}" |grep '^Binary file' >/dev/null \ + && shunit_constants_=`set |grep -a '^__SHUNIT_' |cut -d= -f1` +for shunit_constant_ in ${shunit_constants_}; do + shunit_ro_opts_='' + case ${ZSH_VERSION:-} in + '') ;; # this isn't zsh + [123].*) ;; # early versions (1.x, 2.x, 3.x) + *) shunit_ro_opts_='-g' ;; # all later versions. declare readonly globally + esac + readonly ${shunit_ro_opts_} ${shunit_constant_} +done +unset shunit_constant_ shunit_constants_ shunit_ro_opts_ + +# variables +__shunit_skip=${SHUNIT_FALSE} +__shunit_suite='' + +# counts of tests +__shunit_testSuccess=${SHUNIT_TRUE} +__shunit_testsTotal=0 +__shunit_testsPassed=0 +__shunit_testsFailed=0 + +# counts of asserts +__shunit_assertsTotal=0 +__shunit_assertsPassed=0 +__shunit_assertsFailed=0 +__shunit_assertsSkipped=0 + +__shunit_lineno='' +__shunit_reportGenerated=${SHUNIT_FALSE} + +# macros +_SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi' + +#----------------------------------------------------------------------------- +# assert functions +# + +#/** +# +# +# void +# +# +# +# +# assertEquals +# string [message] +# string expected +# string actual +# +# +# Asserts that expected and +# actual are equal to one another. The message is +# optional. +# +# +#*/ +assertEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertEquals() requires two or three arguments; $# given" + _shunit_error "1: ${1:+$1} 2: ${2:+$2} 3: ${3:+$3}" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + shunit_return=${SHUNIT_TRUE} + if [ "${shunit_expected_}" = "${shunit_actual_}" ]; then + _shunit_assertPass + else + failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" + shunit_return=${SHUNIT_FALSE} + fi + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${shunit_return} +} +_ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# assertNotEquals +# string [message] +# string unexpected +# string actual +# +# +# Asserts that unexpected and +# actual are not +# equal to one another. The message is optional. +# +# +#*/ +assertNotEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertNotEquals() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_unexpected_=$1 + shunit_actual_=$2 + + shunit_return=${SHUNIT_TRUE} + if [ "${shunit_unexpected_}" != "${shunit_actual_}" ]; then + _shunit_assertPass + else + failSame "${shunit_message_}" "$@" + shunit_return=${SHUNIT_FALSE} + fi + + unset shunit_message_ shunit_unexpected_ shunit_actual_ + return ${shunit_return} +} +_ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# assertNull +# string [message] +# string value +# +# +# Asserts that value is null, +# or in shell terms a zero-length string. The message is optional. +# +# +#*/ +assertNull() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertNull() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + assertTrue "${shunit_message_}" "[ -z '$1' ]" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# assertNotNull +# string [message] +# string value +# +# +# Asserts that value is not null, or in shell terms not +# a zero-length string. The message is optional. +# +# +#*/ +assertNotNull() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null + _shunit_error "assertNotNull() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + assertTrue "${shunit_message_}" "[ -n '${1:-}' ]" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# assertSame +# string [message] +# string expected +# string actual +# +# +# This function is functionally equivalent to +# assertEquals. +# +# +#*/ +assertSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertSame() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + assertEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# assertNotSame +# string [message] +# string unexpected +# string actual +# +# +# Asserts that unexpected and +# actual are not +# equal to one another. The message is optional. +# +# +#*/ +assertNotSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertNotSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_:-}$1" + shift + fi + assertNotEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# assertTrue +# string [message] +# string condition +# +# +# Asserts that a given shell test condition is true. The message is +# optional. +# Testing whether something is true or false is easy enough by using +# the assertEquals/assertNotSame functions. Shell supports much more +# complicated tests though, and a means to support them was needed. As such, +# this function tests that conditions are true or false through evaluation +# rather than just looking for a true or false. +# +# The following test will succeed: assertTrue "[ 34 -gt 23 ]" +# The folloing test will fail with a message: assertTrue "test failed" "[ -r '/non/existant/file' ]" +# +# +# +#*/ +assertTrue() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 2 ]; then + _shunit_error "assertTrue() takes one two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_condition_=$1 + + # see if condition is an integer, i.e. a return value + shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` + shunit_return=${SHUNIT_TRUE} + if [ -z "${shunit_condition_}" ]; then + # null condition + shunit_return=${SHUNIT_FALSE} + elif [ "${shunit_condition_}" = "${shunit_match_}" ]; then + # possible return value. treating 0 as true, and non-zero as false. + [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE} + else + # (hopefully) a condition + ( eval ${shunit_condition_} ) >/dev/null 2>&1 + [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE} + fi + + # record the test + if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then + _shunit_assertPass + else + _shunit_assertFail "${shunit_message_}" + fi + + unset shunit_message_ shunit_condition_ shunit_match_ + return ${shunit_return} +} +_ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# assertFalse +# string [message] +# string condition +# +# +# Asserts that a given shell test condition is false. The message is +# optional. +# Testing whether something is true or false is easy enough by using +# the assertEquals/assertNotSame functions. Shell supports much more +# complicated tests though, and a means to support them was needed. As such, +# this function tests that conditions are true or false through evaluation +# rather than just looking for a true or false. +# +# The following test will succeed: assertFalse "[ 'apples' = 'oranges' ]" +# The folloing test will fail with a message: assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]" +# +# +# +#*/ +assertFalse() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertFalse() quires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_condition_=$1 + + # see if condition is an integer, i.e. a return value + shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` + shunit_return=${SHUNIT_TRUE} + if [ -z "${shunit_condition_}" ]; then + # null condition + shunit_return=${SHUNIT_FALSE} + elif [ "${shunit_condition_}" = "${shunit_match_}" ]; then + # possible return value. treating 0 as true, and non-zero as false. + [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE} + else + # (hopefully) a condition + ( eval ${shunit_condition_} ) >/dev/null 2>&1 + [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE} + fi + + # record the test + if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then + _shunit_assertPass + else + _shunit_assertFail "${shunit_message_}" + fi + + unset shunit_message_ shunit_condition_ shunit_match_ + return ${shunit_return} +} +_ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"' + +#----------------------------------------------------------------------------- +# failure functions +# + +#/** +# +# +# void +# +# +# +# +# fail +# string [message] +# +# +# Fails the test immediately, with the optional message. +# +# +#*/ +fail() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 1 ]; then + _shunit_error "fail() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 1 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + + _shunit_assertFail "${shunit_message_}" + + unset shunit_message_ + return ${SHUNIT_FALSE} +} +_FAIL_='eval fail --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# failNotEquals +# string [message] +# string unexpected +# string actual +# +# +# Fails the test if unexpected and +# actual are not +# equal to one another. The message is optional. +# +# +#*/ +failNotEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failNotEquals() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_unexpected_=$1 + shunit_actual_=$2 + + _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_unexpected_}> but was:<${shunit_actual_}>" + + unset shunit_message_ shunit_unexpected_ shunit_actual_ + return ${SHUNIT_FALSE} +} +_FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# failSame +# string [message] +# +# +# Indicate test failure because arguments were the same. The message is +# optional. +# +# +#*/ +failSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + + _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same" + + unset shunit_message_ + return ${SHUNIT_FALSE} +} +_FAIL_SAME_='eval failSame --lineno "${LINENO:-}"' + +#/** +# +# +# void +# +# +# +# +# failNotSame +# string [message] +# string expected +# string actual +# +# +# Indicate test failure because arguments were not the same. The +# message is optional. +# +# +#*/ +failNotSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failNotEquals() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + failNotEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"' + +#----------------------------------------------------------------------------- +# skipping functions +# + +#/** +# +# +# void +# +# +# +# +# startSkipping +# +# +# +# This function forces the remaining assert and fail functions to be +# "skipped", i.e. they will have no effect. Each function skipped will be +# recorded so that the total of asserts and fails will not be altered. +# +# +#*/ +startSkipping() +{ + __shunit_skip=${SHUNIT_TRUE} +} + +#/** +# +# +# void +# +# +# +# +# endSkipping +# +# +# +# This function returns calls to the assert and fail functions to their +# default behavior, i.e. they will be called. +# +# +#*/ +endSkipping() +{ + __shunit_skip=${SHUNIT_FALSE} +} + +#/** +# +# +# boolean +# +# +# +# +# isSkipping +# +# +# +# This function returns the state of skipping. +# +# +#*/ +isSkipping() +{ + return ${__shunit_skip} +} + +#----------------------------------------------------------------------------- +# suite functions +# + +#/** +# +# +# void +# +# +# +# +# suite +# +# +# +# This function can be optionally overridden by the user in their test +# suite. +# If this function exists, it will be called when +# shunit2 is sourced. If it does not exist, shUnit2 will +# search the parent script for all functions beginning with the word +# test, and they will be added dynamically to the test +# suite. +# +# +#*/ +# Note: see _shunit_mktempFunc() for actual implementation +# suite() { :; } + +#/** +# +# +# void +# +# +# +# +# suite_addTest +# string function +# +# +# This function adds a function name to the list of tests scheduled for +# execution as part of this test suite. This function should only be called +# from within the suite() function. +# +# +#*/ +suite_addTest() +{ + shunit_func_=${1:-} + + __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}" + __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1` + + unset shunit_func_ +} + +#/** +# +# +# void +# +# +# +# +# oneTimeSetUp +# +# +# +# This function can be be optionally overridden by the user in their +# test suite. +# If this function exists, it will be called once before any tests are +# run. It is useful to prepare a common environment for all tests. +# +# +#*/ +# Note: see _shunit_mktempFunc() for actual implementation +# oneTimeSetUp() { :; } + +#/** +# +# +# void +# +# +# +# +# oneTimeTearDown +# +# +# +# This function can be be optionally overridden by the user in their +# test suite. +# If this function exists, it will be called once after all tests are +# completed. It is useful to clean up the environment after all tests. +# +# +#*/ +# Note: see _shunit_mktempFunc() for actual implementation +# oneTimeTearDown() { :; } + +#/** +# +# +# void +# +# +# +# +# setUp +# +# +# +# This function can be be optionally overridden by the user in their +# test suite. +# If this function exists, it will be called before each test is run. +# It is useful to reset the environment before each test. +# +# +#*/ +# Note: see _shunit_mktempFunc() for actual implementation +# setUp() { :; } + +#/** +# +# +# void +# +# +# +# +# tearDown +# +# +# +# This function can be be optionally overridden by the user in their +# test suite. +# If this function exists, it will be called after each test completes. +# It is useful to clean up the environment after each test. +# +# +#*/ +# Note: see _shunit_mktempFunc() for actual implementation +# tearDown() { :; } + +#------------------------------------------------------------------------------ +# internal shUnit2 functions +# + +# this function is a cross-platform temporary directory creation tool. not all +# OSes have the mktemp function, so one is included here. +_shunit_mktempDir() +{ + # try the standard mktemp function + ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return + + # the standard mktemp didn't work. doing our own. + if [ -r '/dev/urandom' ]; then + _shunit_random_=`od -vAn -N4 -tx4 "${_shunit_file_}" +#! /bin/sh +exit ${SHUNIT_TRUE} +EOF + chmod +x "${_shunit_file_}" + done + + unset _shunit_file_ +} + +_shunit_cleanup() +{ + _shunit_name_=$1 + + case ${_shunit_name_} in + EXIT) _shunit_signal_=0 ;; + INT) _shunit_signal_=2 ;; + TERM) _shunit_signal_=15 ;; + *) + _shunit_warn "unrecognized trap value (${_shunit_name_})" + _shunit_signal_=0 + ;; + esac + + # do our work + rm -fr "${__shunit_tmpDir}" + + # exit for all non-EXIT signals + if [ ${_shunit_name_} != 'EXIT' ]; then + _shunit_warn "trapped and now handling the (${_shunit_name_}) signal" + # disable EXIT trap + trap 0 + # add 128 to signal and exit + exit `expr ${_shunit_signal_} + 128` + elif [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ] ; then + _shunit_assertFail 'Unknown failure encountered running a test' + _shunit_generateReport + exit ${SHUNIT_ERROR} + fi + + unset _shunit_name_ _shunit_signal_ +} + +# The actual running of the tests happens here. +_shunit_execSuite() +{ + for _shunit_test_ in ${__shunit_suite}; do + __shunit_testSuccess=${SHUNIT_TRUE} + + # disable skipping + endSkipping + + # execute the per-test setup function + setUp + + # execute the test + echo "${_shunit_test_}" + eval ${_shunit_test_} + + # execute the per-test tear-down function + tearDown + + # update stats + if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then + __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1` + else + __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1` + fi + done + + unset _shunit_test_ +} + +# This function exits shUnit2 with the appropriate error code and OK/FAILED +# message. +_shunit_generateReport() +{ + _shunit_ok_=${SHUNIT_TRUE} + + # if no exit code was provided one, determine an appropriate one + [ ${__shunit_testsFailed} -gt 0 \ + -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \ + && _shunit_ok_=${SHUNIT_FALSE} + + echo + if [ ${__shunit_testsTotal} -eq 1 ]; then + echo "Ran ${__shunit_testsTotal} test." + else + echo "Ran ${__shunit_testsTotal} tests." + fi + + _shunit_failures_='' + _shunit_skipped_='' + [ ${__shunit_assertsFailed} -gt 0 ] \ + && _shunit_failures_="failures=${__shunit_assertsFailed}" + [ ${__shunit_assertsSkipped} -gt 0 ] \ + && _shunit_skipped_="skipped=${__shunit_assertsSkipped}" + + if [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then + _shunit_msg_='OK' + [ -n "${_shunit_skipped_}" ] \ + && _shunit_msg_="${_shunit_msg_} (${_shunit_skipped_})" + else + _shunit_msg_="FAILED (${_shunit_failures_}" + [ -n "${_shunit_skipped_}" ] \ + && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}" + _shunit_msg_="${_shunit_msg_})" + fi + + echo + echo ${_shunit_msg_} + __shunit_reportGenerated=${SHUNIT_TRUE} + + unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_ +} + +_shunit_shouldSkip() +{ + [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE} + _shunit_assertSkip +} + +_shunit_assertPass() +{ + __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` +} + +_shunit_assertFail() +{ + _shunit_msg_=$1 + + __shunit_testSuccess=${SHUNIT_FALSE} + __shunit_assertsFailed=`expr ${__shunit_assertsFailed} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` + echo "${__SHUNIT_ASSERT_MSG_PREFIX}${_shunit_msg_}" + + unset _shunit_msg_ +} + +_shunit_assertSkip() +{ + __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` +} + +#------------------------------------------------------------------------------ +# main +# + +# create a temporary storage location +__shunit_tmpDir=`_shunit_mktempDir` + +# provide a public temporary directory for unit test scripts +# TODO(kward): document this +shunit_tmpDir="${__shunit_tmpDir}/tmp" +mkdir "${shunit_tmpDir}" + +# setup traps to clean up after ourselves +trap '_shunit_cleanup EXIT' 0 +trap '_shunit_cleanup INT' 2 +trap '_shunit_cleanup TERM' 15 + +# create phantom functions to work around issues with Cygwin +_shunit_mktempFunc +PATH="${__shunit_tmpDir}:${PATH}" + +# execute the oneTimeSetUp function (if it exists) +oneTimeSetUp + +# execute the suite function defined in the parent test script +# deprecated as of 2.1.0 +suite + +# if no suite function was defined, dynamically build a list of functions +if [ -z "${__shunit_suite}" ]; then + shunit_funcs_=`grep "^[ \t]*test[A-Za-z0-9_]* *()" ${__SHUNIT_PARENT} \ + |sed 's/[^A-Za-z0-9_]//g'` + for shunit_func_ in ${shunit_funcs_}; do + suite_addTest ${shunit_func_} + done +fi +unset shunit_func_ shunit_funcs_ + +# execute the tests +_shunit_execSuite + +# execute the oneTimeTearDown function (if it exists) +oneTimeTearDown + +# generate the report +_shunit_generateReport + +# that's it folks +[ ${__shunit_testsFailed} -eq 0 ] +exit $? + +#/** +# +#*/ diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 0000000..2c05920 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,128 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo + unset VIRTUALENVWRAPPER_INITIALIZED + rm -f "$TMPDIR/catch_output" +} + +SOURCE_SCRIPTS="initialize postmkvirtualenv predeactivate postdeactivate postactivate " +RUN_SCRIPTS="premkvirtualenv prermvirtualenv postrmvirtualenv preactivate get_env_details" + +test_virtualenvwrapper_initialize() { + assertTrue "Initialized" virtualenvwrapper_initialize + for hook in $SOURCE_SCRIPTS + do + assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/$hook ]" + assertFalse "Global $WORKON_HOME/$hook is executable" "[ -x $WORKON_HOME/$hook ]" + done + for hook in $RUN_SCRIPTS + do + assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/$hook ]" + assertTrue "Global $WORKON_HOME/$hook is executable" "[ -x $WORKON_HOME/$hook ]" + done + echo "echo GLOBAL initialize >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" + virtualenvwrapper_initialize + output=$(cat "$TMPDIR/catch_output") + expected="GLOBAL initialize" + assertSame "$expected" "$output" +} + +test_virtualenvwrapper_space_in_workon_home() { + before="$WORKON_HOME" + export WORKON_HOME="$WORKON_HOME/this has spaces" + expected="$WORKON_HOME" + mkdir -p "$expected" + virtualenvwrapper_initialize + RC=$? + assertSame "$expected" "$WORKON_HOME" + assertSame "0" "$RC" + export WORKON_HOME="$before" +} + +test_virtualenvwrapper_verify_workon_home() { + assertTrue "WORKON_HOME not verified" virtualenvwrapper_verify_workon_home +} + +test_virtualenvwrapper_verify_workon_home_missing_dir() { + old_home="$WORKON_HOME" + WORKON_HOME="$WORKON_HOME/not_there" + assertTrue "Directory already exists" "[ ! -d \"$WORKON_HOME\" ]" + virtualenvwrapper_verify_workon_home >"$old_home/output" 2>&1 + output=$(cat "$old_home/output") + assertSame "NOTE: Virtual environments directory $WORKON_HOME does not exist. Creating..." "$output" + WORKON_HOME="$old_home" +} + +test_virtualenvwrapper_verify_workon_home_missing_dir_quiet() { + old_home="$WORKON_HOME" + WORKON_HOME="$WORKON_HOME/not_there_quiet" + assertTrue "Directory already exists" "[ ! -d \"$WORKON_HOME\" ]" + output=$(virtualenvwrapper_verify_workon_home -q 2>&1) + assertSame "" "$output" + WORKON_HOME="$old_home" +} + +test_virtualenvwrapper_verify_workon_home_missing_dir_grep_options() { + old_home="$WORKON_HOME" + WORKON_HOME="$WORKON_HOME/not_there" + # This should prevent the message from being found if it isn't + # unset correctly. + export GREP_OPTIONS="--count" + assertTrue "WORKON_HOME not verified" virtualenvwrapper_verify_workon_home + WORKON_HOME="$old_home" + unset GREP_OPTIONS +} + +test_python_interpreter_set_incorrectly() { + return_to="$(pwd)" + cd "$WORKON_HOME" + mkvirtualenv no_wrappers >/dev/null 2>&1 + RC=$? + assertEquals "mkvirtualenv return code wrong" "0" "$RC" + expected="No module named virtualenvwrapper" + # test_shell is set by tests/run_tests + if [ "$test_shell" = "" ] + then + export test_shell=$SHELL + fi + outfilename="$WORKON_HOME/test_out.$$" + subshell_output=$(VIRTUALENVWRAPPER_PYTHON="$WORKON_HOME/no_wrappers/bin/python" $test_shell $return_to/virtualenvwrapper.sh >"$outfilename" 2>&1) + #echo "$subshell_output" + cat "$outfilename" | sed "s/'//g" | grep -q "$expected" 2>&1 + found_it=$? + #echo "$found_it" + assertTrue "Expected \'$expected\', got: \'$(cat "$outfilename")\'" "[ $found_it -eq 0 ]" + assertFalse "Failed to detect invalid Python location" "VIRTUALENVWRAPPER_PYTHON=$VIRTUAL_ENV/bin/python virtualenvwrapper_run_hook initialize >/dev/null 2>&1" + cd "$return_to" + deactivate +} + +test_virtualenvwrapper_verify_virtualenv(){ + assertTrue "Verified unable to verify virtualenv" virtualenvwrapper_verify_virtualenv + + VIRTUALENVWRAPPER_VIRTUALENV="thiscannotpossiblyexist123" + assertFalse "Incorrectly verified virtualenv" virtualenvwrapper_verify_virtualenv +} + +test_virtualenvwrapper_verify_virtualenv_clone(){ + assertTrue "Verified unable to verify virtualenv_clone" virtualenvwrapper_verify_virtualenv_clone + + VIRTUALENVWRAPPER_VIRTUALENV_CLONE="thiscannotpossiblyexist123" + assertFalse "Incorrectly verified virtualenv_clone" virtualenvwrapper_verify_virtualenv_clone +} + +. "$test_dir/shunit2" diff --git a/tests/test_add2virtualenv.sh b/tests/test_add2virtualenv.sh new file mode 100755 index 0000000..e1a0cb3 --- /dev/null +++ b/tests/test_add2virtualenv.sh @@ -0,0 +1,154 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers >/dev/null 2>&1 +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +test_add2virtualenv () { + mkvirtualenv "pathtest" >/dev/null 2>&1 + full_path=$(pwd) + add2virtualenv "$full_path" + cdsitepackages + # Check contents of path file + path_file="./_virtualenv_path_extensions.pth" + assertTrue "No $full_path in $(cat $path_file)" "grep -q $full_path $path_file" + assertTrue "No path insert code in $(cat $path_file)" "grep -q sys.__egginsert $path_file" + # Check the path we inserted is actually at the top + expected="$full_path" + actual=$($WORKON_HOME/pathtest/bin/python -c "import sys; sys.stdout.write(sys.path[1]+'\n')") + assertSame "$expected" "$actual" + # Make sure the temporary file created + # during the edit was removed + assertFalse "Temporary file ${path_file}.tmp still exists" "[ -f ${path_file}.tmp ]" + cd - >/dev/null 2>&1 +} + +test_add2virtualenv_zsh_noclobber () { + # See issue #137 + if [ ! -z $ZSH_VERSION ] + then + set -o noclobber + elif [[ "$SHELL" =~ "bash" ]] + then + shopt -o -s noclobber + else + return 0 + fi + mkvirtualenv "pathtest_noclobber" >/dev/null 2>&1 + full_path=$(pwd) + add2virtualenv "$full_path" + RC=$? + if [ ! -z $ZSH_VERSION ] + then + unsetopt noclobber + elif [[ "/$SHELL" =~ "/bash" ]] + then + shopt -o -u noclobber + fi + assertEquals "0" "$RC" + cdsitepackages + # Check contents of path file + path_file="./_virtualenv_path_extensions.pth" + assertTrue "No $full_path in $(cat $path_file)" "grep -q $full_path $path_file" + assertTrue "No path insert code in $(cat $path_file)" "grep -q sys.__egginsert $path_file" + # Check the path we inserted is actually at the top + expected="$full_path" + actual=$($WORKON_HOME/pathtest_noclobber/bin/python -c "import sys; sys.stdout.write(sys.path[1]+'\n')") + assertSame "$expected" "$actual" + # Make sure the temporary file created + # during the edit was removed + assertFalse "Temporary file ${path_file}.tmp still exists" "[ -f ${path_file}.tmp ]" + cd - >/dev/null 2>&1 +} + +test_add2virtualenv_relative () { + mkvirtualenv "pathtest_relative" >/dev/null 2>&1 + parent_dir=$(dirname $(pwd)) + base_dir=$(basename $(pwd)) + add2virtualenv "../$base_dir" + cdsitepackages + path_file="./_virtualenv_path_extensions.pth" + assertTrue "No $parent_dir/$base_dir in \"`cat $path_file`\"" "grep -q \"$parent_dir/$base_dir\" $path_file" + cd - >/dev/null 2>&1 +} + +test_add2virtualenv_space () { + # see #132 + mkvirtualenv "pathtest_space" >/dev/null 2>&1 + parent_dir=$(dirname $(pwd)) + cdvirtualenv + mkdir 'a b' + add2virtualenv 'a b' + cdsitepackages + path_file="./_virtualenv_path_extensions.pth" + assertTrue "No 'a b' in \"`cat $path_file`\"" "grep -q 'a b' $path_file" + cd - >/dev/null 2>&1 +} + +test_add2virtualenv_ampersand () { + # see #132 + mkvirtualenv "pathtest_ampersand" >/dev/null 2>&1 + parent_dir=$(dirname $(pwd)) + cdvirtualenv + mkdir 'a & b' + add2virtualenv 'a & b' + cdsitepackages + path_file="./_virtualenv_path_extensions.pth" + assertTrue "No 'a & b' in \"`cat $path_file`\"" "grep -q 'a & b' $path_file" + cd - >/dev/null 2>&1 +} + +test_add2virtualenv_delete () { + path_file="./_virtualenv_path_extensions.pth" + mkvirtualenv "pathtest_delete" >/dev/null 2>&1 + cdsitepackages + # Make sure it was added + add2virtualenv "/full/path" + assertTrue "No /full/path in $(cat $path_file)" "grep -q /full/path $path_file" + # Remove it and verify that change + add2virtualenv -d "/full/path" + assertFalse "/full/path in `cat $path_file`" "grep -q /full/path $path_file" + cd - >/dev/null 2>&1 +} + +test_add2virtualenv_delete_space () { + path_file="./_virtualenv_path_extensions.pth" + mkvirtualenv "pathtest_delete_space" >/dev/null 2>&1 + cdsitepackages + # Make sure it was added + add2virtualenv "/full/path with spaces" + assertTrue "No /full/path with spaces in $(cat $path_file)" "grep -q '/full/path with spaces' $path_file" + # Remove it and verify that change + add2virtualenv -d "/full/path with spaces" + assertFalse "/full/path with spaces in `cat $path_file`" "grep -q '/full/path with spaces' $path_file" + cd - >/dev/null 2>&1 +} + +test_add2virtualenv_delete_ampersand () { + path_file="./_virtualenv_path_extensions.pth" + mkvirtualenv "pathtest_delete_ampersand" >/dev/null 2>&1 + cdsitepackages + # Make sure it was added + add2virtualenv "/full/path & dir" + assertTrue "No /full/path & dir in $(cat $path_file)" "grep -q '/full/path & dir' $path_file" + # Remove it and verify that change + add2virtualenv -d "/full/path & dir" + assertFalse "/full/path & dir in `cat $path_file`" "grep -q '/full/path & dir' $path_file" + cd - >/dev/null 2>&1 +} + + +. "$test_dir/shunit2" diff --git a/tests/test_allvirtualenv.sh b/tests/test_allvirtualenv.sh new file mode 100755 index 0000000..7cc26b9 --- /dev/null +++ b/tests/test_allvirtualenv.sh @@ -0,0 +1,43 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + unset VIRTUAL_ENV + # These three env names must sort the same whether the OS considers leading whitespace or not. + # "test" is after " env" and after "env", so the asserts work on OSX and Linux. + mkvirtualenv test1 >/dev/null 2>&1 + mkvirtualenv test2 >/dev/null 2>&1 + # Only test with leading and internal spaces. Directory names with trailing spaces are legal, + # and work with virtualenv on OSX, but error out on Linux. + mkvirtualenv " env with space" >/dev/null 2>&1 + deactivate +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +tearDown () { + deactivate >/dev/null 2>&1 +} + +test_allvirtualenv_all() { + assertTrue "Did not find test1" "allvirtualenv pwd | grep -q 'test1$'" + assertTrue "Did not find test2" "allvirtualenv pwd | grep -q 'test2$'" + assertTrue "Did not find ' env with space'" "allvirtualenv pwd | grep -q ' env with space'" +} + +test_allvirtualenv_spaces() { + assertTrue "Command did not output The Zen of Python" "allvirtualenv python -c 'import this' | grep -q 'The Zen of Python'" +} + +. "$test_dir/shunit2" diff --git a/tests/test_cd.sh b/tests/test_cd.sh new file mode 100755 index 0000000..1001204 --- /dev/null +++ b/tests/test_cd.sh @@ -0,0 +1,80 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + unset VIRTUAL_ENV + mkvirtualenv cd-test >/dev/null 2>&1 + deactivate +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo + workon cd-test +} + +tearDown () { + deactivate >/dev/null 2>&1 +} + +cd () { + fail "Should not be using override cd function" +} + +test_cdvirtual() { + start_dir="$(pwd)" + cdvirtualenv + assertSame "$VIRTUAL_ENV" "$(pwd)" + cdvirtualenv bin + assertSame "$VIRTUAL_ENV/bin" "$(pwd)" + virtualenvwrapper_cd "$start_dir" +} + +test_cdsitepackages () { + start_dir="$(pwd)" + cdsitepackages + pyvers=$(python -V 2>&1 | cut -f2 -d' ' | cut -f1-2 -d.) + sitepackages="$VIRTUAL_ENV/lib/python${pyvers}/site-packages" + assertSame "$sitepackages" "$(pwd)" + virtualenvwrapper_cd "$start_dir" +} + +test_cdsitepackages_with_arg () { + start_dir="$(pwd)" + pyvers=$(python -V 2>&1 | cut -f2 -d' ' | cut -f1-2 -d.) + sitepackage_subdir="$VIRTUAL_ENV/lib/python${pyvers}/site-packages/subdir" + mkdir -p "${sitepackage_subdir}" + cdsitepackages subdir + assertSame "$sitepackage_subdir" "$(pwd)" + virtualenvwrapper_cd "$start_dir" +} + +test_cdvirtualenv_no_workon_home () { + old_home="$WORKON_HOME" + export WORKON_HOME="$WORKON_HOME/not_there" + cdvirtualenv >"$old_home/output" 2>&1 + output=$(cat "$old_home/output") + assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" + WORKON_HOME="$old_home" +} + +test_cdsitepackages_no_workon_home () { + deactivate 2>&1 + old_home="$WORKON_HOME" + virtualenvwrapper_cd "$WORKON_HOME" + export WORKON_HOME="$WORKON_HOME/not_there" + assertFalse "Was able to change to site-packages" cdsitepackages + assertSame "$old_home" "$(pwd)" + WORKON_HOME="$old_home" +} + + +. "$test_dir/shunit2" diff --git a/tests/test_cd_alias.sh b/tests/test_cd_alias.sh new file mode 100755 index 0000000..e7ec6ae --- /dev/null +++ b/tests/test_cd_alias.sh @@ -0,0 +1,52 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + unset VIRTUAL_ENV + mkvirtualenv cd-test >/dev/null 2>&1 + deactivate +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo + workon cd-test +} + +tearDown () { + deactivate >/dev/null 2>&1 +} + +test_cd() { + alias cd='fail "Should not be using override cd function"' + start_dir="$(pwd)" + virtualenvwrapper_cd "$VIRTUAL_ENV" + assertSame "$VIRTUAL_ENV" "$(pwd)" + virtualenvwrapper_cd "$start_dir" + unalias cd +} + +# Define hook function to make cd break +chpwd () { + return 1 +} +# Run a test that uses cd to ensure the hook is not called +test_cd_zsh_chpwd_not_called () { + if [ -n "$ZSH_VERSION" ]; then + start_dir="$(pwd)" + virtualenvwrapper_cd "$VIRTUAL_ENV" + assertSame "$VIRTUAL_ENV" "$(pwd)" + virtualenvwrapper_cd "$start_dir" + fi + unset -f chpwd >/dev/null 2>&1 +} + +. "$test_dir/shunit2" diff --git a/tests/test_cd_space_in_name.sh b/tests/test_cd_space_in_name.sh new file mode 100755 index 0000000..281dcbf --- /dev/null +++ b/tests/test_cd_space_in_name.sh @@ -0,0 +1,55 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + export WORKON_HOME="$WORKON_HOME/ this has spaces" + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + unset VIRTUAL_ENV + # Only test with leading and internal spaces. Directory names with trailing spaces are legal, + # and work with virtualenv on OSX, but error out on Linux. + mkvirtualenv " env with space" >/dev/null 2>&1 + deactivate +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +tearDown () { + deactivate >/dev/null 2>&1 +} + +cd () { + fail "Should not be using override cd function" +} + +test_cdvirtual_space_in_workon_home_space_in_name() { + workon " env with space" + start_dir="$(pwd)" + cdvirtualenv + assertSame "$VIRTUAL_ENV" "$(pwd)" + cdvirtualenv bin + assertSame "$VIRTUAL_ENV/bin" "$(pwd)" + virtualenvwrapper_cd "$start_dir" +} + +test_cdsitepackages_space_in_name () { + workon " env with space" + start_dir="$(pwd)" + cdsitepackages + pyvers=$(python -V 2>&1 | cut -f2 -d' ' | cut -f1-2 -d.) + sitepackages="$VIRTUAL_ENV/lib/python${pyvers}/site-packages" + assertSame "$sitepackages" "$(pwd)" + virtualenvwrapper_cd "$start_dir" +} + + +. "$test_dir/shunit2" diff --git a/tests/test_cp.sh b/tests/test_cp.sh new file mode 100755 index 0000000..cadd5ea --- /dev/null +++ b/tests/test_cp.sh @@ -0,0 +1,140 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +setUp () { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + rm -f "$TMPDIR/catch_output" + echo +} + +tearDown() { + if type deactivate >/dev/null 2>&1 + then + deactivate + fi + rm -rf "$WORKON_HOME" +} + +test_new_env_activated () { + mkvirtualenv "source" >/dev/null 2>&1 + RC=$? + assertEquals "0" "$RC" + (cd tests/testpackage && pip install .) >/dev/null 2>&1 + cpvirtualenv "source" "destination" >/dev/null 2>&1 + rmvirtualenv "source" >/dev/null 2>&1 + testscript="$(which testscript.py)" + assertTrue "Environment test script not found in path" "[ $WORKON_HOME/destination/bin/testscript.py -ef $testscript ]" + testscriptcontent="$(cat $testscript)" + assertTrue "No cpvirtualenvtest in $testscriptcontent" "echo $testscriptcontent | grep cpvirtualenvtest" + assertTrue virtualenvwrapper_verify_active_environment +} + +test_virtual_env_variable () { + mkvirtualenv "source" >/dev/null 2>&1 + cpvirtualenv "source" "destination" >/dev/null 2>&1 + assertSame "Wrong virtualenv name" "destination" $(basename "$VIRTUAL_ENV") + assertTrue "$WORKON_HOME not in $VIRTUAL_ENV" "echo $VIRTUAL_ENV | grep -q $WORKON_HOME" +} + +test_virtual_env_variable_space_in_name () { + # Only test with leading and internal spaces. Directory names with trailing spaces are legal, + # and work with virtualenv on OSX, but error out on Linux. + mkvirtualenv " space source" >/dev/null 2>&1 + cpvirtualenv " space source" " space destination" >/dev/null 2>&1 + assertSame "Wrong virtualenv name" " space destination" "$(basename "$VIRTUAL_ENV")" + assertTrue "$WORKON_HOME not in $VIRTUAL_ENV" "echo $VIRTUAL_ENV | grep -q $WORKON_HOME" +} + +fake_venv () { + ### + # create a silly file to ensure copy happens + ### + typeset envname="$1" + virtualenv $@ + touch "$WORKON_HOME/$envname/fake_virtualenv_was_here" +} + +fake_venv_clone () { + ### + # create a silly file to ensure copy happens + ### + typeset src_path="$1" + touch "$src_path/fake_virtualenv_clone_was_here" + virtualenv-clone $@ +} + +test_virtualenvwrapper_virtualenv_variable () { + + eval 'virtualenvwrapper_verify_virtualenv () { + return 0 + }' + + VIRTUALENVWRAPPER_VIRTUALENV=fake_venv + assertSame "VIRTUALENVWRAPPER_VIRTUALENV is not set correctly" "$VIRTUALENVWRAPPER_VIRTUALENV" "fake_venv" + + mkvirtualenv "source" >/dev/null 2>&1 + assertTrue "Fake file not made in fake_venv" "[ -f "$VIRTUAL_ENV/fake_virtualenv_was_here" ]" + cpvirtualenv "source" "destination" >/dev/null 2>&1 + unset VIRTUALENVWRAPPER_VIRTUALENV + assertTrue "VIRTUALENVWRAPPER_CLONE did not clone fake file" "[ -f $WORKON_HOME/destination/fake_virtualenv_was_here ]" +} + +test_virtualenvwrapper_virtualenv_clone_variable () { + + eval 'virtualenvwrapper_verify_virtualenv_clone () { + return 0 + }' + + VIRTUALENVWRAPPER_VIRTUALENV_CLONE=fake_venv_clone + assertSame "VIRTUALENVWRAPPER_VIRTUALENV_CLONE is not set correctly" "$VIRTUALENVWRAPPER_VIRTUALENV_CLONE" "fake_venv_clone" + + mkvirtualenv "source" >/dev/null 2>&1 + cpvirtualenv "source" "destination" >/dev/null 2>&1 + unset VIRTUALENVWRAPPER_VIRTUALENV_CLONE + assertTrue "VIRTUALENVWRAPPER_CLONE did not clone fake file" "[ -f $WORKON_HOME/destination/fake_virtualenv_clone_was_here ]" +} + +test_source_does_not_exist () { + assertSame "Please provide a valid virtualenv to copy." "$(cpvirtualenv virtualenvthatdoesntexist foo)" +} + +test_hooks () { + mkvirtualenv "source" >/dev/null 2>&1 + + # Set the interpreter of the hook script to the simple shell + echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" + echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" + chmod +x "$WORKON_HOME/premkvirtualenv" + + echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" + + # Set the interpreter of the hook script to the simple shell + echo "#!/bin/sh" > "$WORKON_HOME/precpvirtualenv" + echo "echo GLOBAL precpvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/precpvirtualenv" + chmod +x "$WORKON_HOME/precpvirtualenv" + + # Set the interpreter of the hook script to the simple shell + echo "#!/bin/sh" > "$WORKON_HOME/postcpvirtualenv" + echo "echo GLOBAL postcpvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postcpvirtualenv" + + cpvirtualenv "source" "destination" >/dev/null 2>&1 + + output=$(cat "$TMPDIR/catch_output") + workon_home_as_pwd=$(cd $WORKON_HOME; pwd) + + expected="GLOBAL precpvirtualenv $workon_home_as_pwd $workon_home_as_pwd/source destination +GLOBAL premkvirtualenv $workon_home_as_pwd destination +GLOBAL postmkvirtualenv +GLOBAL postcpvirtualenv" + + assertSame "$expected" "$output" + + rm -f "$WORKON_HOME/premkvirtualenv" + rm -f "$WORKON_HOME/postmkvirtualenv" +} + +. "$test_dir/shunit2" diff --git a/tests/test_cpvirtualenv.sh b/tests/test_cpvirtualenv.sh new file mode 100755 index 0000000..b39fea0 --- /dev/null +++ b/tests/test_cpvirtualenv.sh @@ -0,0 +1,231 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +setUp () { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + echo +} + +tearDown () { + if type deactivate >/dev/null 2>&1 + then + deactivate + fi + rm -rf "$WORKON_HOME" +} + +test_no_arguments () { + assertSame "Please provide a valid virtualenv to copy." "$(cpvirtualenv)" +} + +test_venv_already_exists_in_workon () { + mkvirtualenv "cpvenv_test" >/dev/null 2>&1 + assertSame "cpvenv_test virtualenv already exists." "$(cpvirtualenv 'cpvenv_test')" +} + +test_bad_path () { + assertSame "Please provide a valid virtualenv to copy." "$(cpvirtualenv '~/cpvenv_test')" +} + +test_copy_venv () { + # verify venvs don't exist + assertTrue "Virtualenv to copy already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" + assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" + mkvirtualenv "cpvenv_test" >/dev/null 2>&1 + touch "$WORKON_HOME/cpvenv_test/mytestpackage" + + assertTrue "Virtualenv to copy didn't get created" "[ -d $WORKON_HOME/cpvenv_test ]" + assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" + + cpvirtualenv "cpvenv_test" "copied_venv" >/dev/null 2>&1 + + # verify copied venv exist + assertTrue "Copied virtualenv doesn't exist" "[ -d $WORKON_HOME/copied_venv ]" + # verify test package exists + assertTrue "Test package is missing" "[ -f $WORKON_HOME/copied_venv/mytestpackage ]" +} + +test_copy_venv_activate () { + # verify venvs don't exist + assertTrue "Virtualenv to copy already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" + assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" + mkvirtualenv "cpvenv_test" >/dev/null 2>&1 + + assertTrue "Virtualenv to copy didn't get created" "[ -d $WORKON_HOME/cpvenv_test ]" + assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" + + cpvirtualenv "cpvenv_test" "copied_venv" >/dev/null 2>&1 + + # verify copied venv exist + assertTrue "Copied virtualenv doesn't exist" "[ -d $WORKON_HOME/copied_venv ]" + + assertSame "copied_venv" "$(basename $VIRTUAL_ENV)" + assertTrue "Virtualenv not active" virtualenvwrapper_verify_active_environment +} + +test_copy_venv_is_listed () { + # verify venvs don't exist + assertTrue "Virtualenv to copy already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" + assertTrue "Copied virtualenv already exists" "[ ! -d $WORKON_HOME/copied_venv ]" + mkvirtualenv "cpvenv_test" >/dev/null 2>&1 + + typeset listed=$(lsvirtualenv) + [[ "$listed" != *copied_venv* ]] + RC=$? + assertTrue "Copied virtualenv found in virtualenv list" $RC + + cpvirtualenv "cpvenv_test" "copied_venv" >/dev/null 2>&1 + + listed=$(lsvirtualenv -b) + [[ "$listed" == *copied_venv* ]] + RC=$? + assertTrue "Copied virtualenv not found in virtualenv list" $RC +} + +test_clone_venv () { + typeset tmplocation=$(dirname $WORKON_HOME) + + # verify venvs don't exist + assertTrue "Virtualenv to clone already exists" "[ ! -d $tmplocation/cpvenv_test ]" + assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" + + $VIRTUALENVWRAPPER_VIRTUALENV "$tmplocation/cpvenv_test" >/dev/null 2>&1 + touch "$tmplocation/cpvenv_test/mytestpackage" + + assertTrue "Virtualenv to clone didn't get created" "[ -d $tmplocation/cpvenv_test ]" + assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" + + cpvirtualenv "$tmplocation/cpvenv_test" "cloned_venv" >/dev/null 2>&1 + + # verify cloned venv exist + assertTrue "Cloned virtualenv doesn't exist" "[ -d $WORKON_HOME/cloned_venv ]" + # verify test package exists + assertTrue "Test package is missing" "[ -f $WORKON_HOME/cloned_venv/mytestpackage ]" + + rm -rf "$tmplocation/cpvenv_test" +} + +test_clone_venv_activate () { + typeset tmplocation=$(dirname $WORKON_HOME) + + # verify venvs don't exist + assertTrue "Virtualenv to clone already exists" "[ ! -d $tmplocation/cpvenv_test ]" + assertTrue "Virtualenv with same name as clone source already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" + assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" + + $VIRTUALENVWRAPPER_VIRTUALENV "$tmplocation/cpvenv_test" >/dev/null 2>&1 + + assertTrue "Virtualenv to clone didn't get created" "[ -d $tmplocation/cpvenv_test ]" + assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" + + #cpvirtualenv "$tmplocation/cpvenv_test" "cloned_venv" >/dev/null 2>&1 + cpvirtualenv "$tmplocation/cpvenv_test" "cloned_venv" >/dev/null 2/>&1 + + # verify cloned venv exist + assertTrue "Cloned virtualenv doesn't exist" "[ -d $WORKON_HOME/cloned_venv ]" + + assertSame "cloned_venv" "$(basename $VIRTUAL_ENV)" + assertTrue "Virtualenv not active" virtualenvwrapper_verify_active_environment + + rm -rf "$tmplocation/cpvenv_test" +} + +test_clone_venv_is_listed (){ + typeset tmplocation=$(dirname $WORKON_HOME) + + # verify venvs don't exist + assertTrue "Virtualenv to clone already exists" "[ ! -d $tmplocation/cpvenv_test ]" + assertTrue "Virtualenv with same name as clone source already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" + assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cloned_venv ]" + + typeset listed=$(lsvirtualenv) + [[ "$listed" != *cloned_venv* ]] + RC=$? + assertTrue "Cloned virtualenv found in virtualenv list" $RC + + $VIRTUALENVWRAPPER_VIRTUALENV "$tmplocation/cpvenv_test" >/dev/null 2>&1 + cpvirtualenv "$tmplocation/cpvenv_test" "cloned_venv" >/dev/null 2>&1 + + listed=$(lsvirtualenv -b) + [[ "$listed" == *cloned_venv* ]] + RC=$? + assertTrue "Cloned virtualenv not found in virtualenv list" $RC + + rm -rf "$tmplocation/cpvenv_test" + +} + +test_clone_venv_using_same_name () { + typeset tmplocation=$(dirname $WORKON_HOME) + + # verify venvs don't exist + assertTrue "Virtualenv to clone already exists" "[ ! -d $tmplocation/cpvenv_test ]" + assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" + + $VIRTUALENVWRAPPER_VIRTUALENV "$tmplocation/cpvenv_test" >/dev/null 2>&1 + touch "$tmplocation/cpvenv_test/mytestpackage" + + assertTrue "Virtualenv to clone didn't get created" "[ -d $tmplocation/cpvenv_test ]" + assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" + + typeset listed=$(lsvirtualenv) + [[ "$listed" != *cpvenv_test* ]] + RC=$? + assertTrue "Cloned virtualenv found in virtualenv list" $RC + + cpvirtualenv "$tmplocation/cpvenv_test" >/dev/null 2>&1 >/dev/null 2>&1 + + # verify cloned venv exist + assertTrue "Cloned virtualenv doesn't exist" "[ -d $WORKON_HOME/cpvenv_test ]" + assertTrue "Test package is missing" "[ -f $WORKON_HOME/cpvenv_test/mytestpackage ]" + + listed=$(lsvirtualenv -b) + [[ "$listed" == *cpvenv_test* ]] + RC=$? + assertTrue "Cloned virtualenv not found in virtualenv list" $RC + + assertSame "cpvenv_test" "$(basename $VIRTUAL_ENV)" + assertTrue "Virtualenv not active" virtualenvwrapper_verify_active_environment + + rm -rf "$tmplocation/cpvenv_test" +} + +test_clone_venv_using_vars () { + + # verify venvs don't exist + assertTrue "Virtualenv to clone already exists" "[ ! -d $TMPDIR/cpvenv_test ]" + assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" + + $VIRTUALENVWRAPPER_VIRTUALENV "$TMPDIR/cpvenv_test" >/dev/null 2>&1 + touch "$TMPDIR/cpvenv_test/mytestpackage" + + assertTrue "Virtualenv to clone didn't get created" "[ -d $TMPDIR/cpvenv_test ]" + assertTrue "Cloned virtualenv already exists" "[ ! -d $WORKON_HOME/cpvenv_test ]" + + typeset listed=$(lsvirtualenv) + [[ "$listed" != *cpvenv_test* ]] + RC=$? + assertTrue "Cloned virtualenv found in virtualenv list" $RC + + cpvirtualenv '$TMPDIR/cpvenv_test' >/dev/null 2>&1 >/dev/null 2>&1 + + # verify cloned venv exist + assertTrue "Cloned virtualenv doesn't exist" "[ -d $WORKON_HOME/cpvenv_test ]" + assertTrue "Test package is missing" "[ -f $WORKON_HOME/cpvenv_test/mytestpackage ]" + + listed=$(lsvirtualenv -b) + [[ "$listed" == *cpvenv_test* ]] + RC=$? + assertTrue "Cloned virtualenv not found in virtualenv list" $RC + + assertSame "cpvenv_test" "$(basename $VIRTUAL_ENV)" + assertTrue "Virtualenv not active" virtualenvwrapper_verify_active_environment + + rm -rf "$TMPDIR/cpvenv_test" +} + +source "$test_dir/shunit2" diff --git a/tests/test_deactivate.sh b/tests/test_deactivate.sh new file mode 100755 index 0000000..bbc6414 --- /dev/null +++ b/tests/test_deactivate.sh @@ -0,0 +1,57 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + mkvirtualenv "env1" +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo + rm -f "$TMPDIR/catch_output" +} + +test_deactivate () { + workon env1 + assertNotNull "$VIRTUAL_ENV" + deactivate + assertNull "$VIRTUAL_ENV" + assertFalse virtualenvwrapper_verify_active_environment +} + +test_deactivate_hooks () { + workon env1 + + for t in pre post + do + echo "echo GLOBAL ${t}deactivate \$VIRTUALENVWRAPPER_LAST_VIRTUAL_ENV >> $TMPDIR/catch_output" > "$WORKON_HOME/${t}deactivate" + echo "echo ENV ${t}deactivate \$VIRTUALENVWRAPPER_LAST_VIRTUAL_ENV >> $TMPDIR/catch_output" > "$WORKON_HOME/env1/bin/${t}deactivate" + done + + touch "$TMPDIR/catch_output" + + deactivate + + output=$(cat "$TMPDIR/catch_output") + expected="ENV predeactivate +GLOBAL predeactivate +ENV postdeactivate $WORKON_HOME/env1 +GLOBAL postdeactivate $WORKON_HOME/env1" + assertSame "$expected" "$output" + + for t in pre post + do + rm -f "$WORKON_HOME/env1/bin/${t}activate" + rm -f "$WORKON_HOME/${t}activate" + done +} + +. "$test_dir/shunit2" diff --git a/tests/test_derive_workon_home.sh b/tests/test_derive_workon_home.sh new file mode 100755 index 0000000..4f5ddd1 --- /dev/null +++ b/tests/test_derive_workon_home.sh @@ -0,0 +1,43 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" +TMP_WORKON_HOME="$WORKON_HOME" + +oneTimeSetUp() { + rm -rf "$TMP_WORKON_HOME" + mkdir -p "$TMP_WORKON_HOME" + load_wrappers + echo $PYTHONPATH +} + +oneTimeTearDown() { + rm -rf "$TMP_WORKON_HOME" +} + +setUp () { + echo + WORKON_HOME="$TMP_WORKON_HOME" +} + +test_default() { + unset WORKON_HOME + assertSame "$HOME/.virtualenvs" "$(virtualenvwrapper_derive_workon_home)" +} + +test_includes_relative_path() { + WORKON_HOME="$WORKON_HOME/../$(basename $WORKON_HOME)" + assertSame "$WORKON_HOME" "$(virtualenvwrapper_derive_workon_home)" +} + +test_begins_relative_path() { + WORKON_HOME=".test-virtualenvs" + assertSame "$HOME/.test-virtualenvs" "$(virtualenvwrapper_derive_workon_home)" +} + +test_includes_tilde() { + WORKON_HOME="~/.test-virtualenvs" + assertSame "$HOME/.test-virtualenvs" "$(virtualenvwrapper_derive_workon_home)" +} + +. "$test_dir/shunit2" diff --git a/tests/test_dir_stack.sh b/tests/test_dir_stack.sh new file mode 100755 index 0000000..8383866 --- /dev/null +++ b/tests/test_dir_stack.sh @@ -0,0 +1,48 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + test_begin_dir=$(pwd) +} + +oneTimeTearDown() { + cd "$test_begin_dir" +} + +setUp () { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + mkdir "$WORKON_HOME/start_here" + mkdir "$WORKON_HOME/on_the_stack" + echo +} + +tearDown() { + if type deactivate >/dev/null 2>&1 + then + deactivate + fi + rm -rf "$WORKON_HOME" +} + +test_ticket_101 () { + mkvirtualenv some_env + deactivate + cd "$WORKON_HOME/start_here" + pushd "$WORKON_HOME/on_the_stack" + rmvirtualenv some_env + mkvirtualenv some_env >/dev/null 2>&1 + #echo "After mkvirtualenv: `pwd`" + deactivate + #echo "After deactivate: `pwd`" + popd + #echo "After popd: `pwd`" + current_dir=$(pwd) + assertSame "$WORKON_HOME/start_here" "$current_dir" + +} + +. "$test_dir/shunit2" diff --git a/tests/test_expandpath.sh b/tests/test_expandpath.sh new file mode 100755 index 0000000..f96f67d --- /dev/null +++ b/tests/test_expandpath.sh @@ -0,0 +1,28 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" +TMP_WORKON_HOME="$WORKON_HOME" + +oneTimeSetUp() { + load_wrappers + echo $PYTHONPATH +} + +oneTimeTearDown() { + return 0 +} + +test_tilde() { + assertSame "$HOME" "$(virtualenvwrapper_expandpath ~)" +} + +test_vars() { + assertSame "$HOME" "$(virtualenvwrapper_expandpath '$HOME')" +} + +test_tilde_vars() { + assertSame "$HOME" "$(virtualenvwrapper_expandpath '~$USER')" +} + +. "$test_dir/shunit2" diff --git a/tests/test_hook_dir.sh b/tests/test_hook_dir.sh new file mode 100755 index 0000000..8c86e50 --- /dev/null +++ b/tests/test_hook_dir.sh @@ -0,0 +1,39 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + mkdir -p "$WORKON_HOME/hooks" +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo + rm -f "$WORKON_HOME/hooks/*" +} + +SOURCE_SCRIPTS="initialize postmkvirtualenv predeactivate postdeactivate postactivate " +RUN_SCRIPTS="premkvirtualenv prermvirtualenv postrmvirtualenv preactivate get_env_details" + +test_virtualenvwrapper_initialize() { + export VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME/hooks" + load_wrappers + for hook in $SOURCE_SCRIPTS + do + assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/hooks/$hook ]" + assertFalse "Global $WORKON_HOME/$hook is executable" "[ -x $WORKON_HOME/hooks/$hook ]" + done + for hook in $RUN_SCRIPTS + do + assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/hooks/$hook ]" + assertTrue "Global $WORKON_HOME/$hook is executable" "[ -x $WORKON_HOME/hooks/$hook ]" + done +} + +. "$test_dir/shunit2" diff --git a/tests/test_lazy.sh b/tests/test_lazy.sh new file mode 100755 index 0000000..c475d01 --- /dev/null +++ b/tests/test_lazy.sh @@ -0,0 +1,107 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + [ ! -z "$ZSH_VERSION" ] && unsetopt shwordsplit + source "$test_dir/../virtualenvwrapper_lazy.sh" + [ ! -z "$ZSH_VERSION" ] && setopt shwordsplit +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +function_defined_lazy() { + name="$1" + assertTrue "$name not defined" "type $name" + assertTrue "$name does not load virtualenvwrapper" "typeset -f $name | grep 'virtualenvwrapper_load'" + if [ "$name" = "mkvirtualenv" ] + then + lookfor="rmvirtualenv" + else + lookfor="mkvirtualenv" + fi + assertFalse "$name includes reference to $lookfor: $(typeset -f $name)" "typeset -f $name | grep $lookfor" +} + +test_mkvirtualenv_defined_lazy() { + function_defined_lazy mkvirtualenv +} + +test_rmvirtualenv_defined_lazy() { + function_defined_lazy rmvirtualenv +} + +test_lsvirtualenv_defined_lazy() { + function_defined_lazy lsvirtualenv +} + +test_showvirtualenv_defined_lazy() { + function_defined_lazy showvirtualenv +} + +test_workon_defined_lazy() { + function_defined_lazy workon +} + +test_add2virtualenv_defined_lazy() { + function_defined_lazy add2virtualenv +} + +test_cdsitepackages_defined_lazy() { + function_defined_lazy cdsitepackages +} + +test_cdvirtualenv_defined_lazy() { + function_defined_lazy cdvirtualenv +} + +test_cdvirtualenv_defined_lazy() { + function_defined_lazy cdvirtualenv +} + +test_lssitepackages_defined_lazy() { + function_defined_lazy lssitepackages +} + +test_toggleglobalsitepackages_defined_lazy() { + function_defined_lazy toggleglobalsitepackages +} + +test_cpvirtualenv_defined_lazy() { + function_defined_lazy cpvirtualenv +} + +test_setvirtualenvproject_defined_lazy() { + function_defined_lazy setvirtualenvproject +} + +test_mkproject_defined_lazy() { + function_defined_lazy mkproject +} + +test_cdproject_defined_lazy() { + function_defined_lazy cdproject +} + +test_mktmpenv_defined_lazy() { + function_defined_lazy mktmpenv +} + +test_wipeenv_defined_lazy() { + function_defined_lazy wipeenv +} + +test_allvirtualenv_defined_lazy() { + function_defined_lazy allvirtualenv +} + +. "$test_dir/shunit2" diff --git a/tests/test_lazy_extending.sh b/tests/test_lazy_extending.sh new file mode 100755 index 0000000..f3b3716 --- /dev/null +++ b/tests/test_lazy_extending.sh @@ -0,0 +1,39 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + [ ! -z "$ZSH_VERSION" ] && unsetopt shwordsplit +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +function_defined_lazy() { + name="$1" + assertTrue "$name not defined" "type $name" + assertTrue "$name does not load virtualenvwrapper" "typeset -f $name | grep 'virtualenvwrapper_load'" + if [ "$name" = "mkvirtualenv" ] + then + lookfor="rmvirtualenv" + else + lookfor="mkvirtualenv" + fi + assertFalse "$name includes reference to $lookfor: $(typeset -f $name)" "typeset -f $name | grep $lookfor" +} + +test_custom_defined_lazy() { + _VIRTUALENVWRAPPER_API="my_custom_command" + source "$test_dir/../virtualenvwrapper_lazy.sh" + function_defined_lazy my_custom_command +} + +. "$test_dir/shunit2" diff --git a/tests/test_lazy_loaded.sh b/tests/test_lazy_loaded.sh new file mode 100755 index 0000000..9d217c2 --- /dev/null +++ b/tests/test_lazy_loaded.sh @@ -0,0 +1,111 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + source "$test_dir/../virtualenvwrapper_lazy.sh" + virtualenvwrapper_load +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +function_defined_normal() { + name="$1" + assertTrue "$name not defined" "type $name" + assertFalse "$name still set to run lazy loader" "typeset -f $name | grep 'virtualenvwrapper_load'" +} + +test_mkvirtualenv_defined_normal() { + function_defined_normal mkvirtualenv +} + +test_rmvirtualenv_defined_normal() { + function_defined_normal rmvirtualenv +} + +test_lsvirtualenv_defined_normal() { + function_defined_normal lsvirtualenv +} + +test_showvirtualenv_defined_normal() { + function_defined_normal showvirtualenv +} + +test_workon_defined_normal() { + function_defined_normal workon +} + +test_add2virtualenv_defined_normal() { + function_defined_normal add2virtualenv +} + +test_cdsitepackages_defined_normal() { + function_defined_normal cdsitepackages +} + +test_cdvirtualenv_defined_normal() { + function_defined_normal cdvirtualenv +} + +test_cdvirtualenv_defined_normal() { + function_defined_normal cdvirtualenv +} + +test_lssitepackages_defined_normal() { + function_defined_normal lssitepackages +} + +test_cpvirtualenv_defined_normal() { + function_defined_normal cpvirtualenv +} + +test_setvirtualenvproject_defined_normal() { + function_defined_normal setvirtualenvproject +} + +test_mkproject_defined_normal() { + function_defined_normal mkproject +} + +test_cdproject_defined_normal() { + function_defined_normal cdproject +} + +test_mktmpenv_defined_normal() { + function_defined_normal mktmpenv +} + +test_wipeenv_defined_normal() { + function_defined_normal wipeenv +} + +test_allvirtualenv_defined_normal() { + function_defined_normal allvirtualenv +} + + +# test_virtualenvwrapper_initialize() { +# assertTrue "Initialized" virtualenvwrapper_initialize +# for hook in premkvirtualenv postmkvirtualenv prermvirtualenv postrmvirtualenv preactivate postactivate predeactivate postdeactivate +# do +# assertTrue "Global $WORKON_HOME/$hook was not created" "[ -f $WORKON_HOME/$hook ]" +# assertTrue "Global $WORKON_HOME/$hook is not executable" "[ -x $WORKON_HOME/$hook ]" +# done +# assertTrue "Log file was not created" "[ -f $WORKON_HOME/hook.log ]" +# echo "echo GLOBAL initialize >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" +# virtualenvwrapper_initialize +# output=$(cat "$TMPDIR/catch_output") +# expected="GLOBAL initialize" +# assertSame "$expected" "$output" +# } + +. "$test_dir/shunit2" diff --git a/tests/test_lazy_run.sh b/tests/test_lazy_run.sh new file mode 100755 index 0000000..4c75f11 --- /dev/null +++ b/tests/test_lazy_run.sh @@ -0,0 +1,29 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + (cd "$WORKON_HOME" && virtualenv lazy_load_test >/dev/null 2>&1) + source "$test_dir/../virtualenvwrapper_lazy.sh" +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +test_workon_changes_defs() { + # See issue #144 + assertFalse "virtualenvwrapper_run_hook is already defined" "type virtualenvwrapper_run_hook" + workon lazy_load_test >/dev/null 2>&1 + assertTrue "virtualenvwrapper_run_hook is not defined" "type virtualenvwrapper_run_hook" + assertTrue "workon still set to run lazy loader" "typeset -f $name | grep 'virtualenvwrapper_load'" +} + +. "$test_dir/shunit2" diff --git a/tests/test_log_file.sh b/tests/test_log_file.sh new file mode 100755 index 0000000..4bdc317 --- /dev/null +++ b/tests/test_log_file.sh @@ -0,0 +1,32 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +setUp () { + echo +} + +test_set_by_user() { + export VIRTUALENVWRAPPER_LOG_FILE="$WORKON_HOME/hooks.log" + load_wrappers + assertTrue "Log file was not created" "[ -f $VIRTUALENVWRAPPER_LOG_FILE ]" +} + +test_file_permissions() { + export VIRTUALENVWRAPPER_LOG_FILE="$WORKON_HOME/hooks.log" + load_wrappers + perms=$(ls -l "$VIRTUALENVWRAPPER_LOG_FILE" | cut -f1 -d' ') + #echo $perms + assertTrue "Log file permissions are wrong: $perms" "echo $perms | grep '^-rw-rw'" +} + +test_not_set_by_user() { + unset WORKON_HOME + unset VIRTUALENVWRAPPER_LOG_FILE + unset VIRTUALENVWRAPPER_HOOK_DIR + load_wrappers + assertSame "" "$VIRTUALENVWRAPPER_LOG_FILE" +} + +. "$test_dir/shunit2" diff --git a/tests/test_ls.sh b/tests/test_ls.sh new file mode 100755 index 0000000..18d0008 --- /dev/null +++ b/tests/test_ls.sh @@ -0,0 +1,102 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +test_get_site_packages_dir () { + mkvirtualenv "lssitepackagestest" >/dev/null 2>&1 + d=$(virtualenvwrapper_get_site_packages_dir) + echo "site-packages in $d" + assertTrue "site-packages dir $d does not exist" "[ -d $d ]" + deactivate +} + +test_lssitepackages () { + mkvirtualenv "lssitepackagestest" >/dev/null 2>&1 + contents="$(lssitepackages)" + assertTrue "did not find pip in site-packages: ${contents}" "echo $contents | grep -q pip" + deactivate +} + +test_lssitepackages_space_in_name () { + # Only test with leading and internal spaces. Directory names with trailing spaces are legal, + # and work with virtualenv on OSX, but error out on Linux. + mkvirtualenv " space lssitepackagestest" >/dev/null 2>&1 + contents="$(lssitepackages)" + assertTrue "did not find pip in site-packages: ${contents}" "echo $contents | grep -q pip" + deactivate +} + +test_lssitepackages_add2virtualenv () { + mkvirtualenv "lssitepackagestest" >/dev/null 2>&1 + parent_dir=$(dirname $(pwd)) + base_dir=$(basename $(pwd)) + add2virtualenv "../$base_dir" + contents="$(lssitepackages)" + actual=$(echo $contents | grep $base_dir) + expected=$(echo $contents) + assertSame "$expected" "$actual" + deactivate +} + +test_lssitepackages_no_workon_home () { + old_home="$WORKON_HOME" + export WORKON_HOME="$WORKON_HOME/not_there" + rm -rf "$WORKON_HOME" + lssitepackages >"$old_home/output" 2>&1 + output=$(cat "$old_home/output") + assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" + WORKON_HOME="$old_home" +} + +test_lsvirtualenv_no_workon_home () { + old_home="$WORKON_HOME" + export WORKON_HOME="$WORKON_HOME/not_there" + rm -rf "$WORKON_HOME" + lsvirtualenv >"$old_home/output" 2>&1 + output=$(cat "$old_home/output") + assertTrue "Did not see expected message" "echo $output | grep -q 'does not exist'" + WORKON_HOME="$old_home" +} + +test_lsvirtualenv_space_in_workon_home () { + old_home="$WORKON_HOME" + export WORKON_HOME="$WORKON_HOME/with space" + mkdir "$WORKON_HOME" + (cd "$WORKON_HOME"; virtualenv testenv) >/dev/null 2>&1 + lsvirtualenv -b >"$old_home/output" + output=$(cat "$old_home/output") + assertTrue "Did not see expected message in \"$output\"" "echo $output | grep -q 'testenv'" + WORKON_HOME="$old_home" +} + +test_lsvirtualenv_space_in_env_name () { + # Only test with leading and internal spaces. Directory names with trailing spaces are legal, + # and work with virtualenv on OSX, but error out on Linux. + mkvirtualenv " env with space" >/dev/null 2>&1 + lsvirtualenv -b >"$WORKON_HOME/output" 2>&1 + assertTrue "Did not see expected message in \"$output\"" "cat \"$WORKON_HOME/output\" | grep -q ' env with space'" +} + +test_lsvirtualenv_blank_lines () { + # There should be no blank lines in the list of virtualenvs + mkvirtualenv "at-least-one-env" >/dev/null 2>&1 + assertFalse "Found blank line in virtualenvwrapper_show_workon_options output" "virtualenvwrapper_show_workon_options | grep -q '^$'" +} + + +. "$test_dir/shunit2" diff --git a/tests/test_mktmpenv.sh b/tests/test_mktmpenv.sh new file mode 100755 index 0000000..d2a8785 --- /dev/null +++ b/tests/test_mktmpenv.sh @@ -0,0 +1,59 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +test_mktmpenv_no_name() { + before=$(lsvirtualenv -b) + mktmpenv >/dev/null 2>&1 + after=$(lsvirtualenv -b) + assertFalse "Environment was not created" "[ \"$before\" = \"$after\" ]" + assertSame "$VIRTUAL_ENV" "$(pwd)" +} + +test_mktmpenv_name() { + mktmpenv name-given-by-user >/dev/null 2>&1 + RC=$? + assertTrue "Error was not detected" "[ $RC -ne 0 ]" +} + +test_mktmpenv_n() { + mktmpenv -n >/dev/null 2>&1 + assertNotSame "$VIRTUAL_ENV" "$(pwd)" +} + +test_mktmpenv_no_cd() { + mktmpenv --no-cd >/dev/null 2>&1 + assertNotSame "$VIRTUAL_ENV" "$(pwd)" +} + +test_mktmpenv_virtualenv_args() { + mktmpenv --without-pip >/dev/null 2>&1 + contents="$(lssitepackages)" + assertFalse "found pip in site-packages: ${contents}" "echo $contents | grep -q pip" +} + +test_deactivate() { + mktmpenv >/dev/null 2>&1 + assertTrue "Environment was not created" "[ ! -z \"$VIRTUAL_ENV\" ]" + env_name=$(basename "$VIRTUAL_ENV") + deactivate >/dev/null 2>&1 + assertFalse "Environment still exists" "[ -d \"$WORKON_HOME/$env_name\" ]" +} + +. "$test_dir/shunit2" diff --git a/tests/test_mkvirtualenv.sh b/tests/test_mkvirtualenv.sh new file mode 100755 index 0000000..d8ecf11 --- /dev/null +++ b/tests/test_mkvirtualenv.sh @@ -0,0 +1,220 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo + rm -f "$TMPDIR/catch_output" +} + +test_create() { + mkvirtualenv "env1" >/dev/null 2>&1 + assertTrue "Environment directory was not created" "[ -d $WORKON_HOME/env1 ]" + for hook in postactivate predeactivate postdeactivate + do + assertTrue "env1 $hook was not created" "[ -f $WORKON_HOME/env1/bin/$hook ]" + assertFalse "env1 $hook is executable" "[ -x $WORKON_HOME/env1/bin/$hook ]" + done +} + +test_create_space_in_name() { + # Only test with leading and internal spaces. Directory names with trailing spaces are legal, + # and work with virtualenv on OSX, but error out on Linux. + mkvirtualenv " env with space" >/dev/null 2>&1 + assertTrue "Environment directory was not created" "[ -d \"$WORKON_HOME/ env with space\" ]" + for hook in postactivate predeactivate postdeactivate + do + assertTrue "$hook was not created" "[ -f \"$WORKON_HOME/ env with space/bin/$hook\" ]" + assertFalse "$hook is executable" "[ -x \"$WORKON_HOME/ env with space/bin/$hook\" ]" + done + assertTrue virtualenvwrapper_verify_active_environment + env_name=$(basename "$VIRTUAL_ENV") + assertSame " env with space" "$env_name" +} + +test_activates () { + mkvirtualenv "env2" >/dev/null 2>&1 + assertTrue virtualenvwrapper_verify_active_environment + assertSame "env2" $(basename "$VIRTUAL_ENV") +} + +test_hooks () { + echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" + echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" + chmod +x "$WORKON_HOME/premkvirtualenv" + + echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" + mkvirtualenv "env3" >/dev/null 2>&1 + output=$(cat "$TMPDIR/catch_output") + workon_home_as_pwd=$(cd $WORKON_HOME; pwd) + expected="GLOBAL premkvirtualenv $workon_home_as_pwd env3 +GLOBAL postmkvirtualenv" + assertSame "$expected" "$output" + rm -f "$WORKON_HOME/premkvirtualenv" + rm -f "$WORKON_HOME/postmkvirtualenv" + deactivate + rmvirtualenv "env3" >/dev/null 2>&1 +} + +test_no_virtualenv () { + # Find "which" before we change the path + which=$(which which) + old_path="$PATH" + PATH="/bin:/usr/sbin:/sbin" + venv=$($which virtualenv 2>/dev/null) + if [ ! -z "$venv" ] + then + echo "FOUND \"$venv\" in PATH so skipping this test" + export PATH="$old_path" + return 0 + fi + mkvirtualenv should_not_be_created >/dev/null 2>&1 + RC=$? + # Restore the path before testing because + # the test script depends on commands in the + # path. + export PATH="$old_path" + assertSame "$RC" "1" +} + +test_no_args () { + mkvirtualenv 2>/dev/null 1>&2 + RC=$? + assertSame "2" "$RC" +} + +test_no_workon_home () { + old_home="$WORKON_HOME" + export WORKON_HOME="$WORKON_HOME/not_there" + mkvirtualenv should_be_created >"$old_home/output" 2>&1 + output=$(cat "$old_home/output") + assertTrue "Did not see expected message in \"$output\"" "cat \"$old_home/output\" | grep 'does not exist'" + assertTrue "Did not create environment" "[ -d \"$WORKON_HOME/should_be_created\" ]" + WORKON_HOME="$old_home" +} + +test_mkvirtualenv_hooks_system_site_packages () { + # See issue #189 + + echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" + echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" + chmod +x "$WORKON_HOME/premkvirtualenv" + + echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" + mkvirtualenv --system-site-packages "env189" >/dev/null 2>&1 + output=$(cat "$TMPDIR/catch_output") + workon_home_as_pwd=$(cd $WORKON_HOME; pwd) + expected="GLOBAL premkvirtualenv $workon_home_as_pwd env189 +GLOBAL postmkvirtualenv" + assertSame "$expected" "$output" + rm -f "$WORKON_HOME/premkvirtualenv" + rm -f "$WORKON_HOME/postmkvirtualenv" + deactivate + rmvirtualenv "env189" >/dev/null 2>&1 +} + +test_mkvirtualenv_args () { + # See issue #102 + VIRTUALENVWRAPPER_VIRTUALENV_ARGS="--without-pip" + # With the argument, verify that they are not copied. + mkvirtualenv "without_pip" >/dev/null 2>&1 + local RC=$? + assertTrue "mkvirtualenv failed" "[ $RC -eq 0 ]" + contents="$(lssitepackages)" + assertFalse "found pip in site-packages: ${contents}" "echo $contents | grep -q pip" + rmvirtualenv "without_pip" >/dev/null 2>&1 + unset VIRTUALENVWRAPPER_VIRTUALENV_ARGS +} + +test_no_such_virtualenv () { + VIRTUALENVWRAPPER_VIRTUALENV=/path/to/missing/program + + echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" + echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" + chmod +x "$WORKON_HOME/premkvirtualenv" + + echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" + mkvirtualenv "env3" >/dev/null 2>&1 + output=$(cat "$TMPDIR/catch_output" 2>/dev/null) + workon_home_as_pwd=$(cd $WORKON_HOME; pwd) + expected="" + assertSame "$expected" "$output" + rm -f "$WORKON_HOME/premkvirtualenv" + rm -f "$WORKON_HOME/postmkvirtualenv" + + VIRTUALENVWRAPPER_VIRTUALENV=virtualenv +} + +test_virtualenv_fails () { + # Test to reproduce the conditions in issue #76 + # https://bitbucket.org/dhellmann/virtualenvwrapper/issue/76/ + # + # Should not run the premkvirtualenv or postmkvirtualenv hooks + # because the environment is not created and even the + # premkvirtualenv hooks are run *after* the environment exists + # (but before it is activated). + export pre_test_dir=$(cd "$test_dir"; pwd) + + VIRTUALENVWRAPPER_VIRTUALENV=false + + echo "#!/bin/sh" > "$WORKON_HOME/premkvirtualenv" + echo "echo GLOBAL premkvirtualenv \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/premkvirtualenv" + chmod +x "$WORKON_HOME/premkvirtualenv" + + echo "echo GLOBAL postmkvirtualenv >> $TMPDIR/catch_output" > "$WORKON_HOME/postmkvirtualenv" + mkvirtualenv "env3" >/dev/null 2>&1 + output=$(cat "$TMPDIR/catch_output" 2>/dev/null) + workon_home_as_pwd=$(cd $WORKON_HOME; pwd) + expected="" + assertSame "$expected" "$output" + rm -f "$WORKON_HOME/premkvirtualenv" + rm -f "$WORKON_HOME/postmkvirtualenv" + + VIRTUALENVWRAPPER_VIRTUALENV=virtualenv +} + +test_mkvirtualenv_python_not_sticky () { + typeset _save=$VIRTUALENVWRAPPER_VIRTUALENV + VIRTUALENVWRAPPER_VIRTUALENV=true + mkvirtualenv --python blah foo + assertSame "" "$interpreter" + VIRTUALENVWRAPPER_VIRTUALENV=$_save +} + +test_mkvirtualenv_python_short_option () { + typeset _save=$VIRTUALENVWRAPPER_VIRTUALENV + VIRTUALENVWRAPPER_VIRTUALENV=echo + output="$(mkvirtualenv -p python foo)" + assertSame "--python=python foo" "$output" + VIRTUALENVWRAPPER_VIRTUALENV=$_save +} + +test_mkvirtualenv_python_long_option () { + typeset _save=$VIRTUALENVWRAPPER_VIRTUALENV + VIRTUALENVWRAPPER_VIRTUALENV=echo + output="$(mkvirtualenv --python python foo)" + assertSame "--python=python foo" "$output" + VIRTUALENVWRAPPER_VIRTUALENV=$_save +} + +test_mkvirtualenv_python_long_option_equal () { + typeset _save=$VIRTUALENVWRAPPER_VIRTUALENV + VIRTUALENVWRAPPER_VIRTUALENV=echo + output="$(mkvirtualenv --python=python foo)" + assertSame "--python=python foo" "$output" + VIRTUALENVWRAPPER_VIRTUALENV=$_save +} + + +. "$test_dir/shunit2" diff --git a/tests/test_mkvirtualenv_associate.sh b/tests/test_mkvirtualenv_associate.sh new file mode 100755 index 0000000..e617f94 --- /dev/null +++ b/tests/test_mkvirtualenv_associate.sh @@ -0,0 +1,133 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" + rm -f "$test_dir/requirements.txt" +} + +setUp () { + echo + echo "#!/bin/sh" > "$WORKON_HOME/preactivate" + echo "#!/bin/sh" > "$WORKON_HOME/postactivate" + rm -f "$TMPDIR/catch_output" + cd "$WORKON_HOME" +} + +test_associate() { + n=1 + project="$WORKON_HOME/project$n" + mkdir "$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 + assertTrue ".project not found" "[ -f $ptrfile ]" + assertEquals "$ptrfile contains wrong content" "$project" "$(cat $ptrfile)" +} + +test_associate_relative_path() { + n=2 + project="project$n" + mkdir "$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 + assertTrue ".project not found" "[ -f $ptrfile ]" + assertEquals \ + "$ptrfile contains wrong content" \ + "$WORKON_HOME/$project" \ + "$(cat $ptrfile | sed 's|^/private||')" +} + +test_associate_not_a_directory() { + n=3 + project="project$n" + touch "$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 + RC=$? + assertTrue "mkvirtualenv should have failed" "[ $RC -ne 0 ]" +} + +test_associate_does_not_exist() { + n=4 + project="project$n" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 + RC=$? + assertTrue "mkvirtualenv should have failed" "[ $RC -ne 0 ]" +} + +test_preactivate() { + n=5 + project="project$n" + mkdir "$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + cat - >"$WORKON_HOME/preactivate" <> "$TMPDIR/catch_output" +else + echo noexists >> "$TMPDIR/catch_output" +fi +EOF + chmod +x "$WORKON_HOME/preactivate" + mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 + assertSame "preactivate did not find file" "exists" "$(cat $TMPDIR/catch_output)" +} + +test_postactivate() { + n=6 + project="project$n" + mkdir "$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" +cat - >"$WORKON_HOME/postactivate" <> "$TMPDIR/catch_output" +else + echo noexists >> "$TMPDIR/catch_output" +fi +EOF + chmod +x "$WORKON_HOME/postactivate" + mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 + assertSame "postactivate did not find file" "exists" "$(cat $TMPDIR/catch_output)" +} + +test_associate_relative_with_dots() { + cd "$WORKON_HOME" + n=7 + project="project$n" + mkdir $project + mkdir $project.sibling + cd $project.sibling + # Change the reference to a sibling directory + project="../$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv -a "$project" "$env" >/dev/null 2>&1 + assertTrue ".project not found" "[ -f $ptrfile ]" + # Sometimes OS X prepends /private on the front of the temporary + # directory, but the directory is the same as without it, so strip + # it if we see the value in the project file. + assertEquals \ + "$ptrfile contains wrong content" \ + "$WORKON_HOME/project$n" \ + "$(cat $ptrfile | sed 's|^/private||')" +} + +. "$test_dir/shunit2" diff --git a/tests/test_mkvirtualenv_install.sh b/tests/test_mkvirtualenv_install.sh new file mode 100755 index 0000000..faf1652 --- /dev/null +++ b/tests/test_mkvirtualenv_install.sh @@ -0,0 +1,34 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" + rm -f "$test_dir/requirements.txt" +} + +setUp () { + echo +} + +test_single_package () { + mkvirtualenv -i IPy "env4" + installed=$(pip freeze) + assertTrue "IPy not found in $installed" "pip freeze | grep IPy" +} + +test_multiple_packages () { + mkvirtualenv -i IPy -i WebTest "env5" + installed=$(pip freeze) + assertTrue "IPy not found in $installed" "pip freeze | grep IPy" + assertTrue "WebTest not found in $installed" "pip freeze | grep WebTest" +} + +. "$test_dir/shunit2" diff --git a/tests/test_mkvirtualenv_requirements.sh b/tests/test_mkvirtualenv_requirements.sh new file mode 100755 index 0000000..744d4fd --- /dev/null +++ b/tests/test_mkvirtualenv_requirements.sh @@ -0,0 +1,28 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" + rm -f "$TMPDIR/requirements.txt" +} + +setUp () { + echo +} + +test_requirements_file () { + echo "IPy" > "$TMPDIR/requirements.txt" + mkvirtualenv -r "$TMPDIR/requirements.txt" "env3" >/dev/null 2>&1 + installed=$(pip freeze) + assertTrue "IPy not found in $installed" "pip freeze | grep IPy" +} + +. "$test_dir/shunit2" diff --git a/tests/test_project.sh b/tests/test_project.sh new file mode 100755 index 0000000..10cb53e --- /dev/null +++ b/tests/test_project.sh @@ -0,0 +1,75 @@ +# -*- mode: shell-script -*- + +test_dir=$(dirname $0) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + rm -rf "$PROJECT_HOME" + mkdir -p "$PROJECT_HOME" +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" + rm -rf "$PROJECT_HOME" +} + +setUp () { + echo + unset VIRTUALENVWRAPPER_INITIALIZED +} + +test_initialize() { + load_wrappers + for hook in premkproject postmkproject + do + assertTrue "Global $hook was not created" "[ -f $WORKON_HOME/$hook ]" + assertTrue "Global $hook is not executable" "[ -x $WORKON_HOME/$hook ]" + done +} + +test_initialize_hook_dir() { + export VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME/hooks" + mkdir -p "$VIRTUALENVWRAPPER_HOOK_DIR" + load_wrappers + for hook in premkproject postmkproject + do + assertTrue "Global $hook was not created" "[ -f $VIRTUALENVWRAPPER_HOOK_DIR/$hook ]" + assertTrue "Global $hook is not executable" "[ -x $VIRTUALENVWRAPPER_HOOK_DIR/$hook ]" + done + VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME" +} + +test_virtualenvwrapper_verify_project_home() { + assertTrue "PROJECT_HOME not verified" virtualenvwrapper_verify_project_home +} + +test_virtualenvwrapper_verify_project_home_missing_dir() { + old_home="$PROJECT_HOME" + PROJECT_HOME="$PROJECT_HOME/not_there" + assertFalse "PROJECT_HOME verified unexpectedly" virtualenvwrapper_verify_project_home + PROJECT_HOME="$old_home" +} + +test_virtualenvwrapper_postactivate_hook() { + load_wrappers + mkproject "test_project_hook" + mkdir .virtualenvwrapper + echo "export TEST_PROJECT_HOOK_VAR=true" > .virtualenvwrapper/postactivate + echo "unset TEST_PROJECT_HOOK_VAR" > .virtualenvwrapper/predeactivate + deactivate + + # Variable should not be set to start + assertSame "${TEST_PROJECT_HOOK_VAR}" "" + + # Activating the env should set it + workon "test_project_hook" + assertSame "true" "${TEST_PROJECT_HOOK_VAR}" + + # Deactivating should unset it + deactivate + assertSame "" "${TEST_PROJECT_HOOK_VAR}" +} + +. "$test_dir/shunit2" diff --git a/tests/test_project_activate.sh b/tests/test_project_activate.sh new file mode 100755 index 0000000..e4b52b3 --- /dev/null +++ b/tests/test_project_activate.sh @@ -0,0 +1,93 @@ +# -*- mode: shell-script -*- + +test_dir=$(dirname $0) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + rm -rf "$PROJECT_HOME" + mkdir -p "$PROJECT_HOME" + load_wrappers +} + +# oneTimeTearDown() { +# rm -rf "$WORKON_HOME" +# rm -rf "$PROJECT_HOME" +# } + +setUp () { + echo + # In case the user has the value set, we need to force it to the default + VIRTUALENVWRAPPER_WORKON_CD=1 +} + +tearDown () { + # In case a test has changed the value, we need to force it to the default + VIRTUALENVWRAPPER_WORKON_CD=1 +} + +test_activate () { + mkproject myproject >/dev/null 2>&1 + deactivate + cd $TMPDIR + assertSame "" "$VIRTUAL_ENV" + workon myproject + assertSame "myproject" "$(basename $VIRTUAL_ENV)" + assertSame "$PROJECT_HOME/myproject" "$(pwd)" + deactivate +} + +test_activate_n () { + mkproject myproject_n >/dev/null 2>&1 + assertTrue $? + deactivate + cd $TMPDIR + assertSame "" "$VIRTUAL_ENV" + workon -n myproject_n + assertSame "myproject_n" "$(basename $VIRTUAL_ENV)" + assertSame "$TMPDIR" "$(pwd)" + deactivate +} + +test_activate_no_cd () { + mkproject myproject_no_cd >/dev/null 2>&1 + assertTrue $? + deactivate + cd $TMPDIR + assertSame "" "$VIRTUAL_ENV" + workon --no-cd myproject_no_cd + assertSame "myproject_no_cd" "$(basename $VIRTUAL_ENV)" + assertSame "$TMPDIR" "$(pwd)" + deactivate +} + +test_activate_workon_cd_disabled () { + export VIRTUALENVWRAPPER_WORKON_CD=0 + mkproject myproject_cd_disabled >/dev/null 2>&1 + assertTrue $? + deactivate + cd $TMPDIR + assertSame "" "$VIRTUAL_ENV" + workon myproject_cd_disabled + assertSame "myproject_cd_disabled" "$(basename $VIRTUAL_ENV)" + assertSame "$TMPDIR" "$(pwd)" + deactivate +} + +test_space_in_path () { + old_project_home="$PROJECT_HOME" + PROJECT_HOME="$PROJECT_HOME/with spaces" + mkdir -p "$PROJECT_HOME" + mkproject "myproject" >/dev/null 2>&1 + deactivate + cd $TMPDIR + workon "myproject" + assertSame "myproject" "$(basename $VIRTUAL_ENV)" + assertSame "$PROJECT_HOME/myproject" "$(pwd)" + deactivate + PROJECT_HOME="$old_project_home" +} + + +. "$test_dir/shunit2" diff --git a/tests/test_project_cd.sh b/tests/test_project_cd.sh new file mode 100755 index 0000000..e336139 --- /dev/null +++ b/tests/test_project_cd.sh @@ -0,0 +1,55 @@ +# -*- mode: shell-script -*- + +test_dir=$(dirname $0) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + rm -rf "$PROJECT_HOME" + mkdir -p "$PROJECT_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" + rm -rf "$PROJECT_HOME" +} + +setUp () { + echo +} + +test_with_project () { + mkproject myproject >/dev/null 2>&1 + cd $TMPDIR + cdproject + assertSame "$PROJECT_HOME/myproject" "$(pwd)" + deactivate +} + +test_without_project () { + mkvirtualenv myproject >/dev/null 2>&1 + cd $TMPDIR + output=$(cdproject 2>&1) + echo "$output" | grep -q "No project set" + RC=$? + assertSame "1" "$RC" + deactivate +} + +test_space_in_path () { + ( + set -e + PROJECT_HOME="$PROJECT_HOME/with spaces" + mkdir -p "$PROJECT_HOME" + mkproject "myproject" >/dev/null 2>&1 + cd "$WORKON_HOME" + cdproject + test "$PROJECT_HOME/myproject" = "$PWD" + ) + assertTrue "Did not cd to project directory" $? +} + + +. "$test_dir/shunit2" diff --git a/tests/test_project_mk.sh b/tests/test_project_mk.sh new file mode 100755 index 0000000..17a9b90 --- /dev/null +++ b/tests/test_project_mk.sh @@ -0,0 +1,91 @@ +# -*- mode: shell-script -*- + +test_dir=$(dirname $0) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + rm -rf "$PROJECT_HOME" + mkdir -p "$PROJECT_HOME" + export VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME/hooks" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" + rm -rf "$PROJECT_HOME" +} + +setUp () { + echo + rm -f "$TMPDIR/catch_output" +} + +tearDown () { + type deactivate >/dev/null 2>&1 && deactivate +} + +test_create_directories () { + mkproject myproject1 >/dev/null 2>&1 + assertTrue "env directory not created" "[ -d $WORKON_HOME/myproject1 ]" + assertTrue "project directory not created" "[ -d $PROJECT_HOME/myproject1 ]" +} + +test_create_virtualenv () { + mkproject myproject2 >/dev/null 2>&1 + assertSame "myproject2" $(basename "$VIRTUAL_ENV") + assertSame "$PROJECT_HOME/myproject2" "$(cat $VIRTUAL_ENV/.project)" +} + +test_hooks () { + echo "echo GLOBAL premkproject \`pwd\` \"\$@\" >> \"$TMPDIR/catch_output\"" >> "$VIRTUALENVWRAPPER_HOOK_DIR/premkproject" + chmod +x "$VIRTUALENVWRAPPER_HOOK_DIR/premkproject" + echo "echo GLOBAL postmkproject \`pwd\` >> $TMPDIR/catch_output" > "$VIRTUALENVWRAPPER_HOOK_DIR/postmkproject" + + mkproject myproject3 >/dev/null 2>&1 + + output=$(cat "$TMPDIR/catch_output") + + expected="GLOBAL premkproject $WORKON_HOME myproject3 +GLOBAL postmkproject $PROJECT_HOME/myproject3" + assertSame "$expected" "$output" + + rm -f "$VIRTUALENVWRAPPER_HOOK_DIR/premkproject" + rm -f "$VIRTUALENVWRAPPER_HOOK_DIR/postmkproject" +} + +test_no_project_home () { + old_home="$PROJECT_HOME" + export PROJECT_HOME="$PROJECT_HOME/not_there" + output=`mkproject should_not_be_created 2>&1` + assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" + PROJECT_HOME="$old_home" +} + +test_project_exists () { + mkproject myproject4 >/dev/null 2>&1 + output=`mkproject myproject4 2>&1` + assertTrue "Did not see expected message 'already exists' in: $output" "echo $output | grep 'already exists'" + output=`mkproject -f myproject4 2>&1` + assertFalse "Saw unexpected message 'already exists' in: $output" "echo $output | grep 'already exists'" +} + +test_same_workon_and_project_home () { + old_project_home="$PROJECT_HOME" + export PROJECT_HOME="$WORKON_HOME" + mkproject myproject5 >/dev/null 2>&1 + assertTrue "env directory not created" "[ -d $WORKON_HOME/myproject1 ]" + assertTrue "project directory was created" "[ -d $old_project_home/myproject1 ]" + PROJECT_HOME="$old_project_home" +} + +test_alternate_linkage_filename () { + export VIRTUALENVWRAPPER_PROJECT_FILENAME=".not-project" + mkproject myproject6 >/dev/null 2>&1 + assertSame "myproject6" $(basename "$VIRTUAL_ENV") + assertSame "$PROJECT_HOME/myproject6" "$(cat $VIRTUAL_ENV/.not-project)" + export VIRTUALENVWRAPPER_PROJECT_FILENAME=".project" +} + +. "$test_dir/shunit2" diff --git a/tests/test_project_templates.sh b/tests/test_project_templates.sh new file mode 100755 index 0000000..32c7fcf --- /dev/null +++ b/tests/test_project_templates.sh @@ -0,0 +1,35 @@ +# -*- mode: shell-script -*- + +test_dir=$(dirname $0) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + (cd "$test_dir/testtemplate" && rm -rf build && "$VIRTUAL_ENV/bin/python" -m pip install .) + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + rm -rf "$PROJECT_HOME" + mkdir -p "$PROJECT_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" + rm -rf "$PROJECT_HOME" +} + +setUp () { + echo +} + +test_list_templates () { + output=$(mkproject -h 2>&1) + assertTrue "Did not find test template in \"$output\"" "echo \"$output\" | grep -q 'Creates a test file'" +} + +test_apply_template () { + mkproject -t test proj1 >/dev/null 2>&1 + assertTrue "Test file not created" "[ -f TEST_FILE ]" + assertTrue "project name not found" "grep -q proj1 TEST_FILE" +} + +. "$test_dir/shunit2" diff --git a/tests/test_rmvirtualenv.sh b/tests/test_rmvirtualenv.sh new file mode 100755 index 0000000..9836344 --- /dev/null +++ b/tests/test_rmvirtualenv.sh @@ -0,0 +1,76 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + mkvirtualenv "deleteme" >/dev/null 2>&1 + # Only test with leading and internal spaces. Directory names with trailing spaces are legal, + # and work with virtualenv on OSX, but error out on Linux. + mkvirtualenv " env with space" >/dev/null 2>&1 + deactivate >/dev/null 2>&1 +} + +test_remove () { + assertTrue "[ -d $WORKON_HOME/deleteme ]" + rmvirtualenv "deleteme" + assertFalse "[ -d $WORKON_HOME/deleteme ]" +} + +test_remove_space_in_name () { + assertTrue "[ -d $WORKON_HOME/\" env with space\" ]" + rmvirtualenv " env with space" + assertFalse "[ -d $WORKON_HOME/\" env with space\" ]" +} + +test_remove_several_envs () { + assertTrue "[ -d $WORKON_HOME/deleteme ]" + assertTrue "[ -d $WORKON_HOME/\" env with space\" ]" + rmvirtualenv deleteme " env with space" + assertFalse "[ -d $WORKON_HOME/deleteme ]" + assertFalse "[ -d $WORKON_HOME/\" env with space\" ]" +} + +test_within_virtualenv () { + mkvirtualenv "deleteme2" >/dev/null 2>&1 + assertTrue "[ -d $WORKON_HOME/deleteme2 ]" + cdvirtualenv + assertSame "$VIRTUAL_ENV" "$(pwd)" + deactivate + rmvirtualenv "deleteme2" + assertSame "$WORKON_HOME" "$(pwd)" + assertFalse "[ -d $WORKON_HOME/deleteme2 ]" +} + +test_rm_aliased () { + alias rm='rm -i' + rmvirtualenv "deleteme" + unalias rm +} + +test_no_such_env () { + assertFalse "[ -d $WORKON_HOME/deleteme2 ]" + assertTrue "rmvirtualenv deleteme2" +} + +test_no_workon_home () { + old_home="$WORKON_HOME" + export WORKON_HOME="$WORKON_HOME/not_there" + rmvirtualenv should_not_be_created >"$old_home/output" 2>&1 + output=$(cat "$old_home/output") + assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" + WORKON_HOME="$old_home" +} + + +. "$test_dir/shunit2" diff --git a/tests/test_run_hook.sh b/tests/test_run_hook.sh new file mode 100755 index 0000000..7ea0b9d --- /dev/null +++ b/tests/test_run_hook.sh @@ -0,0 +1,67 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo + rm -f "$WORKON_HOME/initialize" + rm -f "$WORKON_HOME/prermvirtualenv" + rm -f "$TMPDIR/catch_output" +} + +test_virtualenvwrapper_run_hook() { + echo "echo run >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" + chmod +x "$WORKON_HOME/initialize" + virtualenvwrapper_run_hook "initialize" + output=$(cat "$TMPDIR/catch_output") + expected="run" + assertSame "$expected" "$output" +} + +test_virtualenvwrapper_run_hook_alternate_dir() { + mkdir "$WORKON_HOME/hooks" + echo "echo WORKON_HOME >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" + echo "echo WORKON_HOME/hooks >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/hooks/initialize" + chmod +x "$WORKON_HOME/initialize" + chmod +x "$WORKON_HOME/hooks/initialize" + VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME/hooks" + virtualenvwrapper_run_hook "initialize" + output=$(cat "$TMPDIR/catch_output") + expected="WORKON_HOME/hooks" + assertSame "$expected" "$output" + VIRTUALENVWRAPPER_HOOK_DIR="$WORKON_HOME" +} + +test_virtualenvwrapper_source_hook_permissions() { + echo "echo run >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/initialize" + chmod -x "$WORKON_HOME/initialize" + virtualenvwrapper_run_hook "initialize" + output=$(cat "$TMPDIR/catch_output") + expected="run" + assertSame "$expected" "$output" +} + +test_virtualenvwrapper_run_hook_permissions() { + echo "#!/bin/sh" > "$WORKON_HOME/prermvirtualenv" + echo "echo run $@ >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/prermvirtualenv" + chmod 0444 "$WORKON_HOME/prermvirtualenv" + touch "$TMPDIR/catch_output" + error=$(virtualenvwrapper_run_hook "pre_rmvirtualenv" "foo" 2>&1 | grep "could not run" | cut -f2- -d'[' | cut -f1 -d:) + output=$(cat "$TMPDIR/catch_output") + expected="" + assertSame "$expected" "$output" + assertSame "Errno 13] Permission denied" "$error" +} + +. "$test_dir/shunit2" diff --git a/tests/test_setvirtualenvproject.sh b/tests/test_setvirtualenvproject.sh new file mode 100755 index 0000000..c29dcd3 --- /dev/null +++ b/tests/test_setvirtualenvproject.sh @@ -0,0 +1,95 @@ +# -*- mode: shell-script -*- + +test_dir=$(dirname $0) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + rm -rf "$PROJECT_HOME" + mkdir -p "$PROJECT_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" + rm -rf "$PROJECT_HOME" +} + +setUp () { + echo +} + +test_setvirtualenvproject() { + n=1 + project="$WORKON_HOME/project$n" + mkdir "$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv "$env" >/dev/null 2>&1 + setvirtualenvproject "$env" "$project" >/dev/null 2>&1 + assertTrue ".project not found" "[ -f $ptrfile ]" + assertEquals "$ptrfile contains wrong content" "$project" "$(cat $ptrfile)" +} + +test_setvirtualenvproject_relative_path() { + cd "$WORKON_HOME" + n=2 + project="project$n" + mkdir "$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv "$env" >/dev/null 2>&1 + setvirtualenvproject "$env" "$project" >/dev/null 2>&1 + assertTrue ".project not found" "[ -f $ptrfile ]" + assertEquals \ + "$ptrfile contains wrong content" \ + "$WORKON_HOME/$project" \ + "$(cat $ptrfile | sed 's|^/private||')" +} + +test_setvirtualenvproject_not_a_directory() { + cd "$WORKON_HOME" + n=3 + project="project$n" + touch "$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv "$env" >/dev/null 2>&1 + setvirtualenvproject "$env" "$project" >/dev/null 2>&1 + RC=$? + assertTrue "setvirtualenvproject should have failed" "[ $RC -ne 0 ]" +} + +test_setvirtualenvproject_does_not_exist() { + n=4 + project="project$n" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv "$env" >/dev/null 2>&1 + setvirtualenvproject "$env" "$project" >/dev/null 2>&1 + RC=$? + assertTrue "setvirtualenvproject should have failed" "[ $RC -ne 0 ]" +} + +test_setvirtualenvproject_relative_with_dots() { + cd "$WORKON_HOME" + n=5 + project="project$n" + mkdir $project + mkdir $project.sibling + cd $project.sibling + # Change the reference to a sibling directory + project="../$project" + env="env$n" + ptrfile="$WORKON_HOME/$env/.project" + mkvirtualenv "$env" >/dev/null 2>&1 + setvirtualenvproject "$env" "$project" >/dev/null 2>&1 + assertTrue ".project not found" "[ -f $ptrfile ]" + assertEquals \ + "$ptrfile contains wrong content" \ + "$WORKON_HOME/project$n" \ + "$(cat $ptrfile | sed 's|^/private||')" +} + +. "$test_dir/shunit2" diff --git a/tests/test_support.sh b/tests/test_support.sh new file mode 100755 index 0000000..cf1ac23 --- /dev/null +++ b/tests/test_support.sh @@ -0,0 +1,29 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +test_get_python_version () { + expected="$($VIRTUAL_ENV/bin/python -c 'import sys; sys.stdout.write("%s.%s\n" % sys.version_info[:2])')" + echo "Expecting: $expected" + vers=$(virtualenvwrapper_get_python_version) + echo "Got : $vers" + assertSame "$expected" "$vers" +} + + +. "$test_dir/shunit2" diff --git a/tests/test_tempfile.sh b/tests/test_tempfile.sh new file mode 100755 index 0000000..f3c0842 --- /dev/null +++ b/tests/test_tempfile.sh @@ -0,0 +1,91 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +#export HOOK_VERBOSE_OPTION=-v + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + echo $PYTHONPATH +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo +} + +test_tempfile () { + if [ "$(uname)" = "Darwin" ]; then + # macOS doesn't seem to allow controlling where mktemp creates + # the output files + return 0 + fi + filename=$(virtualenvwrapper_tempfile hook) + assertTrue "Filename is empty" "[ ! -z \"$filename\" ]" + assertTrue "File doesn't exist" "[ -f \"$filename\" ]" + rm -f $filename + comparable_tmpdir=$(echo $TMPDIR | sed 's|/$||') + comparable_dirname=$(dirname $filename | sed 's|/$||') + assertSame "Temporary directory \"$TMPDIR\" and path not the same for $filename" "$comparable_tmpdir" "$comparable_dirname" + assertTrue "virtualenvwrapper-hook not in filename." "echo $filename | grep virtualenvwrapper-hook" +} + +test_bad_mktemp() { + # All of the following bogus mktemp programs should cause + # virtualenvwrapper_tempfile to return non-zero status + mktemp_nonzero() { return 1; } + mktemp_empty_string() { return 0; } + mktemp_missing_executable() { /foo/bar/baz/qux 2>/dev/null; } # returns status 127 + mktemp_missing_result() { echo /foo/bar/baz/qux; } + + for mktemp_func in mktemp_nonzero mktemp_empty_string \ + mktemp_missing_executable mktemp_missing_result + do + virtualenvwrapper_mktemp() { $mktemp_func "$@"; } + filename=$(virtualenvwrapper_tempfile hook 2>/dev/null) + assertSame "($mktemp_func) Unexpected exit code $?" "1" "$?" + done + + # Restore the "real" definition of the replaceable function + virtualenvwrapper_mktemp() { command mktemp "$@"; } +} + +test_no_such_tmpdir () { + if [ "$(uname)" = "Darwin" ]; then + # macOS doesn't seem to allow controlling where mktemp creates + # the output files + return 0 + fi + old_tmpdir="$TMPDIR" + export TMPDIR="$TMPDIR/does-not-exist" + virtualenvwrapper_run_hook "initialize" >/dev/null 2>&1 + RC=$? + assertSame "Unexpected exit code $RC" "1" "$RC" + TMPDIR="$old_tmpdir" +} + +test_tmpdir_not_writable () { + if [ "$(uname)" = "Darwin" ]; then + # macOS doesn't seem to allow controlling where mktemp creates + # the output files + return 0 + fi + old_tmpdir="$TMPDIR" + export TMPDIR="$TMPDIR/cannot-write" + mkdir "$TMPDIR" + chmod ugo-w "$TMPDIR" + virtualenvwrapper_run_hook "initialize" >/dev/null 2>&1 + RC=$? + assertSame "Unexpected exit code $RC" "1" "$RC" + chmod ugo+w "$TMPDIR" + rmdir "$TMPDIR" + TMPDIR="$old_tmpdir" +} + +. "$test_dir/shunit2" diff --git a/tests/test_virtualenvwrapper.sh b/tests/test_virtualenvwrapper.sh new file mode 100755 index 0000000..fe9e689 --- /dev/null +++ b/tests/test_virtualenvwrapper.sh @@ -0,0 +1,41 @@ +# -*- mode: shell-script -*- +# +# Tests for help function 'virtualenvwrapper' + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo + unset VIRTUALENVWRAPPER_SCRIPT + rm -f "$TMPDIR/catch_output" +} + +test_virtualenvwrapper_script_set() { + load_wrappers + assertTrue "VIRTUALENVWRAPPER_SCRIPT is not right: $VIRTUALENVWRAPPER_SCRIPT" \ + "echo $VIRTUALENVWRAPPER_SCRIPT | grep -q /virtualenvwrapper.sh" +} + +test_virtualenvwrapper_version() { + load_wrappers + typeset ver=$(_virtualenvwrapper_version) + assertTrue "version is empty" "[ -n $ver ]" +} + +test_virtualenvwrapper_help_shows_version() { + load_wrappers + typeset pattern="Version: $(_virtualenvwrapper_version)" + assertTrue "version not in command output" "virtualenvwrapper | grep \"$pattern\"" +} + +. "$test_dir/shunit2" diff --git a/tests/test_wipeenv.sh b/tests/test_wipeenv.sh new file mode 100755 index 0000000..f731580 --- /dev/null +++ b/tests/test_wipeenv.sh @@ -0,0 +1,79 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +setUp () { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + echo +} + +tearDown() { + if type deactivate >/dev/null 2>&1 + then + deactivate + fi + rm -rf "$WORKON_HOME" +} + +test_wipeenv () { + mkvirtualenv "wipetest" >/dev/null 2>&1 + (cd tests/testpackage && pip install .) >/dev/null 2>&1 + before="$(pip freeze)" + assertTrue "testpackage not installed" "pip freeze | grep testpackage" + wipeenv >/dev/null 2>&1 + after="$(pip freeze)" + assertFalse "testpackage still installed" "pip freeze | grep testpackage" +} + +test_wipeenv_pip_e () { + mkvirtualenv "wipetest" >/dev/null 2>&1 + (cd tests/testpackage && pip install -e .) >/dev/null 2>&1 + before="$(pip freeze)" + assertTrue "testpackage not installed: $before" "pip freeze | grep testpackage" + wipeenv >/dev/null 2>&1 + after="$(pip freeze)" + assertFalse "testpackage still installed: $after" "pip freeze | grep testpackage" +} + +# test_wipeenv_pip_e_url () { +# mkvirtualenv "wipetest" >/dev/null 2>&1 +# (cd tests/testpackage && pip install -e 'git+https://github.com/kennethreitz/legit.git@3c4d3214811c7892edf903682fdbb44f4050b99a#egg=legit-origin') +# # >/dev/null 2>&1 +# before="$(pip freeze)" +# pip freeze +# assertTrue "legit not installed" "pip freeze | grep legit" +# wipeenv >/dev/null 2>&1 +# after="$(pip freeze)" +# assertFalse "legit still installed" "pip freeze | grep legit" +# } + +test_wipeenv_develop () { + mkvirtualenv "wipetest" >/dev/null 2>&1 + (cd tests/testpackage && pip install -e . --config-settings editable_mode=compat) >/dev/null 2>&1 + before="$(pip freeze)" + assertTrue "testpackage not installed" "pip freeze | grep testpackage" + wipeenv >/dev/null 2>&1 + after="$(pip freeze)" + assertFalse "testpackage still installed" "pip freeze | grep testpackage" +} + +test_empty_env () { + mkvirtualenv "wipetest" >/dev/null 2>&1 + before="$(pip freeze)" + assertFalse "testpackage still installed" "pip freeze | grep testpackage" + wipeenv >/dev/null 2>&1 + after="$(pip freeze)" + assertFalse "testpackage still installed" "pip freeze | grep testpackage" +} + +test_not_active_env () { + mkvirtualenv "wipetest" >/dev/null 2>&1 + deactivate + assertFalse "wipenv did not report an error" "wipeenv >/dev/null 2>&1" +} + +. "$test_dir/shunit2" + diff --git a/tests/test_workon.sh b/tests/test_workon.sh new file mode 100755 index 0000000..25047f9 --- /dev/null +++ b/tests/test_workon.sh @@ -0,0 +1,181 @@ +# -*- mode: shell-script -*- + +test_dir=$(cd $(dirname $0) && pwd) +source "$test_dir/setup.sh" + +oneTimeSetUp() { + rm -rf "$WORKON_HOME" + mkdir -p "$WORKON_HOME" + load_wrappers + mkvirtualenv "test1" >/dev/null 2>&1 + mkvirtualenv "test2" >/dev/null 2>&1 + # Only test with leading and internal spaces. Directory names with trailing spaces are legal, + # and work with virtualenv on OSX, but error out on Linux. + mkvirtualenv " env with space" >/dev/null 2>&1 + deactivate >/dev/null 2>&1 +} + +oneTimeTearDown() { + rm -rf "$WORKON_HOME" +} + +setUp () { + echo + rm -f "$TMPDIR/catch_output" +} + +tearDown () { + deactivate >/dev/null 2>&1 +} + +test_workon () { + workon test1 + assertTrue virtualenvwrapper_verify_active_environment + assertSame "test1" $(basename "$VIRTUAL_ENV") +} + +test_workon_activate_hooks () { + for t in pre post + do + echo "#!/bin/sh" > "$WORKON_HOME/${t}activate" + echo "echo GLOBAL ${t}activate >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/${t}activate" + chmod +x "$WORKON_HOME/${t}activate" + + echo "#!/bin/sh" > "$WORKON_HOME/test2/bin/${t}activate" + echo "echo ENV ${t}activate >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/test1/bin/${t}activate" + chmod +x "$WORKON_HOME/test1/bin/${t}activate" + done + + rm -f "$TMPDIR/catch_output" + touch "$TMPDIR/catch_output" + + workon test1 + + output=$(cat "$TMPDIR/catch_output") + expected="GLOBAL preactivate +ENV preactivate +GLOBAL postactivate +ENV postactivate" + + assertSame "$expected" "$output" + + for t in pre post + do + rm -f "$WORKON_HOME/test1/bin/${t}activate" + rm -f "$WORKON_HOME/${t}activate" + done +} + +test_workon_deactivate_hooks () { + for t in pre post + do + echo "#!/bin/sh" > "$WORKON_HOME/${t}deactivate" + echo "echo GLOBAL ${t}deactivate >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/${t}deactivate" + chmod +x "$WORKON_HOME/${t}deactivate" + + echo "#!/bin/sh" > "$WORKON_HOME/test2/bin/${t}deactivate" + echo "echo ENV ${t}deactivate >> \"$TMPDIR/catch_output\"" >> "$WORKON_HOME/test1/bin/${t}deactivate" + chmod +x "$WORKON_HOME/test1/bin/${t}deactivate" + done + + rm -f "$TMPDIR/catch_output" + touch "$TMPDIR/catch_output" + + workon test1 + workon test2 + + output=$(cat "$TMPDIR/catch_output") + expected="ENV predeactivate +GLOBAL predeactivate +ENV postdeactivate +GLOBAL postdeactivate" + + assertSame "$expected" "$output" + + for t in pre post + do + rm -f "$WORKON_HOME/test1/bin/${t}deactivate" + rm -f "$WORKON_HOME/${t}deactivate" + done +} + +test_virtualenvwrapper_show_workon_options () { + mkdir "$WORKON_HOME/not_env" + (cd "$WORKON_HOME"; ln -s test1 link_env) + envs=$(virtualenvwrapper_show_workon_options | tr '\n' ' ') + # On OSX there are two trailing spaces, on Linux one, so compare substring + assertSame " env with space link_env test1 test2 " "${envs:0:37}" + rmdir "$WORKON_HOME/not_env" + rm -f "$WORKON_HOME/link_env" +} + +test_virtualenvwrapper_show_workon_options_grep_options () { + mkdir "$WORKON_HOME/not_env" + (cd "$WORKON_HOME"; ln -s test1 link_env) + export GREP_OPTIONS="--count" + envs=$(virtualenvwrapper_show_workon_options | tr '\n' ' ') + unset GREP_OPTIONS + # On OSX there are two trailing spaces, on Linux one, so compare substring + assertSame " env with space link_env test1 test2 " "${envs:0:37}" + rmdir "$WORKON_HOME/not_env" + rm -f "$WORKON_HOME/link_env" +} + +test_virtualenvwrapper_show_workon_options_chpwd () { + # https://bitbucket.org/dhellmann/virtualenvwrapper/issue/153 + function chpwd { + local SEARCH=' ' + local REPLACE='%20' + local PWD_URL="file://$HOSTNAME${PWD//$SEARCH/$REPLACE}" + printf '\e]7;%s\a' "$PWD_URL" + echo -n "\033]0;${HOST//.*}:$USER\007" + } + mkdir "$WORKON_HOME/not_env" + envs=$(virtualenvwrapper_show_workon_options | tr '\n' ' ') + # On OSX there are two trailing spaces, on Linux one, so compare substring + assertSame " env with space test1 test2 " "${envs:0:28}" + rmdir "$WORKON_HOME/not_env" + rm -f "$WORKON_HOME/link_env" +} + +test_virtualenvwrapper_show_workon_options_no_envs () { + old_home="$WORKON_HOME" + export WORKON_HOME=${TMPDIR:-/tmp}/$$ + # On OSX there is a space and on Linux there is not, so strip all spaces + envs=$(virtualenvwrapper_show_workon_options 2>/dev/null | sed 's/\n //g') + assertSame "" "$envs" + export WORKON_HOME="$old_home" +} + +test_no_workon_home () { + old_home="$WORKON_HOME" + export WORKON_HOME="$WORKON_HOME/not_there" + workon should_not_be_created >"$old_home/output" 2>&1 + output=$(cat "$old_home/output") + assertTrue "Did not see expected message" "echo $output | grep 'does not exist'" + WORKON_HOME="$old_home" +} + +test_workon_dot () { + cd $WORKON_HOME/test1 + workon . + assertTrue virtualenvwrapper_verify_active_environment + assertSame "test1" $(basename "$VIRTUAL_ENV") +} + +test_workon_dot_with_space () { + cd $WORKON_HOME/" env with space" + workon . + assertTrue virtualenvwrapper_verify_active_environment + env_name=$(basename "$VIRTUAL_ENV") + assertSame " env with space" "$env_name" +} + +test_workon_with_space () { + workon " env with space" + assertTrue virtualenvwrapper_verify_active_environment + env_name=$(basename "$VIRTUAL_ENV") + assertSame " env with space" "$env_name" +} + +. "$test_dir/shunit2" diff --git a/tests/testpackage/requirements.txt b/tests/testpackage/requirements.txt new file mode 100644 index 0000000..49fe098 --- /dev/null +++ b/tests/testpackage/requirements.txt @@ -0,0 +1 @@ +setuptools diff --git a/tests/testpackage/setup.py b/tests/testpackage/setup.py new file mode 100644 index 0000000..67623e5 --- /dev/null +++ b/tests/testpackage/setup.py @@ -0,0 +1,14 @@ +# Test package for virtualenvwrapper tests +from setuptools import setup + +version = '1.0' + +setup( + name='testpackage', + version=version, + description="Fake package", + author="Ingeniweb", + author_email='thomas.desvenain@gmail.com', + url='http://pypi.python.org/pypi/testpackage/', + scripts=['testscript.py'] + ) diff --git a/tests/testpackage/testscript.py b/tests/testpackage/testscript.py new file mode 100644 index 0000000..835111f --- /dev/null +++ b/tests/testpackage/testscript.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +""" +A test script +""" + +print "Hello world" diff --git a/tests/testtemplate/mytemplates/__init__.py b/tests/testtemplate/mytemplates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/testtemplate/mytemplates/main.py b/tests/testtemplate/mytemplates/main.py new file mode 100644 index 0000000..581fb5b --- /dev/null +++ b/tests/testtemplate/mytemplates/main.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# encoding: utf-8 +# +# Copyright (c) 2010 Doug Hellmann. All rights reserved. +# +"""virtualenvwrapper.project plugin for tests +""" + +import logging +import os + +log = logging.getLogger(__name__) + + +def template(args): + """Creates a test file containing the args passed to us + """ + print('Running test template with args %r' % args) + project, project_dir = args + filename = os.path.join(project_dir, 'TEST_FILE') + print('Writing to %s' % filename) + output = open(filename, 'w') + try: + output.write('\n'.join(args)) + finally: + output.close() + return diff --git a/tests/testtemplate/requirements.txt b/tests/testtemplate/requirements.txt new file mode 100644 index 0000000..49fe098 --- /dev/null +++ b/tests/testtemplate/requirements.txt @@ -0,0 +1 @@ +setuptools diff --git a/tests/testtemplate/setup.py b/tests/testtemplate/setup.py new file mode 100644 index 0000000..6c9b794 --- /dev/null +++ b/tests/testtemplate/setup.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +PROJECT = 'testtemplate' +VERSION = '1.0' + +from setuptools import setup, find_packages + +setup( + name=PROJECT, + version=VERSION, + + description='template for testing mkproject', + + author='Doug Hellmann', + author_email='doug.hellmann@gmail.com', + + url='http://www.doughellmann.com/projects/virtualenvwrapper/', + + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'License :: OSI Approved :: BSD License', + 'Programming Language :: Python', + 'Intended Audience :: Developers', + 'Environment :: Console', + ], + + platforms=['Any'], + + provides=['testtemplate', + ], + requires=['virtualenv', + 'virtualenvwrapper (>=2.9)', + ], + + packages=find_packages(), + include_package_data=True, + + entry_points={ + 'virtualenvwrapper.project.template': [ + 'test = mytemplates.main:template', + ], + }, + + zip_safe=False, +) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..f4ce1b6 --- /dev/null +++ b/tox.ini @@ -0,0 +1,53 @@ +[tox] +envlist = py,zsh,style + +[testenv] +commands = bash ./tests/run_tests {envdir} [] +pass_env = + HOME + USER +setenv = + USING_TOX = 1 + SHELL = /bin/bash +allowlist_externals = + bash + zsh + +[testenv:fast] +setenv = + USING_TOX = 1 + SHELL = /bin/bash + FAIL_FAST = true + +[testenv:style] +deps = .[linter] +commands = flake8 virtualenvwrapper docs/source/conf.py + +[flake8] +max-line-length = 200 + +[testenv:zsh] +setenv = + USING_TOX = 1 + SHELL = /bin/zsh + test_shell_opts = -o shwordsplit +commands = zsh -o shwordsplit ./tests/run_tests {envdir} [] + +[testenv:docs] +deps = + -r{toxinidir}/docs/requirements.txt +commands = + sphinx-build -W -j auto -b html -d docs/build/doctrees docs/source docs/build/html + +[testenv:linkcheck] +deps = + -r{toxinidir}/docs/requirements.txt +commands = + sphinx-build -W -j auto -b linkcheck -d docs/build/doctrees docs/source docs/build/linkcheck + +[testenv:pkglint] +deps=.[build] +commands= + python -m build + twine check dist/*.tar.gz dist/*.whl + check-python-versions --only pyproject.toml,.github/workflows/test.yml diff --git a/virtualenvwrapper.sh b/virtualenvwrapper.sh new file mode 100644 index 0000000..26a9452 --- /dev/null +++ b/virtualenvwrapper.sh @@ -0,0 +1,1379 @@ +# -*- mode: shell-script -*- +# +# Shell functions to act as wrapper for Ian Bicking's virtualenv +# (http://pypi.python.org/pypi/virtualenv) +# +# +# Copyright Doug Hellmann, All Rights Reserved +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Doug Hellmann not be used +# in advertising or publicity pertaining to distribution of the software +# without specific, written prior permission. +# +# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# +# +# Project home page: http://www.doughellmann.com/projects/virtualenvwrapper/ +# +# +# Setup: +# +# 1. Create a directory to hold the virtual environments. +# (mkdir $HOME/.virtualenvs). +# 2. Add a line like "export WORKON_HOME=$HOME/.virtualenvs" +# to your .bashrc. +# 3. Add a line like "source /path/to/this/file/virtualenvwrapper.sh" +# to your .bashrc. +# 4. Run: source ~/.bashrc +# 5. Run: workon +# 6. A list of environments, empty, is printed. +# 7. Run: mkvirtualenv temp +# 8. Run: workon +# 9. This time, the "temp" environment is included. +# 10. Run: workon temp +# 11. The virtual environment is activated. +# + +# Locate the global Python where virtualenvwrapper is installed. +if [ "${VIRTUALENVWRAPPER_PYTHON:-}" = "" ] +then + _virtualenvwrapper_python_executable="$(which python3 2>/dev/null)" + if [ -n "$_virtualenvwrapper_python_executable" ] && $_virtualenvwrapper_python_executable -m 'virtualenvwrapper.hook_loader' --help >/dev/null 2>&1 + then + VIRTUALENVWRAPPER_PYTHON=$_virtualenvwrapper_python_executable + fi +fi +if [ "${VIRTUALENVWRAPPER_PYTHON:-}" = "" ] +then + echo -e "ERROR: Python with virtualenvwrapper module not found! +Either, install virtualenvwrapper module for the default python3 interpreter +or set VIRTUALENVWRAPPER_PYTHON to the interpreter to use." 1>&2 + return 1 +fi + +# Set the name of the virtualenv app to use. +if [ "${VIRTUALENVWRAPPER_VIRTUALENV:-}" = "" ] +then + VIRTUALENVWRAPPER_VIRTUALENV="virtualenv" +fi + +# Set the name of the virtualenv-clone app to use. +if [ "${VIRTUALENVWRAPPER_VIRTUALENV_CLONE:-}" = "" ] +then + VIRTUALENVWRAPPER_VIRTUALENV_CLONE="virtualenv-clone" +fi + +# Define script folder depending on the platorm (Win32/Unix) +VIRTUALENVWRAPPER_ENV_BIN_DIR="bin" +if [ "${OS:-}" = "Windows_NT" ] && ([ "${MSYSTEM:-}" = "MINGW32" ] || [ "${MSYSTEM:-}" = "MINGW64" ]) +then + # Only assign this for msys, cygwin use standard Unix paths + # and its own python installation + VIRTUALENVWRAPPER_ENV_BIN_DIR="Scripts" +fi + +# Let the user override the name of the file that holds the project +# directory name. +if [ "${VIRTUALENVWRAPPER_PROJECT_FILENAME:-}" = "" ] +then + export VIRTUALENVWRAPPER_PROJECT_FILENAME=".project" +fi + +# Let the user tell us they never want to cd to projects +# automatically. +export VIRTUALENVWRAPPER_WORKON_CD=${VIRTUALENVWRAPPER_WORKON_CD:-1} + +# Remember where we are running from. +if [ -z "${VIRTUALENVWRAPPER_SCRIPT:-}" ] +then + if [ -n "${BASH_SOURCE:-}" ] + then + export VIRTUALENVWRAPPER_SCRIPT="$BASH_SOURCE" + else + export VIRTUALENVWRAPPER_SCRIPT="$0" + fi +fi + +# Portable shell scripting is hard, let's go shopping. +# +# People insist on aliasing commands like 'cd', either with a real +# alias or even a shell function. Under bash and zsh, "builtin" forces +# the use of a command that is part of the shell itself instead of an +# alias, function, or external command, while "command" does something +# similar but allows external commands. We need to use a builtin for +# cd because we are trying to change the state of the current shell, +# so we use "builtin". +function virtualenvwrapper_cd { + if [ -n "${ZSH_VERSION:-}" ] + then + builtin \cd -q "$@" + else + builtin \cd "$@" + fi +} + +function virtualenvwrapper_expandpath { + if [ "$1" = "" ]; then + return 1 + else + "$VIRTUALENVWRAPPER_PYTHON" -c "import os,sys; sys.stdout.write(os.path.normpath(os.path.expanduser(os.path.expandvars(\"$1\")))+'\n')" + return 0 + fi +} + +function virtualenvwrapper_absolutepath { + if [ "$1" = "" ]; then + return 1 + else + "$VIRTUALENVWRAPPER_PYTHON" -c "import os,sys; sys.stdout.write(os.path.abspath(\"$1\")+'\n')" + return 0 + fi +} + +function virtualenvwrapper_derive_workon_home { + typeset workon_home_dir="$WORKON_HOME" + + # Make sure there is a default value for WORKON_HOME. + # You can override this setting in your .bashrc. + if [ "$workon_home_dir" = "" ] + then + workon_home_dir="$HOME/.virtualenvs" + fi + + # If the path is relative, prefix it with $HOME + # (note: for compatibility) + if echo "$workon_home_dir" | (unset GREP_OPTIONS; command \grep '^[^/~]' > /dev/null) + then + workon_home_dir="$HOME/$WORKON_HOME" + fi + + # Only call on Python to fix the path if it looks like the + # path might contain stuff to expand. + # (it might be possible to do this in shell, but I don't know a + # cross-shell-safe way of doing it -wolever) + if echo "$workon_home_dir" | (unset GREP_OPTIONS; command \grep -E '([\$~]|//)' >/dev/null) + then + # This will normalize the path by: + # - Removing extra slashes (e.g., when TMPDIR ends in a slash) + # - Expanding variables (e.g., $foo) + # - Converting ~s to complete paths (e.g., ~/ to /home/brian/ and ~arthur to /home/arthur) + workon_home_dir="$(virtualenvwrapper_expandpath "$workon_home_dir")" + fi + + echo "$workon_home_dir" + return 0 +} + +# Check if the WORKON_HOME directory exists, +# create it if it does not +# seperate from creating the files in it because this used to just error +# and maybe other things rely on the dir existing before that happens. +function virtualenvwrapper_verify_workon_home { + RC=0 + if [ ! -d "$WORKON_HOME/" ] + then + if [ "$1" != "-q" ] + then + echo "NOTE: Virtual environments directory $WORKON_HOME does not exist. Creating..." 1>&2 + fi + mkdir -p "$WORKON_HOME" + RC=$? + fi + return $RC +} + +#HOOK_VERBOSE_OPTION="-q" + +# Function to wrap mktemp so tests can replace it for error condition +# testing. +function virtualenvwrapper_mktemp { + command \mktemp "$@" +} + +# Expects 1 argument, the suffix for the new file. +function virtualenvwrapper_tempfile { + # Note: the 'X's must come last + typeset suffix=${1:-hook} + typeset file + + file="$(virtualenvwrapper_mktemp -t virtualenvwrapper-$suffix-XXXXXXXXXX)" + touch "$file" + if [ $? -ne 0 ] || [ -z "$file" ] || [ ! -f "$file" ] + then + echo "ERROR: virtualenvwrapper could not create a temporary file name." 1>&2 + return 1 + fi + echo $file + return 0 +} + +# Run the hooks +function virtualenvwrapper_run_hook { + typeset hook_script + typeset result + + hook_script="$(virtualenvwrapper_tempfile ${1}-hook)" || return 1 + + # Use a subshell to run the python interpreter with hook_loader so + # we can change the working directory. This avoids having the + # Python 3 interpreter decide that its "prefix" is the virtualenv + # if we happen to be inside the virtualenv when we start. + ( \ + virtualenvwrapper_cd "$WORKON_HOME" && + "$VIRTUALENVWRAPPER_PYTHON" -m 'virtualenvwrapper.hook_loader' \ + ${HOOK_VERBOSE_OPTION:-} --script "$hook_script" "$@" \ + ) + result=$? + + if [ $result -eq 0 ] + then + if [ ! -f "$hook_script" ] + then + echo "ERROR: virtualenvwrapper_run_hook could not find temporary file $hook_script" 1>&2 + command \rm -f "$hook_script" + return 2 + fi + # cat "$hook_script" + source "$hook_script" + elif [ "${1}" = "initialize" ] + then + cat - 1>&2 <&2 + return 1 + fi + if [ ! -e "$exe_path" ] + then + echo "ERROR: Found $1 in path as \"$exe_path\" but that does not exist" >&2 + return 1 + fi + return 0 +} + + +# Verify that virtualenv is installed and visible +function virtualenvwrapper_verify_virtualenv { + virtualenvwrapper_verify_resource $VIRTUALENVWRAPPER_VIRTUALENV +} + + +function virtualenvwrapper_verify_virtualenv_clone { + virtualenvwrapper_verify_resource $VIRTUALENVWRAPPER_VIRTUALENV_CLONE +} + + +# Verify that the requested environment exists +function virtualenvwrapper_verify_workon_environment { + typeset env_name="$1" + if [ ! -d "$WORKON_HOME/$env_name" ] + then + echo "ERROR: Environment '$env_name' does not exist. Create it with 'mkvirtualenv $env_name'." >&2 + return 1 + fi + return 0 +} + +# Verify that the active environment exists +function virtualenvwrapper_verify_active_environment { + if [ ! -n "${VIRTUAL_ENV}" ] || [ ! -d "${VIRTUAL_ENV}" ] + then + echo "ERROR: no virtualenv active, or active virtualenv is missing" >&2 + return 1 + fi + return 0 +} + +# Help text for mkvirtualenv +function virtualenvwrapper_mkvirtualenv_help { + echo "Usage: mkvirtualenv [-a project_path] [-i package] [-r requirements_file] [-p python_interpreter] [virtualenv options] env_name" + echo + echo " -a project_path" + echo + echo " Provide a full path to a project directory to associate with" + echo " the new environment." + echo + echo " -i package" + echo + echo " Install a package after the environment is created." + echo " This option may be repeated." + echo + echo " -r requirements_file" + echo + echo " Provide a pip requirements file to install a base set of packages" + echo " into the new environment." + echo + echo " -p python_interpreter, --python=python_interpreter" + echo + echo " The Python interpreter to use for the new environment." + echo " This can be specified as -p python3.8 or --python=/path/to/python" + echo + echo; + echo 'virtualenv help:'; + echo; + "$VIRTUALENVWRAPPER_VIRTUALENV" $@; +} + +# Create a new environment, in the WORKON_HOME. +# +# Usage: mkvirtualenv [options] ENVNAME +# (where the options are passed directly to virtualenv) +# +#:help:mkvirtualenv: Create a new virtualenv in $WORKON_HOME +function mkvirtualenv { + typeset -a in_args + typeset -a out_args + typeset -i i + typeset tst + typeset a + typeset envname + typeset requirements + typeset packages + typeset interpreter + typeset project + + in_args=( "$@" ) + + if [ -n "$ZSH_VERSION" ] + then + i=1 + tst="-le" + else + i=0 + tst="-lt" + fi + while test $i $tst $# + do + a="${in_args[$i]}" + # echo "arg $i : $a" + case "$a" in + -a) + i=$(( $i + 1 )) + project="${in_args[$i]}" + if [ ! -d "${project}" ] + then + echo "Cannot associate project with $project, it is not a directory" 1>&2 + return 1 + fi + project="$(virtualenvwrapper_absolutepath ${project})";; + -h|--help) + virtualenvwrapper_mkvirtualenv_help $a; + return;; + -i) + i=$(( $i + 1 )); + packages="$packages ${in_args[$i]}";; + -p|--python*) + if echo "$a" | grep -q "=" + then + interpreter="$(echo "$a" | cut -f2 -d=)" + else + i=$(( $i + 1 )) + interpreter="${in_args[$i]}" + fi;; + -r) + i=$(( $i + 1 )); + requirements="${in_args[$i]}"; + requirements="$(virtualenvwrapper_expandpath "$requirements")";; + *) + if [ ${#out_args} -gt 0 ] + then + out_args=( "${out_args[@]-}" "$a" ) + else + out_args=( "$a" ) + fi;; + esac + i=$(( $i + 1 )) + done + + if [ ! -z "$interpreter" ] + then + out_args=( "--python=$interpreter" ${out_args[@]} ) + fi; + + set -- "${out_args[@]}" + + eval "envname=\$$#" + virtualenvwrapper_verify_workon_home || return 1 + virtualenvwrapper_verify_virtualenv || return 1 + ( + [ -n "$ZSH_VERSION" ] && setopt SH_WORD_SPLIT + virtualenvwrapper_cd "$WORKON_HOME" && + "$VIRTUALENVWRAPPER_VIRTUALENV" $VIRTUALENVWRAPPER_VIRTUALENV_ARGS "$@" && + [ -d "$WORKON_HOME/$envname" ] && \ + virtualenvwrapper_run_hook "pre_mkvirtualenv" "$envname" + ) + typeset RC=$? + [ $RC -ne 0 ] && return $RC + + # If they passed a help option or got an error from virtualenv, + # the environment won't exist. Use that to tell whether + # we should switch to the environment and run the hook. + [ ! -d "$WORKON_HOME/$envname" ] && return 0 + + # If they gave us a project directory, set it up now + # so the activate hooks can find it. + if [ ! -z "$project" ] + then + setvirtualenvproject "$WORKON_HOME/$envname" "$project" + RC=$? + [ $RC -ne 0 ] && return $RC + fi + + # Now activate the new environment + workon "$envname" + + if [ ! -z "$requirements" ] + then + pip install -r "$requirements" + fi + + for a in $packages + do + pip install $a + done + + virtualenvwrapper_run_hook "post_mkvirtualenv" +} + +#:help:rmvirtualenv: Remove a virtualenv +function rmvirtualenv { + virtualenvwrapper_verify_workon_home || return 1 + if [ ${#@} = 0 ] + then + echo "Please specify an environment." >&2 + return 1 + fi + + # support to remove several environments + typeset env_name + # Must quote the parameters, as environments could have spaces in their names + for env_name in "$@" + do + echo "Removing $env_name..." + typeset env_dir="$WORKON_HOME/$env_name" + if [ "$VIRTUAL_ENV" = "$env_dir" ] + then + echo "ERROR: You cannot remove the active environment ('$env_name')." >&2 + echo "Either switch to another environment, or run 'deactivate'." >&2 + return 1 + fi + + if [ ! -d "$env_dir" ]; then + echo "Did not find environment $env_dir to remove." >&2 + fi + + # Move out of the current directory to one known to be + # safe, in case we are inside the environment somewhere. + typeset prior_dir="$(pwd)" + virtualenvwrapper_cd "$WORKON_HOME" + + virtualenvwrapper_run_hook "pre_rmvirtualenv" "$env_name" + command \rm -rf "$env_dir" + virtualenvwrapper_run_hook "post_rmvirtualenv" "$env_name" + + # If the directory we used to be in still exists, move back to it. + if [ -d "$prior_dir" ] + then + virtualenvwrapper_cd "$prior_dir" + fi + done +} + +# List the available environments. +function virtualenvwrapper_show_workon_options { + virtualenvwrapper_verify_workon_home || return 1 + # NOTE: DO NOT use ls or cd here because colorized versions spew control + # characters into the output list. + # echo seems a little faster than find, even with -depth 3. + # Note that this is a little tricky, as there may be spaces in the path. + # + # 1. Look for environments by finding the activate scripts. + # Use a subshell so we can suppress the message printed + # by zsh if the glob pattern fails to match any files. + # This yields a single, space-separated line containing all matches. + # 2. Replace the trailing newline with a space, so every + # possible env has a space following it. + # 3. Strip the bindir/activate script suffix, replacing it with + # a slash, as that is an illegal character in a directory name. + # This yields a slash-separated list of possible env names. + # 4. Replace each slash with a newline to show the output one name per line. + # 5. Eliminate any lines with * on them because that means there + # were no envs. + (virtualenvwrapper_cd "$WORKON_HOME" && echo */$VIRTUALENVWRAPPER_ENV_BIN_DIR/activate) 2>/dev/null \ + | command \tr "\n" " " \ + | command \sed "s|/$VIRTUALENVWRAPPER_ENV_BIN_DIR/activate |/|g" \ + | command \tr "/" "\n" \ + | command \sed "/^[[:space:]]*$/d" \ + | (unset GREP_OPTIONS; command \grep -E -v '^\*$') 2>/dev/null +} + +function _lsvirtualenv_usage { + echo "lsvirtualenv [-blh]" + echo " -b -- brief mode" + echo " -l -- long mode" + echo " -h -- this help message" +} + +#:help:lsvirtualenv: list virtualenvs +function lsvirtualenv { + + typeset long_mode=true + if command -v "getopts" >/dev/null 2>&1 + then + # Use getopts when possible + OPTIND=1 + while getopts ":blh" opt "$@" + do + case "$opt" in + l) long_mode=true;; + b) long_mode=false;; + h) _lsvirtualenv_usage; + return 1;; + ?) echo "Invalid option: -$OPTARG" >&2; + _lsvirtualenv_usage; + return 1;; + esac + done + else + # fallback on getopt for other shell + typeset -a args + args=($(getopt blh "$@")) + if [ $? != 0 ] + then + _lsvirtualenv_usage + return 1 + fi + for opt in $args + do + case "$opt" in + -l) long_mode=true;; + -b) long_mode=false;; + -h) _lsvirtualenv_usage; + return 1;; + esac + done + fi + + if $long_mode + then + allvirtualenv showvirtualenv "$env_name" + else + virtualenvwrapper_show_workon_options + fi +} + +#:help:showvirtualenv: show details of a single virtualenv +function showvirtualenv { + typeset env_name="$1" + if [ -z "$env_name" ] + then + if [ -z "$VIRTUAL_ENV" ] + then + echo "showvirtualenv [env]" + return 1 + fi + env_name=$(basename "$VIRTUAL_ENV") + fi + + virtualenvwrapper_run_hook "get_env_details" "$env_name" + echo +} + +# Show help for workon +function virtualenvwrapper_workon_help { + echo "Usage: workon env_name" + echo "" + echo " Deactivate any currently activated virtualenv" + echo " and activate the named environment, triggering" + echo " any hooks in the process." + echo "" + echo " workon" + echo "" + echo " Print a list of available environments." + echo " (See also lsvirtualenv -b)" + echo "" + echo " workon (-h|--help)" + echo "" + echo " Show this help message." + echo "" + echo " workon (-c|--cd) envname" + echo "" + echo " After activating the environment, cd to the associated" + echo " project directory if it is set." + echo "" + echo " workon (-n|--no-cd) envname" + echo "" + echo " After activating the environment, do not cd to the" + echo " associated project directory." + echo "" +} + +#:help:workon: list or change working virtualenvs +function workon { + typeset -a in_args + typeset -a out_args + + in_args=( "$@" ) + + if [ -n "$ZSH_VERSION" ] + then + i=1 + tst="-le" + else + i=0 + tst="-lt" + fi + typeset cd_after_activate=$VIRTUALENVWRAPPER_WORKON_CD + while test $i $tst $# + do + a="${in_args[$i]}" + case "$a" in + -h|--help) + virtualenvwrapper_workon_help; + return 0;; + -n|--no-cd) + cd_after_activate=0;; + -c|--cd) + cd_after_activate=1;; + *) + if [ ${#out_args} -gt 0 ] + then + out_args=( "${out_args[@]-}" "$a" ) + else + out_args=( "$a" ) + fi;; + esac + i=$(( $i + 1 )) + done + + set -- "${out_args[@]}" + + typeset env_name="$1" + if [ "$env_name" = "" ] + then + lsvirtualenv -b + return 1 + elif [ "$env_name" = "." ] + then + # The IFS default of breaking on whitespace causes issues if there + # are spaces in the env_name, so change it. + IFS='%' + env_name="$(basename $(pwd))" + unset IFS + fi + + virtualenvwrapper_verify_workon_home || return 1 + virtualenvwrapper_verify_workon_environment "$env_name" || return 1 + + activate="$WORKON_HOME/$env_name/$VIRTUALENVWRAPPER_ENV_BIN_DIR/activate" + if [ ! -f "$activate" ] + then + echo "ERROR: Environment '$WORKON_HOME/$env_name' does not contain an activate script." >&2 + return 1 + fi + + # Deactivate any current environment "destructively" + # before switching so we use our override function, + # if it exists, but make sure it's the deactivate function + # we set up + ! type deactivate >/dev/null 2>&1 || { + typeset -f deactivate | grep 'typeset env_postdeactivate_hook' >/dev/null 2>&1 + if [ $? -eq 0 ] + then + deactivate + unset -f deactivate >/dev/null 2>&1 + fi + } + + virtualenvwrapper_run_hook "pre_activate" "$env_name" + + source "$activate" + + # Save the deactivate function from virtualenv under a different name + virtualenvwrapper_original_deactivate=`typeset -f deactivate | sed 's/deactivate/virtualenv_deactivate/g'` + eval "$virtualenvwrapper_original_deactivate" + unset -f deactivate >/dev/null 2>&1 + + # Replace the deactivate() function with a wrapper. + eval 'deactivate () { + typeset env_postdeactivate_hook + typeset old_env + + # Call the local hook before the global so we can undo + # any settings made by the local postactivate first. + virtualenvwrapper_run_hook "pre_deactivate" + + env_postdeactivate_hook="$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/postdeactivate" + old_env=$(basename "$VIRTUAL_ENV") + + # Call the original function. + virtualenv_deactivate $1 + + virtualenvwrapper_run_hook "post_deactivate" "$old_env" + + if [ ! "$1" = "nondestructive" ] + then + # Remove this function + unset -f virtualenv_deactivate >/dev/null 2>&1 + unset -f deactivate >/dev/null 2>&1 + fi + + }' + + VIRTUALENVWRAPPER_PROJECT_CD=$cd_after_activate virtualenvwrapper_run_hook "post_activate" + + return 0 +} + + +# Prints the Python version string for the current interpreter. +function virtualenvwrapper_get_python_version { + # Uses the Python from the virtualenv rather than + # VIRTUALENVWRAPPER_PYTHON because we're trying to determine the + # version installed there so we can build up the path to the + # site-packages directory. + "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/python" -V 2>&1 | cut -f2 -d' ' | cut -f-2 -d. +} + +# Prints the path to the site-packages directory for the current environment. +function virtualenvwrapper_get_site_packages_dir { + "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/python" -c "import sysconfig; print(sysconfig.get_path('platlib'))" +} + +# Path management for packages outside of the virtual env. +# Based on a contribution from James Bennett and Jannis Leidel. +# +# add2virtualenv directory1 directory2 ... +# +# Adds the specified directories to the Python path for the +# currently-active virtualenv. This will be done by placing the +# directory names in a path file named +# "virtualenv_path_extensions.pth" inside the virtualenv's +# site-packages directory; if this file does not exist, it will be +# created first. +# +#:help:add2virtualenv: add directory to the import path +function add2virtualenv { + virtualenvwrapper_verify_workon_home || return 1 + virtualenvwrapper_verify_active_environment || return 1 + + site_packages="`virtualenvwrapper_get_site_packages_dir`" + + if [ ! -d "${site_packages}" ] + then + echo "ERROR: currently-active virtualenv does not appear to have a site-packages directory" >&2 + return 1 + fi + + # Prefix with _ to ensure we are loaded as early as possible, + # and at least before easy_install.pth. + path_file="$site_packages/_virtualenv_path_extensions.pth" + + if [ "$*" = "" ] + then + echo "Usage: add2virtualenv dir [dir ...]" + if [ -f "$path_file" ] + then + echo + echo "Existing paths:" + cat "$path_file" | grep -v "^import" + fi + return 1 + fi + + remove=0 + if [ "$1" = "-d" ] + then + remove=1 + shift + fi + + if [ ! -f "$path_file" ] + then + echo "import sys; sys.__plen = len(sys.path)" > "$path_file" || return 1 + echo "import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)" >> "$path_file" || return 1 + fi + + for pydir in "$@" + do + absolute_path="$(virtualenvwrapper_absolutepath "$pydir")" + if [ "$absolute_path" != "$pydir" ] + then + echo "Warning: Converting \"$pydir\" to \"$absolute_path\"" 1>&2 + fi + + if [ $remove -eq 1 ] + then + sed -i.tmp "\:^$absolute_path$: d" "$path_file" + else + sed -i.tmp '1 a\ +'"$absolute_path"' +' "$path_file" + fi + rm -f "${path_file}.tmp" + done + return 0 +} + +# Does a ``cd`` to the site-packages directory of the currently-active +# virtualenv. +#:help:cdsitepackages: change to the site-packages directory +function cdsitepackages { + virtualenvwrapper_verify_workon_home || return 1 + virtualenvwrapper_verify_active_environment || return 1 + typeset site_packages="`virtualenvwrapper_get_site_packages_dir`" + virtualenvwrapper_cd "$site_packages/$1" +} + +# Does a ``cd`` to the root of the currently-active virtualenv. +#:help:cdvirtualenv: change to the $VIRTUAL_ENV directory +function cdvirtualenv { + virtualenvwrapper_verify_workon_home || return 1 + virtualenvwrapper_verify_active_environment || return 1 + virtualenvwrapper_cd "$VIRTUAL_ENV/$1" +} + +# Shows the content of the site-packages directory of the currently-active +# virtualenv +#:help:lssitepackages: list contents of the site-packages directory +function lssitepackages { + virtualenvwrapper_verify_workon_home || return 1 + virtualenvwrapper_verify_active_environment || return 1 + typeset site_packages="`virtualenvwrapper_get_site_packages_dir`" + ls $@ "$site_packages" + + path_file="$site_packages/_virtualenv_path_extensions.pth" + if [ -f "$path_file" ] + then + echo + echo "_virtualenv_path_extensions.pth:" + cat "$path_file" + fi +} + +#:help:cpvirtualenv: duplicate the named virtualenv to make a new one +function cpvirtualenv { + virtualenvwrapper_verify_workon_home || return 1 + virtualenvwrapper_verify_virtualenv_clone || return 1 + + typeset src_name="$1" + typeset trg_name="$2" + typeset src + typeset trg + + # without a source there is nothing to do + if [ "$src_name" = "" ]; then + echo "Please provide a valid virtualenv to copy." + return 1 + else + # see if it's already in workon + if [ ! -e "$WORKON_HOME/$src_name" ]; then + # so it's a virtualenv we are importing + # make sure we have a full path + # and get the name + src="$(virtualenvwrapper_expandpath "$src_name")" + # final verification + if [ ! -e "$src" ]; then + echo "Please provide a valid virtualenv to copy." + return 1 + fi + src_name="$(basename "$src")" + else + src="$WORKON_HOME/$src_name" + fi + fi + + if [ "$trg_name" = "" ]; then + # target not given, assume + # same as source + trg="$WORKON_HOME/$src_name" + trg_name="$src_name" + else + trg="$WORKON_HOME/$trg_name" + fi + trg="$(virtualenvwrapper_expandpath "$trg")" + + # validate trg does not already exist + # catch copying virtualenv in workon home + # to workon home + if [ -e "$trg" ]; then + echo "$trg_name virtualenv already exists." + return 1 + fi + + echo "Copying $src_name as $trg_name..." + ( + [ -n "$ZSH_VERSION" ] && setopt SH_WORD_SPLIT + virtualenvwrapper_cd "$WORKON_HOME" && + "$VIRTUALENVWRAPPER_VIRTUALENV_CLONE" "$src" "$trg" + [ -d "$trg" ] && + virtualenvwrapper_run_hook "pre_cpvirtualenv" "$src" "$trg_name" && + virtualenvwrapper_run_hook "pre_mkvirtualenv" "$trg_name" + ) + typeset RC=$? + [ $RC -ne 0 ] && return $RC + + [ ! -d "$WORKON_HOME/$trg_name" ] && return 1 + + # Now activate the new environment + workon "$trg_name" + + virtualenvwrapper_run_hook "post_mkvirtualenv" + virtualenvwrapper_run_hook "post_cpvirtualenv" +} + +# +# virtualenvwrapper project functions +# + +# Verify that the PROJECT_HOME directory exists +function virtualenvwrapper_verify_project_home { + if [ -z "$PROJECT_HOME" ] + then + echo "ERROR: Set the PROJECT_HOME shell variable to the name of the directory where projects should be created." >&2 + return 1 + fi + if [ ! -d "$PROJECT_HOME" ] + then + [ "$1" != "-q" ] && echo "ERROR: Project directory '$PROJECT_HOME' does not exist. Create it or set PROJECT_HOME to an existing directory." >&2 + return 1 + fi + return 0 +} + +# Given a virtualenv directory and a project directory, +# set the virtualenv up to be associated with the +# project +#:help:setvirtualenvproject: associate a project directory with a virtualenv +function setvirtualenvproject { + typeset venv="$1" + typeset prj="$2" + if [ -z "$venv" ] + then + venv="$VIRTUAL_ENV" + fi + if [ -z "$prj" ] + then + prj="$(pwd)" + else + prj=$(virtualenvwrapper_absolutepath "${prj}") + fi + + # If what we were given isn't a directory, see if it is under + # $WORKON_HOME. + if [ ! -d "$venv" ] + then + venv="$WORKON_HOME/$venv" + fi + if [ ! -d "$venv" ] + then + echo "No virtualenv $(basename $venv)" 1>&2 + return 1 + fi + + # Make sure we have a valid project setting + if [ ! -d "$prj" ] + then + echo "Cannot associate virtualenv with \"$prj\", it is not a directory" 1>&2 + return 1 + fi + + echo "Setting project for $(basename $venv) to $prj" + echo "$prj" > "$venv/$VIRTUALENVWRAPPER_PROJECT_FILENAME" +} + +# Show help for mkproject +function virtualenvwrapper_mkproject_help { + echo "Usage: mkproject [-f|--force] [-t template] [virtualenv options] project_name" + echo + echo "-f, --force Create the virtualenv even if the project directory" + echo " already exists" + echo + echo "Multiple templates may be selected. They are applied in the order" + echo "specified on the command line." + echo + echo "mkvirtualenv help:" + echo + mkvirtualenv -h + echo + echo "Available project templates:" + echo + "$VIRTUALENVWRAPPER_PYTHON" -c 'from virtualenvwrapper.hook_loader import main; main()' -l project.template +} + +#:help:mkproject: create a new project directory and its associated virtualenv +function mkproject { + typeset -a in_args + typeset -a out_args + typeset -i i + typeset tst + typeset a + typeset t + typeset force + typeset templates + + in_args=( "$@" ) + force=0 + + if [ -n "$ZSH_VERSION" ] + then + i=1 + tst="-le" + else + i=0 + tst="-lt" + fi + while test $i $tst $# + do + a="${in_args[$i]}" + case "$a" in + -h|--help) + virtualenvwrapper_mkproject_help; + return;; + -f|--force) + force=1;; + -t) + i=$(( $i + 1 )); + templates="$templates ${in_args[$i]}";; + *) + if [ ${#out_args} -gt 0 ] + then + out_args=( "${out_args[@]-}" "$a" ) + else + out_args=( "$a" ) + fi;; + esac + i=$(( $i + 1 )) + done + + set -- "${out_args[@]}" + + # echo "templates $templates" + # echo "remainder $@" + # return 0 + + eval "typeset envname=\$$#" + virtualenvwrapper_verify_project_home || return 1 + + if [ -d "$PROJECT_HOME/$envname" -a $force -eq 0 ] + then + echo "Project $envname already exists." >&2 + return 1 + fi + + mkvirtualenv "$@" || return 1 + + virtualenvwrapper_cd "$PROJECT_HOME" + + virtualenvwrapper_run_hook "project.pre_mkproject" $envname + + echo "Creating $PROJECT_HOME/$envname" + mkdir -p "$PROJECT_HOME/$envname" + setvirtualenvproject "$VIRTUAL_ENV" "$PROJECT_HOME/$envname" + + virtualenvwrapper_cd "$PROJECT_HOME/$envname" + + for t in $templates + do + echo + echo "Applying template $t" + # For some reason zsh insists on prefixing the template + # names with a space, so strip them out before passing + # the value to the hook loader. + virtualenvwrapper_run_hook --name $(echo $t | sed 's/^ //') "project.template" "$envname" "$PROJECT_HOME/$envname" + done + + virtualenvwrapper_run_hook "project.post_mkproject" +} + +#:help:cdproject: change directory to the active project +function cdproject { + virtualenvwrapper_verify_workon_home || return 1 + virtualenvwrapper_verify_active_environment || return 1 + if [ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME" ] + then + typeset project_dir="$(cat "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME")" + if [ ! -z "$project_dir" ] + then + virtualenvwrapper_cd "$project_dir" + else + echo "Project directory $project_dir does not exist" 1>&2 + return 1 + fi + else + echo "No project set in $VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME" 1>&2 + return 1 + fi + return 0 +} + +# +# Temporary virtualenv +# +# Originally part of virtualenvwrapper.tmpenv plugin +# +#:help:mktmpenv: create a temporary virtualenv +function mktmpenv { + typeset tmpenvname + typeset RC + typeset -a in_args + typeset -a out_args + + in_args=( "$@" ) + + if [ -n "$ZSH_VERSION" ] + then + i=1 + tst="-le" + else + i=0 + tst="-lt" + fi + typeset cd_after_activate=$VIRTUALENVWRAPPER_WORKON_CD + while test $i $tst $# + do + a="${in_args[$i]}" + case "$a" in + -n|--no-cd) + cd_after_activate=0;; + -c|--cd) + cd_after_activate=1;; + *) + if [ ${#out_args} -gt 0 ] + then + out_args=( "${out_args[@]-}" "$a" ) + else + out_args=( "$a" ) + fi;; + esac + i=$(( $i + 1 )) + done + + set -- "${out_args[@]}" + + # Generate a unique temporary name + tmpenvname=$("$VIRTUALENVWRAPPER_PYTHON" -c 'import uuid,sys; sys.stdout.write(uuid.uuid4()+"\n")' 2>/dev/null) + if [ -z "$tmpenvname" ] + then + # This python does not support uuid + tmpenvname=$("$VIRTUALENVWRAPPER_PYTHON" -c 'import random,sys; sys.stdout.write(hex(random.getrandbits(64))[2:-1]+"\n")' 2>/dev/null) + fi + tmpenvname="tmp-$tmpenvname" + + # Create the environment + mkvirtualenv "$@" "$tmpenvname" + RC=$? + if [ $RC -ne 0 ] + then + return $RC + fi + + # Change working directory + [ "$cd_after_activate" = "1" ] && cdvirtualenv + + # Create the tmpenv marker file + echo "This is a temporary environment. It will be deleted when you run 'deactivate'." | tee "$VIRTUAL_ENV/README.tmpenv" + + # Update the postdeactivate script + cat - >> "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/postdeactivate" < "$req_file" + if [ -n "$(cat "$req_file")" ] + then + echo "Uninstalling packages:" + echo + while read line; do + typeset pkg="" + if [[ "$line" =~ ^-f ]]; then + # ignore lines starting -f which pip sometimes + # includes and that do not point to specific + # dependencies + continue + fi + if [[ "$line" =~ ^-e ]]; then + # fix lines pointing to editable packages, which look like: + # -e git+ssh://git@github.com/python-virtualenvwrapper/virtualenvwrapper.git@1dc9e5f52102f0133b804c0c8a6b76c55db908bf#egg=testpackage&subdirectory=tests/testpackage + # and parse out the egg name to pass to pip + pkg=$(echo "$line" | cut -f2 -d' ' | sed -e 's|&subdirectory.*||g' -e 's|.*egg=||g') + else + # Strip version specifiers off of the end of the line + # to keep only the package name. + pkg=$(echo "$line" | sed -e 's/[<>!=].*//g') + fi + echo $pkg + pip uninstall -y "$pkg" + done < "$req_file" + else + echo "Nothing to remove." + fi + rm -f "$req_file" +} + +# +# Run a command in each virtualenv +# +#:help:allvirtualenv: run a command in all virtualenvs +function allvirtualenv { + virtualenvwrapper_verify_workon_home || return 1 + typeset d + + # The IFS default of breaking on whitespace causes issues if there + # are spaces in the env_name, so change it. + IFS='%' + virtualenvwrapper_show_workon_options | while read d + do + [ ! -d "$WORKON_HOME/$d" ] && continue + echo "$d" + echo "$d" | sed 's/./=/g' + # Activate the environment, but not with workon + # because we don't want to trigger any hooks. + (source "$WORKON_HOME/$d/$VIRTUALENVWRAPPER_ENV_BIN_DIR/activate"; + virtualenvwrapper_cd "$VIRTUAL_ENV"; + "$@") + echo + done + unset IFS +} + +function _virtualenvwrapper_version { + "$VIRTUALENVWRAPPER_PYTHON" -m 'virtualenvwrapper.hook_loader' --version +} + +#:help:virtualenvwrapper: show this help message +function virtualenvwrapper { + typeset version=$(_virtualenvwrapper_version) + cat <', + dest='script_filename', + default=None, + ) + parser.add_option( + '-s', '--source', + help='Print the shell commands to be run in the current shell', + action='store_true', + dest='sourcing', + default=False, + ) + parser.add_option( + '-l', '--list', + help='Print a list of the plugins available for the given hook', + action='store_true', + default=False, + dest='listing', + ) + parser.add_option( + '-v', '--verbose', + help='Show more information on the console', + action='store_const', + const=2, + default=1, + dest='verbose_level', + ) + parser.add_option( + '-q', '--quiet', + help='Show less information on the console', + action='store_const', + const=0, + dest='verbose_level', + ) + parser.add_option( + '-n', '--name', + help='Only run the hook from the named plugin', + action='append', + dest='names', + default=[], + ) + parser.add_option( + '--version', + help='Show the version of virtualenvwrapper', + action='store_true', + default=False, + ) + parser.disable_interspersed_args() # stop when on option without an '-' + options, args = parser.parse_args() + + if options.version: + print(virtualenvwrapper.version.version) + return 0 + + root_logger = logging.getLogger('virtualenvwrapper') + + # Set up logging to a file + logfile = os.environ.get('VIRTUALENVWRAPPER_LOG_FILE') + if logfile: + root_logger.setLevel(logging.DEBUG) + file_handler = GroupWriteRotatingFileHandler( + logfile, + maxBytes=10240, + backupCount=1, + ) + formatter = logging.Formatter(LOG_FORMAT) + file_handler.setFormatter(formatter) + root_logger.addHandler(file_handler) + + # Send higher-level messages to the console, too + console = logging.StreamHandler(sys.stderr) + console_level = [logging.WARNING, + logging.INFO, + logging.DEBUG, + ][options.verbose_level] + console.setLevel(console_level) + formatter = logging.Formatter('%(name)s %(message)s') + console.setFormatter(formatter) + root_logger.addHandler(console) + root_logger.setLevel(console_level) + + # logging.getLogger(__name__).debug('cli args %s', args) + + # Determine which hook we're running + if not args: + if options.listing: + list_hooks() + return 0 + else: + parser.error('Please specify the hook to run') + hook = args[0] + + if options.sourcing and options.script_filename: + parser.error('--source and --script are mutually exclusive.') + + if options.sourcing: + hook += '_source' + + log = logging.getLogger('virtualenvwrapper.hook_loader') + + log.debug('Running %s hooks', hook) + run_hooks(hook, options, args) + + if options.script_filename: + log.debug('Saving sourcable %s hooks to %s', + hook, options.script_filename) + options.sourcing = True + try: + with open(options.script_filename, "w") as output: + output.write('# %s\n' % hook) + # output.write('echo %s\n' % hook) + # output.write('set -x\n') + run_hooks(hook + '_source', options, args, output) + except (IOError, OSError) as e: + log.error('Error while writing to %s: \n %s', + options.script_filename, e) + sys.exit(1) + + return 0 + + +def run_hooks(hook, options, args, output=None): + log = logging.getLogger('virtualenvwrapper.hook_loader') + if output is None: + output = sys.stdout + + namespace = 'virtualenvwrapper.%s' % hook + if options.names: + log.debug('looking for %s hooks %s' % (namespace, options.names)) + hook_mgr = NamedExtensionManager(namespace, options.names) + else: + log.debug('looking for %s hooks' % namespace) + hook_mgr = ExtensionManager(namespace) + + if options.listing: + def show(ext): + output.write(' %-10s -- %s\n' % + (ext.name, inspect.getdoc(ext.plugin) or '')) + try: + hook_mgr.map(show) + except RuntimeError: # no templates + output.write(' No templates installed.\n') + + elif options.sourcing: + def get_source(ext, args): + # Show the shell commands so they can + # be run in the calling shell. + log.debug('getting source instructions for %s' % ext.name) + contents = (ext.plugin(args) or '').strip() + if contents: + output.write('# %s\n' % ext.name) + output.write(contents) + output.write("\n") + try: + hook_mgr.map(get_source, args[1:]) + except RuntimeError: + pass + + else: + # Just run the plugin ourselves + def invoke(ext, args): + log.debug('running %s' % ext.name) + ext.plugin(args) + try: + hook_mgr.map(invoke, args[1:]) + except RuntimeError: + pass + + +def list_hooks(output=None): + if output is None: + output = sys.stdout + static_names = [ + 'initialize', + 'get_env_details', + 'project.pre_mkproject', + 'project.post_mkproject', + 'project.template', + ] + pre_post_hooks = ( + '_'.join(h) + for h in itertools.product(['pre', 'post'], + ['mkvirtualenv', + 'rmvirtualenv', + 'activate', + 'deactivate', + 'cpvirtualenv', + ]) + ) + for hook in itertools.chain(static_names, pre_post_hooks): + output.write(hook + '\n') + + +if __name__ == '__main__': + main() diff --git a/virtualenvwrapper/project.py b/virtualenvwrapper/project.py new file mode 100644 index 0000000..a255081 --- /dev/null +++ b/virtualenvwrapper/project.py @@ -0,0 +1,76 @@ +# encoding: utf-8 +# +# Copyright (c) 2010 Doug Hellmann. All rights reserved. +# +"""virtualenvwrapper.project +""" + +import logging +import os + +from virtualenvwrapper.user_scripts import PERMISSIONS, make_hook, run_global + +log = logging.getLogger(__name__) + +GLOBAL_HOOKS = [ + # mkproject + ("premkproject", + "This hook is run after a new project is created " + "and before it is activated.", + PERMISSIONS), + ("postmkproject", + "This hook is run after a new project is activated.", + PERMISSIONS), +] + + +def initialize(args): + """Set up user hooks + """ + for filename, comment, permissions in GLOBAL_HOOKS: + make_hook(os.path.join('$VIRTUALENVWRAPPER_HOOK_DIR', filename), + comment, permissions) + return + + +def pre_mkproject(args): + log.debug('pre_mkproject %s', str(args)) + run_global('premkproject', *args) + return + + +def post_mkproject_source(args): + return """ +# +# Run user-provided scripts +# +[ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postmkproject" ] && \ + source "$VIRTUALENVWRAPPER_HOOK_DIR/postmkproject" +""" + + +def post_activate_source(args): + return """ +# +# Change to the project directory, as long as we haven't been told not to. +# +[ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME" \ + -a "$VIRTUALENVWRAPPER_PROJECT_CD" = 1 ] && \ + virtualenvwrapper_cd \ + "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")" +if [ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME" ]; then + if [ -f "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")/.virtualenvwrapper/postactivate" ]; then + source "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")/.virtualenvwrapper/postactivate" + fi +fi +""" + + +def pre_deactivate_source(args): + return """ +if [ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME" ]; then + if [ -f "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")/.virtualenvwrapper/predeactivate" ]; then + source "$(cat \"$VIRTUAL_ENV/$VIRTUALENVWRAPPER_PROJECT_FILENAME\")/.virtualenvwrapper/predeactivate" + fi +fi +""" diff --git a/virtualenvwrapper/user_scripts.py b/virtualenvwrapper/user_scripts.py new file mode 100644 index 0000000..8a5eedc --- /dev/null +++ b/virtualenvwrapper/user_scripts.py @@ -0,0 +1,340 @@ +# encoding: utf-8 +# +# Copyright (c) 2010 Doug Hellmann. All rights reserved. +# +"""Plugin to handle hooks in user-defined scripts. +""" + +import logging +import os +import re +import stat +import subprocess +import sys + + +log = logging.getLogger(__name__) + +# Are we running under msys +if sys.platform == 'win32' and \ + os.environ.get('OS') == 'Windows_NT' and \ + os.environ.get('MSYSTEM') in ('MINGW32', 'MINGW64'): + is_msys = True + script_folder = 'Scripts' +else: + is_msys = False + script_folder = 'bin' + + +def _get_msys_shell(): + if 'MSYS_HOME' in os.environ: + return [get_path(os.environ['MSYS_HOME'], 'bin', 'sh.exe')] + else: + for path in os.environ['PATH'].split(';'): + if os.path.exists(os.path.join(path, 'sh.exe')): + return [get_path(path, 'sh.exe')] + raise Exception('Could not find sh.exe') + + +def run_script(script_path, *args): + """Execute a script in a subshell. + """ + if os.path.exists(script_path): + cmd = [script_path] + list(args) + if is_msys: + cmd = _get_msys_shell() + cmd + log.debug('running %s', str(cmd)) + try: + subprocess.call(cmd) + except OSError: + _, msg, _ = sys.exc_info() + log.error('could not run "%s": %s', script_path, str(msg)) + # log.debug('Returned %s', return_code) + return + + +def run_global(script_name, *args): + """Run a script from $VIRTUALENVWRAPPER_HOOK_DIR. + """ + script_path = get_path('$VIRTUALENVWRAPPER_HOOK_DIR', script_name) + run_script(script_path, *args) + return + + +PERMISSIONS = ( + stat.S_IRWXU # read/write/execute, user + | stat.S_IRGRP # read, group + | stat.S_IXGRP # execute, group + | stat.S_IROTH # read, others + | stat.S_IXOTH # execute, others +) +PERMISSIONS_SOURCED = PERMISSIONS & ~( + # remove executable bits for + stat.S_IXUSR # ... user + | stat.S_IXGRP # ... group + | stat.S_IXOTH # ... others +) + + +GLOBAL_HOOKS = [ + # initialize + ("initialize", + "This hook is sourced during the startup phase " + "when loading virtualenvwrapper.sh.", + PERMISSIONS_SOURCED), + + # mkvirtualenv + ("premkvirtualenv", + "This hook is run after a new virtualenv is created " + "and before it is activated.\n" + "# argument: name of new environment", + PERMISSIONS), + ("postmkvirtualenv", + "This hook is sourced after a new virtualenv is activated.", + PERMISSIONS_SOURCED), + + # cpvirtualenv: + # precpvirtualenv (run), + # postcpvirtualenv (sourced) + + # rmvirtualenv + ("prermvirtualenv", + "This hook is run before a virtualenv is deleted.\n" + "# argument: full path to environment directory", + PERMISSIONS), + ("postrmvirtualenv", + "This hook is run after a virtualenv is deleted.\n" + "# argument: full path to environment directory", + PERMISSIONS), + + # deactivate + ("predeactivate", + "This hook is sourced before every virtualenv is deactivated.", + PERMISSIONS_SOURCED), + ("postdeactivate", + "This hook is sourced after every virtualenv is deactivated.", + PERMISSIONS_SOURCED), + + # activate + ("preactivate", + "This hook is run before every virtualenv is activated.\n" + "# argument: environment name", + PERMISSIONS), + ("postactivate", + "This hook is sourced after every virtualenv is activated.", + PERMISSIONS_SOURCED), + + # mkproject: + # premkproject (run), + # postmkproject (sourced) + + # get_env_details + ("get_env_details", + "This hook is run when the list of virtualenvs is printed " + "so each name can include details.\n" + "# argument: environment name", + PERMISSIONS), +] + + +LOCAL_HOOKS = [ + # deactivate + ("predeactivate", + "This hook is sourced before this virtualenv is deactivated.", + PERMISSIONS_SOURCED), + ("postdeactivate", + "This hook is sourced after this virtualenv is deactivated.", + PERMISSIONS_SOURCED), + + # activate + ("preactivate", + "This hook is run before this virtualenv is activated.", + PERMISSIONS), + ("postactivate", + "This hook is sourced after this virtualenv is activated.", + PERMISSIONS_SOURCED), + + # get_env_details + ("get_env_details", + "This hook is run when the list of virtualenvs is printed " + "in 'long' mode so each name can include details.\n" + "# argument: environment name", + PERMISSIONS), +] + + +def make_hook(filename, comment, permissions): + """Create a hook script. + + :param filename: The name of the file to write. + :param comment: The comment to insert into the file. + """ + filename = get_path(filename) + if not os.path.exists(filename): + log.info('creating %s', filename) + f = open(filename, 'w') + try: + # for sourced scripts, the shebang line won't be used; + # it is useful for editors to recognize the file type, though + f.write("#!%(shell)s\n# %(comment)s\n\n" % { + 'comment': comment, + 'shell': os.environ.get('SHELL', '/bin/sh'), + }) + finally: + f.close() + os.chmod(filename, permissions) + return + + +# HOOKS + + +def initialize(args): + for filename, comment, permissions in GLOBAL_HOOKS: + make_hook(get_path('$VIRTUALENVWRAPPER_HOOK_DIR', filename), + comment, permissions) + return + + +def initialize_source(args): + return """ +# +# Run user-provided scripts +# +[ -f "$VIRTUALENVWRAPPER_HOOK_DIR/initialize" ] && \ + source "$VIRTUALENVWRAPPER_HOOK_DIR/initialize" +""" + + +def pre_mkvirtualenv(args): + log.debug('pre_mkvirtualenv %s', str(args)) + envname = args[0] + for filename, comment, permissions in LOCAL_HOOKS: + make_hook(get_path('$WORKON_HOME', envname, script_folder, filename), + comment, permissions) + run_global('premkvirtualenv', *args) + return + + +def post_mkvirtualenv_source(args): + log.debug('post_mkvirtualenv_source %s', str(args)) + return """ +# +# Run user-provided scripts +# +[ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postmkvirtualenv" ] && \ + source "$VIRTUALENVWRAPPER_HOOK_DIR/postmkvirtualenv" +""" + + +def pre_cpvirtualenv(args): + log.debug('pre_cpvirtualenv %s', str(args)) + envname = args[0] + for filename, comment, permissions in LOCAL_HOOKS: + make_hook(get_path('$WORKON_HOME', envname, script_folder, filename), + comment, permissions) + run_global('precpvirtualenv', *args) + return + + +def post_cpvirtualenv_source(args): + log.debug('post_cpvirtualenv_source %s', str(args)) + return """ +# +# Run user-provided scripts +# +[ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postcpvirtualenv" ] && \ + source "$VIRTUALENVWRAPPER_HOOK_DIR/postcpvirtualenv" +""" + + +def pre_rmvirtualenv(args): + log.debug('pre_rmvirtualenv') + run_global('prermvirtualenv', *args) + return + + +def post_rmvirtualenv(args): + log.debug('post_rmvirtualenv') + run_global('postrmvirtualenv', *args) + return + + +def pre_activate(args): + log.debug('pre_activate') + run_global('preactivate', *args) + script_path = get_path('$WORKON_HOME', args[0], + script_folder, 'preactivate') + run_script(script_path, *args) + return + + +def post_activate_source(args): + log.debug('post_activate_source') + return """ +# +# Run user-provided scripts +# +[ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postactivate" ] && \ + source "$VIRTUALENVWRAPPER_HOOK_DIR/postactivate" +[ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/postactivate" ] && \ + source "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/postactivate" +""" + + +def pre_deactivate_source(args): + log.debug('pre_deactivate_source') + return """ +# +# Run user-provided scripts +# +[ -f "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/predeactivate" ] && \ + source "$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/predeactivate" +[ -f "$VIRTUALENVWRAPPER_HOOK_DIR/predeactivate" ] && \ + source "$VIRTUALENVWRAPPER_HOOK_DIR/predeactivate" +""" + + +def post_deactivate_source(args): + log.debug('post_deactivate_source') + return """ +# +# Run user-provided scripts +# +VIRTUALENVWRAPPER_LAST_VIRTUAL_ENV="$WORKON_HOME/%(env_name)s" +[ -f "$WORKON_HOME/%(env_name)s/bin/postdeactivate" ] && \ + source "$WORKON_HOME/%(env_name)s/bin/postdeactivate" +[ -f "$VIRTUALENVWRAPPER_HOOK_DIR/postdeactivate" ] && \ + source "$VIRTUALENVWRAPPER_HOOK_DIR/postdeactivate" +unset VIRTUALENVWRAPPER_LAST_VIRTUAL_ENV +""" % {'env_name': args[0]} + + +def get_env_details(args): + log.debug('get_env_details') + run_global('get_env_details', *args) + script_path = get_path('$WORKON_HOME', args[0], + script_folder, 'get_env_details') + run_script(script_path, *args) + return + + +def get_path(*args): + ''' + Get a full path from args. + + Path separator is determined according to the os and the shell and + allow to use is_msys. + + Variables and user are expanded during the process. + ''' + path = os.path.expanduser(os.path.expandvars(os.path.join(*args))) + if is_msys: + # MSYS accept unix or Win32 and sometimes + # it drives to mixed style paths + if re.match(r'^/[a-zA-Z](/|^)', path): + # msys path could starts with '/c/'-form drive letter + path = ''.join((path[1], ':', path[2:])) + path = path.replace('/', os.sep) + + return os.path.abspath(path) diff --git a/virtualenvwrapper_bashrc b/virtualenvwrapper_bashrc deleted file mode 100644 index 904a560..0000000 --- a/virtualenvwrapper_bashrc +++ /dev/null @@ -1,117 +0,0 @@ -# -# $Id$ -# -# Shell functions to act as wrapper for Ian Bicking's virtualenv -# (http://pypi.python.org/pypi/virtualenv) -# - -# -# Setup: -# -# 1. Add a line like "export WORKON_HOME=$HOME/.virtualenvs" -# to your .bashrc. -# 2. Add a line like "source /path/to/this/file/virtualenvwrapper_bashrc" -# to your .bashrc. -# 3. Run: source ~/.bashrc -# 4. Run: workon -# 5. A list of environments, empty, is printed. -# 6. Run: mkvirtualenv temp -# 7. Run: workon -# 8. This time, the "temp" environment is included. -# 9. Run: workon temp -# 10. The virtual environment is activated. -# - -# Make sure there is a default value for WORKON_HOME. -# You can override this setting in your .bashrc. -if [ "$WORKON_HOME" = "" ] -then - export WORKON_HOME="$HOME/.virtualenvs" -fi - -# Verify that the WORKON_HOME directory exists -function verify_workon_home () { - if [ ! -d "$WORKON_HOME" ] - then - echo "ERROR: Virtual environments directory '$WORKON_HOME' does not exist." - return 1 - fi - return 0 -} - -# Verify that the requested environment exists -function verify_workon_environment () { - typeset env_name="$1" - if [ ! -d "$WORKON_HOME/$env_name" ] - then - echo "ERROR: Environment '$env_name' does not exist. Create it with 'mkvirtualenv $env_name'." - return 1 - fi - return 0 -} - -# Create a new environment, in the WORKON_HOME. -# -# Usage: mkvirtualenv [options] ENVNAME -# (where the options are passed directly to virtualenv) -# -function mkvirtualenv () { - verify_workon_home - (cd "$WORKON_HOME"; virtualenv $*) - workon "${@:-1}" -} - -# Remove an environment, in the WORKON_HOME. -function rmvirtualenv () { - typeset env_name="$1" - verify_workon_home - env_dir="$WORKON_HOME/$env_name" - if [ "$VIRTUAL_ENV" == "$env_dir" ] - then - echo "ERROR: You cannot remove the active environment ('$env_name')." - return 1 - fi - rm -rf "$env_dir" -} - -# List the available environments. -function show_workon_options () { - verify_workon_home - ls "$WORKON_HOME" | egrep -v '*.egg' | sort -} - -# List or change working virtual environments -# -# Usage: workon [environment_name] -# -function workon () { - typeset env_name="$1" - if [ "$env_name" = "" ] - then - show_workon_options - return 1 - fi - - verify_workon_home || return 1 - verify_workon_environment $env_name || return 1 - - activate="$WORKON_HOME/$env_name/bin/activate" - if [ ! -f "$activate" ] - then - echo "ERROR: Environment '$WORKON_HOME/$env_name' does not contain an activate script." - return 1 - fi - - if [ -f "$VIRTUAL_ENV/bin/predeactivate" ] - then - source "$VIRTUAL_ENV/bin/predeactivate" - fi - - source "$activate" - - if [ -f "$VIRTUAL_ENV/bin/postactivate" ] - then - source "$VIRTUAL_ENV/bin/postactivate" - fi - return 0 -} \ No newline at end of file diff --git a/virtualenvwrapper_lazy.sh b/virtualenvwrapper_lazy.sh new file mode 100644 index 0000000..3902d08 --- /dev/null +++ b/virtualenvwrapper_lazy.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# Alternative startup script for faster login times. + +export _VIRTUALENVWRAPPER_API="$_VIRTUALENVWRAPPER_API mkvirtualenv rmvirtualenv lsvirtualenv showvirtualenv workon add2virtualenv cdsitepackages cdvirtualenv lssitepackages toggleglobalsitepackages cpvirtualenv setvirtualenvproject mkproject cdproject mktmpenv wipeenv allvirtualenv" + +if [ -z "$VIRTUALENVWRAPPER_SCRIPT" ] +then + export VIRTUALENVWRAPPER_SCRIPT="$(command \which virtualenvwrapper.sh)" +fi +if [ -z "$VIRTUALENVWRAPPER_SCRIPT" ] +then + echo "ERROR: virtualenvwrapper_lazy.sh: Could not find virtualenvwrapper.sh" 1>&2 +fi + +# Load the real implementation of the API from virtualenvwrapper.sh +function virtualenvwrapper_load { + # Only source the script once. + # We might get called multiple times, because not all of _VIRTUALENVWRAPPER_API gets + # a real completion. + if [ -z $VIRTUALENVWRAPPER_LAZY_LOADED ] + then + # NOTE: For Zsh, I have tried to unset any auto-load completion. + # (via `compctl + $(echo ${_VIRTUALENVWRAPPER_API})`. + # But this does not appear to work / triggers a crash. + source "$VIRTUALENVWRAPPER_SCRIPT" + VIRTUALENVWRAPPER_LAZY_LOADED=1 + fi +} + +# Set up "alias" functions based on the API definition. +function virtualenvwrapper_setup_lazy_loader { + typeset venvw_name + for venvw_name in $(echo ${_VIRTUALENVWRAPPER_API}) + do + eval " +function $venvw_name { + virtualenvwrapper_load + ${venvw_name} \"\$@\" +} +" + done +} + +# Set up completion functions to virtualenvwrapper_load +function virtualenvwrapper_setup_lazy_completion { + if [ -n "$BASH" ] ; then + function virtualenvwrapper_lazy_load { + virtualenvwrapper_load + return 124 + } + complete -o nospace -F virtualenvwrapper_lazy_load $(echo ${_VIRTUALENVWRAPPER_API}) + elif [ -n "$ZSH_VERSION" ] ; then + compctl -K virtualenvwrapper_load $(echo ${_VIRTUALENVWRAPPER_API}) + fi +} + +virtualenvwrapper_setup_lazy_loader +# Cannot be reset in zsh to fallback to files (e.g. mkvirtualenv). +virtualenvwrapper_setup_lazy_completion + +unset virtualenvwrapper_setup_lazy_loader +unset virtualenvwrapper_setup_lazy_completion