rewrite the documentation (#1519)

This commit is contained in:
Bernát Gábor 2020-02-04 14:51:00 +00:00 committed by GitHub
parent 786c3d0add
commit 7a5d03fe15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 1274 additions and 2461 deletions

View file

@ -6,7 +6,7 @@ repos:
args: [--safe]
language_version: python3.8
- repo: https://github.com/asottile/blacken-docs
rev: v1.4.0
rev: v1.5.0-1
hooks:
- id: blacken-docs
additional_dependencies: [black==19.10b0]
@ -38,7 +38,7 @@ repos:
additional_dependencies: ["flake8-bugbear == 20.1.2"]
language_version: python3.8
- repo: https://github.com/asottile/pyupgrade
rev: v1.26.0
rev: v1.26.2
hooks:
- id: pyupgrade
- repo: https://github.com/pre-commit/pygrep-hooks

18
.readthedocs.yml Normal file
View file

@ -0,0 +1,18 @@
version: 2
build:
image: latest
formats:
- htmlzip
- epub
- pdf
python:
version: 3.7
install:
- method: pip
path: .
extra_requirements:
- docs
sphinx:
builder: html
configuration: docs/conf.py
fail_on_warning: true

View file

@ -1,93 +0,0 @@
Author
------
Ian Bicking
Maintainers
-----------
Brian Rosner
Bernat Gabor
Carl Meyer
Jannis Leidel
Paul Moore
Paul Nasrat
Marcus Smith
Contributors
------------
Alex Grönholm
Anatoly Techtonik
Antonio Cuni
Antonio Valentino
Armin Ronacher
Barry Warsaw
Benjamin Root
Bradley Ayers
Branden Rolston
Brandon Carl
Brian Kearns
Cap Petschulat
CBWhiz
Chris Adams
Chris McDonough
Christos Kontas
Christian Hudon
Christian Stefanescu
Christopher Nilsson
Cliff Xuan
Curt Micol
Damien Nozay
Dan Sully
Daniel Hahler
Daniel Holth
David Schoonover
Denis Costa
Doug Hellmann
Doug Napoleone
Douglas Creager
Eduard-Cristian Stefan
Erik M. Bray
Ethan Jucovy
Gabriel de Perthuis
Gunnlaugur Thor Briem
Graham Dennis
Greg Haskins
Jason Penney
Jason R. Coombs
Jeff Hammel
Jeremy Orem
Jason Penney
Jason R. Coombs
John Kleint
Jonathan Griffin
Jonathan Hitchcock
Jorge Vargas
Josh Bronson
Kamil Kisiel
Kyle Gibson
Konstantin Zemlyak
Kumar McMillan
Lars Francke
Marc Abramowitz
Mika Laitio
Mike Hommey
Miki Tebeka
Philip Jenvey
Philippe Ombredanne
Piotr Dobrogost
Preston Holmes
Ralf Schmitt
Raul Leal
Ronny Pfannschmidt
Satrajit Ghosh
Sergio de Carvalho
Stefano Rivera
Tarek Ziadé
Thomas Aglassinger
Vinay Sajip
Vitaly Babiy
Vladimir Rutsky
Wang Xuerui
Wouter De Borger

View file

@ -1,25 +0,0 @@
virtualenv
==========
See docs/index.rst for user documentation.
Contributor notes
-----------------
* virtualenv is designed to work on python 2 and 3 with a single code base.
Use Python 3 print-function syntax, and always ``use sys.exc_info()[1]``
inside the ``except`` block to get at exception objects.
* Pull requests should be made against ``master`` branch, which is also our
latest stable version.
* All changes to files inside virtualenv_embedded must be integrated to
``virtualenv.py`` with ``tox -e embed``. The tox run will report failure
when changes are integrated, as a flag for CI.
* The codebase must be linted with ``tox -e fix_lint`` before being merged.
The tox run will report failure when the linters revise code, as a flag
for CI.
.. _git-flow: https://github.com/nvie/gitflow
.. _coordinate development: http://nvie.com/posts/a-successful-git-branching-model/

View file

@ -1,6 +1,4 @@
Copyright (c) 2007 Ian Bicking and Contributors
Copyright (c) 2009 Ian Bicking, The Open Planning Project
Copyright (c) 2011-2016 The virtualenv developers
Copyright (c) 2020-202x The virtualenv developers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View file

@ -4,7 +4,6 @@ exclude .gitignore
exclude .github/*
exclude azure-pipelines.yml
exclude CONTRIBUTING.rst
exclude readthedocs.yml
exclude MANIFEST.in

65
docs/_static/custom.css vendored Normal file
View file

@ -0,0 +1,65 @@
.wy-nav-content {
max-width: 51.5em;
padding: 1em;
}
#virtualenv img {
margin-bottom: 6px;
}
/* override table width restrictions */
.wy-table-responsive table th, .wy-table-responsive table td {
/* !important prevents the common CSS stylesheets from
overriding this as on RTD they are loaded after this stylesheet */
white-space: normal !important;
padding: 6px !important;
}
.wy-table-responsive {
overflow: visible !important;
}
.rst-content table.docutils td ol {
margin-bottom: 0;
}
.rst-content table.docutils td ul {
margin-bottom: 0;
}
.rst-content table.docutils td p {
margin-bottom: 0;
}
div[class*="highlight-"] {
margin-bottom: 12px;
}
table {
margin-left: 20px !important;
}
#release-history p {
margin-bottom: 0;
margin-top: 0;
}
#release-history h3 {
margin-bottom: 6px;
}
#release-history ul {
margin-bottom: 12px;
}
#release-history ul ul {
margin-bottom: 0;
}
#release-history h2 {
margin-bottom: 12px;
}
.rst-content code {
padding: 2px;
}

View file

@ -0,0 +1 @@
Create the first iteration of the new documentation - by :user:`gaborbernat`.

View file

@ -0,0 +1,2 @@
Bash activation script should have no extensions instead of ``.sh`` (this fixes the :pypi:`virtualenvwrapper`
integration) - by :user:`gaborbernat`.

View file

@ -0,0 +1,7 @@
Show less information when we run with a single verbosity (``-v``):
- no longer shows accepted interpreters information (as the last proposed one is always the accepted one),
- do not display the ``str_spec`` attribute for ``PythonSpec`` as these can be deduced from the other attributes,
- for the ``app-data`` seeder do not show the type of lock, only the path to the app data directory,
By :user:`gaborbernat`.

View file

@ -0,0 +1,2 @@
Fixed cannot discover a python interpreter that has already been discovered under a different path (such is the case
when we have multiple symlinks to the same interpreter) - by :user:`gaborbernat`.

View file

@ -1 +1 @@
Support relative paths for ``-p`` - by ``gaborbernat``.
Support relative paths for ``-p`` - by :user:`gaborbernat`.

View file

@ -11,4 +11,4 @@ Improve base executable discovery mechanism:
system pythons version - these two can differ if the OS upgraded the system python underneath and the virtualenv
host was created via copy),
by ``gaborbernat``.
by :user:`gaborbernat`.

View file

@ -1 +1 @@
Creating virtual environments in parallel fail with cannot acquire lock within app data - by ``gaborbernat``.
Creating virtual environments in parallel fail with cannot acquire lock within app data - by :user:`gaborbernat`.

View file

@ -1 +1 @@
pth files were not processed under Debian CPython2 interpreters - by ``gaborbernat``.
pth files were not processed under Debian CPython2 interpreters - by :user:`gaborbernat`.

View file

@ -1,2 +1,2 @@
Generate ``bash`` and ``fish`` activators on Windows too (as these can be available with git bash, cygwin or mysys2)
- by ``gaborbernat``.
- by :user:`gaborbernat`.

View file

@ -0,0 +1,2 @@
Fix prompt not displayed correctly with upcoming fish 3.10 due to us not preserving ``$pipestatus`` - by
:user:`krobelus`.

View file

@ -1 +1 @@
Project readme is now of type MarkDown instead of reStructuredText - by ``gaborbernat``.
Project readme is now of type MarkDown instead of reStructuredText - by :user:`gaborbernat`.

View file

@ -1 +1 @@
Upgrade the bundled ``wheel`` package from ``0.34.0`` to ``0.34.2`` - by ``gaborbernat``.
Upgrade the bundled ``wheel`` package from ``0.34.0`` to ``0.34.2`` - by :user:`gaborbernat`.

View file

@ -1,2 +1,2 @@
Stable order within ``pyenv.cfg`` and add ``include-system-site-packages`` only for creators that reference a global
Python - by ``gaborbernat``.
Python - by user:`gaborbernat`.

View file

@ -1,31 +1,36 @@
{% for section, _ in sections.items() %}
{% set underline = underlines[0] %}
{% if section %}
{{section}}
{{ underline * section|length }}
{% set underline = underlines[1] %}
{% set top_underline = underlines[0] %}
{% if versiondata.name %}
v{{ versiondata.version }} ({{ versiondata.date }})
{{ top_underline * ((versiondata.version + versiondata.date)|length + 4)}}
{% else %}
{{ versiondata.version }} ({{ versiondata.date }})
{{ top_underline * ((versiondata.version + versiondata.date)|length + 3)}}
{% endif %}
{% for section, _ in sections.items() %}
{% set underline = underlines[1] %}
{% if sections[section] %}
{% for category, val in definitions.items() if category in sections[section] %}
{{ definitions[category]['name'] }}
{{ underline * definitions[category]['name']|length }}
{% for category, val in definitions.items() if category in sections[section]%}
{{ definitions[category]['name'] }} - {{ versiondata.version }}
{{ underline * ((definitions[category]['name'] + versiondata.version)|length + 3)}}
{% if definitions[category]['showcontent'] %}
{% for text, values in sections[section][category].items() %}
- {{ text }} ({{ values|join(', ') }})
{% endfor %}
{% else %}
- {{ sections[section][category]['']|join(', ') }}
{% endif %}
{% endif %}
{% if sections[section][category]|length == 0 %}
No significant changes.
{% endif %}
{% endfor %}
{% else %}
No significant changes.
{% endif %}
{% endfor %}
{% else %}
No significant changes.
{% endif %}
{% endfor %}

File diff suppressed because it is too large Load diff

81
docs/cli_interface.rst Normal file
View file

@ -0,0 +1,81 @@
CLI interface
=============
.. _cli_flags:
cli flags
~~~~~~~~~
``virtualenv`` is primarily a command line interface application. It's mainly aimed to be used from a command line, as
such you'll need to to have a shell to run it. Then you can type in ``virtualenv`` (name of the application) followed by
flags that control its behaviour. All options do have a sensible default, so if you pass no options you'll get a
virtual environment in the current working directories ``venv`` folder. The Default values for the command line
options can be modified either via the :ref:`conf_file` or :ref:`env_vars`. Environment variables takes priority over
the configuration file values (the ``--help`` will show if a default comes from the environment variable as the help
message will end in this case either with ``via env var`` or ``via config file``).
Below you can see the options you can pass in, together with its default value, and a short description of what it does:
:command:`virtualenv [OPTIONS]`
.. table_cli::
:module: virtualenv.run
:func: build_parser
Defaults
~~~~~~~~
.. _conf_file:
Configuration file
^^^^^^^^^^^^^^^^^^
virtualenv looks for a standard ini config file. The exact place depends on the operating system you're using, as
determined by :pypi:`appdirs` application data definition. The config file location is printed as epilog for the CLI
tools help message.
The keys of the settings are derived from the long command line option, e.g. the option :option:`--python <python>`
would look like this:
.. code-block:: ini
[virtualenv]
python = /opt/python-3.3/bin/python
Appending options like :option:`extra-search-dir` can be written on multiple lines:
.. code-block:: ini
[virtualenv]
extra-search-dir =
/path/to/dists
/path/to/other/dists
.. _env_vars:
Environment Variables
^^^^^^^^^^^^^^^^^^^^^
Each command line option is automatically used to look for environment variables with the name format
``VIRTUALENV_<UPPER_NAME>``. That means the name of the command line options are capitalized and have dashes (``'-'``)
replaced with underscores (``'_'``).
For example, to automatically use a custom Python binary instead of the one virtualenv is run with you can also set an
environment variable:
.. code-block:: console
env VIRTUALENV_PYTHON=/opt/python-3.8/bin/python virtualenv
This also works for appending command line options, like :option:`extra-search-dir`. Just pass a literal newline
between the passed values, e.g.:
.. code-block:: console
env VIRTUALENV_EXTRA_SEARCH_DIR="/path/to/dists\n/path/to/other/dists" virtualenv
is the same as calling:
.. code-block:: console
virtualenv --extra-search-dir=/path/to/dists --extra-search-dir=/path/to/other/dists

View file

@ -1,74 +1,87 @@
from __future__ import absolute_import, unicode_literals
import os
import re
import subprocess
import sys
from datetime import date, datetime
from pathlib import Path
from virtualenv import __version__
import sphinx_rtd_theme
extensions = ["sphinx.ext.autodoc", "sphinx.ext.extlinks"]
source_suffix = ".rst"
master_doc = "index"
project = "virtualenv"
# noinspection PyShadowingBuiltins
copyright = "2007-2018, Ian Bicking, The Open Planning Project, PyPA"
ROOT_SRC_TREE_DIR = Path(__file__).parents[1]
def generate_draft_news():
home = "https://github.com"
issue = "{}/issue".format(home)
fragments_path = ROOT_SRC_TREE_DIR / "docs" / "changelog"
for pattern, replacement in (
(r"[^`]@([^,\s]+)", r"`@\1 <{}/\1>`_".format(home)),
(r"[^`]#([\d]+)", r"`#pr\1 <{}/\1>`_".format(issue)),
):
for path in fragments_path.glob("*.rst"):
path.write_text(re.sub(pattern, replacement, path.read_text()))
env = os.environ.copy()
env["PATH"] += os.pathsep.join([os.path.dirname(sys.executable)] + env["PATH"].split(os.pathsep))
changelog = subprocess.check_output(
["towncrier", "--draft", "--version", "DRAFT"], cwd=str(ROOT_SRC_TREE_DIR), env=env, universal_newlines=True
)
if "No significant changes" in changelog:
content = ""
else:
note = "*Changes in master, but not released yet are under the draft section*."
content = "{}\n\n{}".format(note, changelog)
(ROOT_SRC_TREE_DIR / "docs" / "_draft.rst").write_text(content)
generate_draft_news()
from virtualenv.version import __version__
company = "PyPA"
name = "virtualenv"
version = ".".join(__version__.split(".")[:2])
release = __version__
copyright = f"2007-{date.today().year}, {company}, PyPA"
today_fmt = "%B %d, %Y"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosectionlabel",
"sphinx.ext.extlinks",
]
templates_path = []
unused_docs = []
pygments_style = "sphinx"
exclude_patterns = ["changelog/*"]
source_suffix = ".rst"
exclude_patterns = ["_build", "changelog/*", "_draft.rst"]
master_doc = "index"
pygments_style = "default"
always_document_param_types = True
project = name
today_fmt = "%B %d, %Y"
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_theme_options = {
"canonical_url": "https://virtualenv.pypa.io/",
"logo_only": False,
"display_version": True,
"prev_next_buttons_location": "bottom",
"collapse_navigation": False,
"sticky_navigation": True,
"navigation_depth": 6,
"includehidden": True,
}
html_static_path = ["_static"]
html_last_updated_fmt = datetime.now().isoformat()
htmlhelp_basename = "Pastedoc"
autoclass_content = "both" # Include __init__ in class documentation
autodoc_member_order = "bysource"
autosectionlabel_prefix_document = True
extlinks = {
"issue": ("https://github.com/pypa/virtualenv/issues/%s", "#"),
"pull": ("https://github.com/pypa/virtualenv/pull/%s", "PR #"),
"user": ("https://github.com/%s", "@"),
"pypi": ("https://pypi.org/project/%s", ""),
}
html_theme = "sphinx_rtd_theme"
html_theme_options = {
"canonical_url": "https://virtualenv.pypa.io/en/latest/",
"logo_only": False,
"display_version": True,
"prev_next_buttons_location": "bottom",
"style_external_links": True,
# Toc options
"collapse_navigation": True,
"sticky_navigation": True,
"navigation_depth": 4,
"includehidden": True,
"titles_only": False,
}
html_last_updated_fmt = "%b %d, %Y"
htmlhelp_basename = "Pastedoc"
def generate_draft_news():
root = Path(__file__).parents[1]
exe = Path(sys.executable)
towncrier = exe.with_name("towncrier{}".format(exe.suffix))
new = subprocess.check_output([str(towncrier), "--draft", "--version", "NEXT"], cwd=root, universal_newlines=True)
(root / "docs" / "_draft.rst").write_text("" if "No significant changes" in new else new)
generate_draft_news()
def setup(app):
# the CLI arguments are dynamically generated
doc_tree = Path(app.doctreedir)
cli_interface_doctree = doc_tree / "cli_interface.doctree"
if cli_interface_doctree.exists():
cli_interface_doctree.unlink()
HERE = Path(__file__).parent
if str(HERE) not in sys.path:
sys.path.append(str(HERE))
# noinspection PyUnresolvedReferences
from render_cli import CliTable, literal_data
app.add_css_file("custom.css")
app.add_directive(CliTable.name, CliTable)
app.add_role("literal_data", literal_data)

View file

@ -1,56 +1,206 @@
Development
===========
Getting started
---------------
``virtualenv`` is a volunteer maintained open source project and we welcome contributions of all forms. The sections
below will help you get started with development, testing, and documentation. Were pleased that you are interested in
working on virtualenv. This document is meant to get you setup to work on virtualenv and to act as a guide and reference
to the development setup. If you face any issues during this process, please
`open an issue <https://github.com/pypa/virtualenv/issues/new?title=Trouble+with+development+environment>`_ about it on
the issue tracker.
Setup
~~~~~
virtualenv is a command line application written in Python, as such you'll need:
- **the source code** is available on `GitHub <https://github.com/pypa/pip>`_, so use some client to clone the
repository via:
.. code-block:: console
git clone https://github.com/pypa/virtualenv
cd virtualenv
- a **Python interpreter** is needed, we recommend using ``CPython``, you can use
`this guide <https://realpython.com/installing-python/>`_ to do that
- to automatically get the projects development dependencies and run the test suite you can use the :pypi:`tox` tool,
we recommend using the `pipx <https://pipxproject.github.io/pipx/>`_ project to achieve this.
Running from source tree
~~~~~~~~~~~~~~~~~~~~~~~~
The easiest way to do this is to generate the development tox environment, and then invoke virtualenv from under the
``.tox/dev`` folder
.. code-block:: console
tox -e dev
.tox/dev/bin/virtualenv # on Linux
.tox/dev/Scripts/virtualenv # on Windows
Running tests
~~~~~~~~~~~~~
virtualenv's tests are written using the :pypi:`pytest` test framework. :pypi:`tox` is used to automate the setup
and execution of virtualenv's tests.
To run tests locally execute:
.. code-block:: console
tox -e py
This will run the test suite for the same Python version as under what ``tox`` is installed. Alternatively you can
specify a specific version of python by using the ``pyxy`` format, such as: ``py38``, ``pypy3``, etc.
``tox`` has been configured to forward any additional arguments it is given to ``pytest``.
This enables the use of pytest's
`rich CLI <https://docs.pytest.org/en/latest/usage.html#specifying-tests-selecting-tests>`_. As an example, you can
select tests using the various ways that pytest provides:
.. code-block:: console
# Using markers
tox -e py -- -m "not slow"
# Using keywords
tox -e py -- -k "test_extra"
Some tests require additional dependencies to be run, such is the various shell activators (``bash``, ``fish``,
``powershell``, etc). These tests will automatically be skipped if these are not present, note however that in CI
all tests are run; so if all tests succeed locally it may still fail in the CI.
Running linters
~~~~~~~~~~~~~~~
virtualenv uses :pypi:`pre-commit` for managing linting of the codebase. ``pre-commit`` performs various checks on all
files in virtualenv and uses tools that help follow a consistent code style within the codebase. To use linters locally,
run:
.. code-block:: console
tox -e fix_lint
.. note::
Avoid using ``# noqa`` comments to suppress linter warnings - wherever possible, warnings should be fixed instead.
``# noqa`` comments are reserved for rare cases where the recommended style causes severe readability problems.
Building documentation
~~~~~~~~~~~~~~~~~~~~~~
virtualenv's documentation is built using :pypi:`Sphinx`. The documentation is written in reStructuredText. To build it
locally, run:
.. code-block:: console
tox -e docs
The built documentation can be found in the ``.tox/docs_out`` folder and may be viewed by opening ``index.html`` within
that folder.
Release
~~~~~~~
Virtualenv's release schedule is tied to ``pip``, ``setuptools`` and ``wheel``. We bundle the latest version of these
libraries so each time there's a new version of any of these, there will be a new virtualenv release shortly afterwards
(we usually wait just a few days to avoid pulling in any broken releases).
Contributing
------------
-------------
Refer to the `pip development`_ documentation - it applies equally to
virtualenv, except that virtualenv issues should be filed on the `virtualenv
repo`_ at GitHub.
Submitting pull requests
~~~~~~~~~~~~~~~~~~~~~~~~
Virtualenv's release schedule is tied to pip's -- each time there's a new pip
release, there will be a new virtualenv release that bundles the new version of
pip.
Submit pull requests against the ``master`` branch, providing a good description of what you're doing and why. You must
have legal permission to distribute any code you contribute to virtualenv and it must be available under the MIT
License. Provide tests that cover your changes and run the tests locally first. virtualenv
:ref:`supports <compatibility-requirements>` multiple Python versions and operating systems. Any pull request must
consider and work on all these platforms.
Files in the ``virtualenv_embedded/`` subdirectory are embedded into
``virtualenv.py`` itself as base64-encoded strings (in order to support
single-file use of ``virtualenv.py`` without installing it). If your patch
changes any file in ``virtualenv_embedded/``, run ``tox -e embed`` to update
the embedded version of that file in ``virtualenv.py``; commit that and submit
it as part of your patch / pull request. The tox run will report failure
when changes are embedded, as a flag for CI.
Pull Requests should be small to facilitate easier review. Keep them self-contained, and limited in scope.
`Studies have shown <https://www.kessler.de/prd/smartbear/BestPracticesForPeerCodeReview.pdf>`_ that review quality
falls off as patch size grows. Sometimes this will result in many small PRs to land a single large feature. In
particular, pull requests must not be treated as "feature branches", with ongoing development work happening within the
PR. Instead, the feature should be broken up into smaller, independent parts which can be reviewed and merged
individually.
The codebase should be linted before a pull request is merged by running
``tox -e fix_lint``. The tox run will report failure when any linting
revisions are required, as a flag for CI.
Additionally, avoid including "cosmetic" changes to code that is unrelated to your change, as these make reviewing the
PR more difficult. Examples include re-flowing text in comments or documentation, or addition or removal of blank lines
or whitespace within lines. Such changes can be made separately, as a "formatting cleanup" PR, if needed.
.. _pip development: https://pip.pypa.io/en/latest/development/
.. _virtualenv repo: https://github.com/pypa/virtualenv/
Automated testing
~~~~~~~~~~~~~~~~~
Running the tests
-----------------
All pull requests and merges to 'master' branch are tested using
`Azure Pipelines <https://azure.microsoft.com/en-gb/services/devops/pipelines/>`_ (configured by
``azure-pipelines.yml`` file at the root of the repository). You can find the status and results to the CI runs for your
PR on GitHub's Web UI for the pull request. You can also find links to the CI services' pages for the specific builds in
the form of "Details" links, in case the CI run fails and you wish to view the output.
The easy way to run tests (handles test dependencies automatically, works with the ``sdist`` too)::
To trigger CI to run again for a pull request, you can close and open the pull request or submit another change to the
pull request. If needed, project maintainers can manually trigger a restart of a job/build.
$ tox
NEWS entries
~~~~~~~~~~~~
Note you need to first install tox separately by using::
The ``changes.rst`` file is managed using :pypi:`towncrier` and all non trivial changes must be accompanied by a news
entry. To add an entry to the news file, first you need to have created an issue describing the change you want to
make. A Pull Request itself *may* function as such, but it is preferred to have a dedicated issue (for example, in case
the PR ends up rejected due to code quality reasons).
$ python -m pip --user install -U tox
Once you have an issue or pull request, you take the number and you create a file inside of the ``docs/changelog``
directory named after that issue number with an extension of:
Run ``python -m tox -av`` for a list of all supported Python environments or just run the
tests in all of the available ones by running just ``tox``.
- ``feature.rst``,
- ``bugfix.rst``,
- ``doc.rst``,
- ``removal.rst``,
- ``misc.rst``.
Status and License
------------------
Thus if your issue or PR number is ``1234`` and this change is fixing a bug, then you would create a file
``docs/changelog/1234.bugfix.rst``. PRs can span multiple categories by creating multiple files (for instance, if you
added a feature and deprecated/removed the old feature at the same time, you would create
``docs/changelog/1234.bugfix.rst`` and ``docs/changelog/1234.remove.rst``). Likewise if a PR touches multiple issues/PRs
you may create a file for each of them with the exact same contents and :pypi:`towncrier` will deduplicate them.
``virtualenv`` is a successor to `workingenv
<http://cheeseshop.python.org/pypi/workingenv.py>`_, and an extension
of `virtual-python
<http://peak.telecommunity.com/DevCenter/EasyInstall#creating-a-virtual-python>`_.
Contents of a NEWS entry
^^^^^^^^^^^^^^^^^^^^^^^^
It was written by Ian Bicking, sponsored by the `Open Planning
Project <http://openplans.org>`_ and is now maintained by a
`group of developers <https://github.com/pypa/virtualenv/raw/master/AUTHORS.txt>`_.
It is licensed under an
`MIT-style permissive license <https://github.com/pypa/virtualenv/raw/master/LICENSE.txt>`_.
The contents of this file are reStructuredText formatted text that will be used as the content of the news file entry.
You do not need to reference the issue or PR numbers here as towncrier will automatically add a reference to all of
the affected issues when rendering the news file.
In order to maintain a consistent style in the ``changes.rst`` file, it is preferred to keep the news entry to the
point, in sentence case, shorter than 120 characters and in an imperative tone -- an entry should complete the sentence
``This change will ...``. In rare cases, where one line is not enough, use a summary line in an imperative tone followed
by a blank line separating it from a description of the feature/change in one or more paragraphs, each wrapped
at 120 characters. Remember that a news entry is meant for end users and should only contain details relevant to an end
user.
Choosing the type of NEWS entry
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A trivial change is anything that does not warrant an entry in the news file. Some examples are: code refactors that
don't change anything as far as the public is concerned, typo fixes, white space modification, etc. To mark a PR
as trivial a contributor simply needs to add a randomly named, empty file to the ``news/`` directory with the extension
of ``.trivial``.
Becoming a maintainer
~~~~~~~~~~~~~~~~~~~~~
If you want to become an official maintainer, start by helping out. As a first step, we welcome you to triage issues on
virtualenv's issue tracker. virtualenv maintainers provide triage abilities to contributors once they have been around
for some time and contributed positively to the project. This is optional and highly recommended for becoming a
virtualenv maintainer. Later, when you think you're ready, get in touch with one of the maintainers and they will
initiate a vote among the existing maintainers.
.. note::
Upon becoming a maintainer, a person should be given access to various virtualenv-related tooling across
multiple platforms. These are noted here for future reference by the maintainers:
- GitHub Push Access
- PyPI Publishing Access
- CI Administration capabilities
- ReadTheDocs Administration capabilities

95
docs/extend.rst Normal file
View file

@ -0,0 +1,95 @@
Extend functionality
====================
``virtualenv`` allows one to extend the builtin functionality via a plugin system. To add a plugin you need to:
- write a python file containing the plugin code which follows our expected interface,
- package is a a python library,
- install it alongside the virtual environment.
.. warning::
The public API of some of these components is still to be finalized, consider the current interface a beta one
until we get some feedback on how well we planned ahead. We expect to do this by end of Q3 2020. Consider the class
interface explained below as initial draft proposal. We reserve the right to change the API until then, however such
changes will be communicated in a timely fashion, and you'll have time to migrate. Thank you for your understanding.
Python discovery
----------------
The python discovery mechanism is a component that needs to answer the following answer: based on some type of user
input give me a Python interpreter on the machine that matches that. The builtin interpreter achieves tries to discover
an installed Python interpreter (based on PEP-515 and ``PATH`` discovery) on the users machine where the user input is a
python specification. An alternative such discovery mechanism for example would be to use the popular
`pyenv <https://github.com/pyenv/pyenv>`_ project to discover, and if not present install the requested Python
interpreter. Python discovery mechanisms must be registered under key ``virtualenv.discovery``, and the plugin must
implement :class:`virtualenv.discovery.discover.Discover`:
.. code-block:: ini
virtualenv.discovery =
pyenv = virtualenv_pyenv.discovery:PyEnvDiscovery
.. currentmodule:: virtualenv.discovery.discover
.. autoclass:: Discover
:undoc-members:
:members:
Creators
--------
Creators are what actually perform the creation of a virtual environment. The builtin virtual environment creators
all achieve this by referencing a global install; but would be just as valid for a creator to install a brand new
entire python under the target path; or one could add additional creators that can create virtual environments for other
python implementations, such as IronPython. They must be registered under and entry point with key
``virtualenv.discovery`` , and the class must implement :class:`virtualenv.create.creator.Creator`:
.. code-block:: ini
virtualenv.create =
cpython3-posix = virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Posix
.. currentmodule:: virtualenv.create.creator
.. autoclass:: Creator
:undoc-members:
:members:
:exclude-members: run, set_pyenv_cfg, debug_script, debug_script, validate_dest, debug
Seed mechanism
--------------
Seeders are what given a virtual environment will install somehow some seed packages into it. They must be registered
under and entry point with key ``virtualenv.seed`` , and the class must implement
:class:`virtualenv.seed.seeder.Seeder`:
.. code-block:: ini
virtualenv.seed =
db = virtualenv.seed.fromDb:InstallFromDb
.. currentmodule:: virtualenv.seed.seeder
.. autoclass:: Seeder
:undoc-members:
:members:
Activation scripts
------------------
If you want add an activator for a new shell you can do this by implementing a new activator. They must be registered
under and entry point with key ``virtualenv.activate`` , and the class must implement
:class:`virtualenv.activation.activator.Activator`:
.. code-block:: ini
virtualenv.activate =
bash = virtualenv.activation.bash:BashActivator
.. currentmodule:: virtualenv.activation.activator
.. autoclass:: Activator
:undoc-members:
:members:

View file

@ -1,138 +1,86 @@
Virtualenv
==========
`Mailing list <http://groups.google.com/group/python-virtualenv>`_ |
`Issues <https://github.com/pypa/virtualenv/issues>`_ |
`Github <https://github.com/pypa/virtualenv>`_ |
`PyPI <https://pypi.org/project/virtualenv/>`_ |
User IRC: #pypa
Dev IRC: #pypa-dev
.. image:: https://img.shields.io/pypi/v/virtualenv?style=flat-square
:target: https://pypi.org/project/virtualenv/#history
:alt: Latest version on PyPI
.. image:: https://img.shields.io/pypi/implementation/virtualenv?style=flat-square
:alt: PyPI - Implementation
.. image:: https://img.shields.io/pypi/pyversions/virtualenv?style=flat-square
:alt: PyPI - Python Version
.. image:: https://readthedocs.org/projects/virtualenv/badge/?version=latest&style=flat-square
:target: https://virtualenv.pypa.io
:alt: Documentation status
.. image:: https://img.shields.io/pypi/dm/virtualenv?style=flat-square
:target: https://pypistats.org/packages/virtualenv
:alt: PyPI - Downloads
.. image:: https://img.shields.io/pypi/l/virtualenv?style=flat-square
:target: https://opensource.org/licenses/MIT
:alt: PyPI - License
.. image:: https://img.shields.io/github/issues/pypa/virtualenv?style=flat-square
:target: https://github.com/pypa/virtualenv/issues
:alt: Open issues
.. image:: https://img.shields.io/github/issues-pr/pypa/virtualenv?style=flat-square
:target: https://github.com/pypa/virtualenv/pulls
:alt: Open pull requests
.. image:: https://img.shields.io/github/stars/pypa/virtualenv?style=flat-square
:target: https://pypistats.org/packages/virtualenv
:alt: Package popularity
Introduction
``virtualenv`` is a tool to create isolated Python environments. Since Python ``3.3``, a subset of it has been
integrated into the standard library under the `venv module <https://docs.python.org/3/library/venv.html>`_. The
``venv`` module does not offer all features of this library, to name just a few more prominent:
- is slower (by not having the ``app-data`` seed method),
- is not as extensible,
- cannot create virtual environments for arbitrarily installed python versions (and automatically discover these),
- is not upgrade-able via `pip <https://pip.pypa.io/en/stable/installing/>`_,
- does not have as rich programmatic API (describe virtual environments without creating them).
The basic problem being addressed is one of dependencies and versions, and indirectly permissions.
Imagine you have an application that needs version ``1`` of ``LibFoo``, but another application requires version
``2``. How can you use both these libraries? If you install everything into your host python (e.g. ``python3.8``)
it's easy to end up in a situation where two packages have conflicting requirements.
Or more generally, what if you want to install an application *and leave it be*? If an application works, any change
in its libraries or the versions of those libraries can break the application. Also, what if you can't install packages
into the global ``site-packages`` directory, due to not having permissions to change the host python environment?
In all these cases, ``virtualenv`` can help you. It creates an environment that has its own installation directories,
that doesn't share libraries with other virtualenv environments (and optionally doesn't access the globally installed
libraries either).
Useful links
------------
**Related projects, abstsractions on top of it**
``virtualenv`` is a tool to create isolated Python environments. Since
Python 3.3, a subset of it has been integrated into the standard
library under the `venv module <https://docs.python.org/3/library/venv.html>`_.
Note though, that the ``venv`` module does not offer all features of this
library (e.g. cannot create bootstrap scripts, cannot create virtual
environments for other python versions than the host python,
not relocatable, etc.). Tools in general as such still may prefer using
virtualenv for its ease of upgrading (via pip), unified handling of different
Python versions and some more advanced features.
* :pypi:`virtualenvwrapper` is a useful set of scripts to make your workflow with many virtualenv even easier
* :pypi:`pew` is another wrapper for virtualenv that makes use of a different activation technique
* :pypi:`tox` - integrates setting up and running tests within virtual environments driven by a ``tox.ini``
configuration file
* :pypi:`nox` - integrates setting up and running tests within virtual environments driven by a ``nox.py``
python file
The basic problem being addressed is one of dependencies and versions,
and indirectly permissions. Imagine you have an application that
needs version 1 of LibFoo, but another application requires version
2. How can you use both these applications? If you install
everything into ``/usr/lib/python2.7/site-packages`` (or whatever your
platform's standard location is), it's easy to end up in a situation
where you unintentionally upgrade an application that shouldn't be
upgraded.
**Tutorials**
Or more generally, what if you want to install an application *and
leave it be*? If an application works, any change in its libraries or
the versions of those libraries can break the application.
* `Corey Schafer tutorial <https://www.youtube.com/watch?v=N5vscPTWKOk>`_ on how to use it
* `Using virtualenv with mod_wsgi <http://code.google.com/p/modwsgi/wiki/VirtualEnvironments>`_
Also, what if you can't install packages into the global
``site-packages`` directory? For instance, on a shared host.
**Presenting how the package works from within**
In all these cases, ``virtualenv`` can help you. It creates an
environment that has its own installation directories, that doesn't
share libraries with other virtualenv environments (and optionally
doesn't access the globally installed libraries either).
* `Bernat Gabor: status quo of virtual environments <https://www.youtube.com/watch?v=o1Vue9CWRxU>`_
* `Carl Meyer: Reverse-engineering Ian Bicking's brain: inside pip and virtualenv
<http://pyvideo.org/video/568/reverse-engineering-ian-bicking--39-s-brain--insi>`_
.. comment: split here
.. toctree::
:maxdepth: 2
:hidden:
installation
userguide
reference
user_guide
cli_interface
extend
development
changes
Other Documentation and Links
-----------------------------
* `Blog announcement of virtualenv`__.
.. __: http://blog.ianbicking.org/2007/10/10/workingenv-is-dead-long-live-virtualenv/
* James Gardner has written a tutorial on using `virtualenv with
Pylons
<http://wiki.pylonshq.com/display/pylonscookbook/Using+a+Virtualenv+Sandbox>`_.
* Chris Perkins created a `showmedo video including virtualenv
<http://showmedo.com/videos/video?name=2910000&fromSeriesID=291>`_.
* Doug Hellmann's `virtualenvwrapper`_ is a useful set of scripts to make
your workflow with many virtualenvs even easier. `His initial blog post on it`__.
He also wrote `an example of using virtualenv to try IPython`__.
.. _virtualenvwrapper: https://pypi.org/project/virtualenvwrapper/
.. __: https://doughellmann.com/blog/2008/05/01/virtualenvwrapper/
.. __: https://doughellmann.com/blog/2008/02/01/ipython-and-virtualenv/
* `Pew`_ is another wrapper for virtualenv that makes use of a different
activation technique.
.. _Pew: https://pypi.org/project/pew/
* `Using virtualenv with mod_wsgi
<http://code.google.com/p/modwsgi/wiki/VirtualEnvironments>`_.
* `virtualenv commands
<https://github.com/thisismedium/virtualenv-commands>`_ for some more
workflow-related tools around virtualenv.
* PyCon US 2011 talk: `Reverse-engineering Ian Bicking's brain: inside pip and virtualenv
<http://pyvideo.org/video/568/reverse-engineering-ian-bicking--39-s-brain--insi>`_.
By the end of the talk, you'll have a good idea exactly how pip
and virtualenv do their magic, and where to go looking in the source
for particular behaviors or bug fixes.
Compare & Contrast with Alternatives
------------------------------------
There are several alternatives that create isolated environments:
* Python 3's `venv module <https://docs.python.org/3/library/venv.html>`_
is recommended for projects that no longer need to support Python 2 and want
to create just simple environments for the host python.
* ``workingenv`` (which I do not suggest you use anymore) is the
predecessor to this library. It used the main Python interpreter,
but relied on setting ``$PYTHONPATH`` to activate the environment.
This causes problems when running Python scripts that aren't part of
the environment (e.g., a globally installed ``hg`` or ``bzr``). It
also conflicted a lot with Setuptools.
* `virtual-python
<http://peak.telecommunity.com/DevCenter/EasyInstall#creating-a-virtual-python>`_
is also a predecessor to this library. It uses only symlinks, so it
couldn't work on Windows. It also symlinks over the *entire*
standard library and global ``site-packages``. As a result, it
won't see new additions to the global ``site-packages``.
This script only symlinks a small portion of the standard library
into the environment, and so on Windows it is feasible to simply
copy these files over. Also, it creates a new/empty
``site-packages`` and also adds the global ``site-packages`` to the
path, so updates are tracked separately. This script also installs
Setuptools automatically, saving a step and avoiding the need for
network access.
* `zc.buildout <http://pypi.org/project/zc.buildout>`_ doesn't
create an isolated Python environment in the same style, but
achieves similar results through a declarative config file that sets
up scripts with very particular packages. As a declarative system,
it is somewhat easier to repeat and manage, but more difficult to
experiment with. ``zc.buildout`` includes the ability to setup
non-Python systems (e.g., a database server or an Apache instance).
I *strongly* recommend anyone doing application development or
deployment use one of these tools.

View file

@ -1,69 +1,59 @@
Installation
============
.. warning::
via pipx
--------
We advise installing virtualenv-1.9 or greater. Prior to version 1.9, the
pip included in virtualenv did not download from PyPI over SSL.
:pypi:`virtualenv` is a CLI tool that needs a Python interpreter to run. If you already have a ``Python 3.5+``
interpreter the best is to use :pypi:`pipx` to install virtualenv into an isolated environment. This has the added
benefit that later you'll be able to upgrade virtualenv without affecting other parts of the system.
.. warning::
.. code-block:: console
When using pip to install virtualenv, we advise using pip 1.3 or greater.
Prior to version 1.3, pip did not download from PyPI over SSL.
pipx install virtualenv
virtualenv --help
.. warning::
via pip
-------
Alternatively you can install it within the global Python interpreter itself (perhaps as a user package via the
``--user`` flag). Be cautious if you are using a python install that is managed by your operating system or
another package manager. ``pip`` might not coordinate with those tools, and may leave your system in an
inconsistent state.
We advise against using easy_install to install virtualenv when using
setuptools < 0.9.7, because easy_install didn't download from PyPI over SSL
and was broken in some subtle ways.
.. code-block:: console
In Windows, run the ``pip`` provided by your Python installation to install ``virtualenv``.
python -m pip --user install virtualenv
python -m virtualenv --help
::
via zipapp
----------
> pip install virtualenv
You can use virtualenv without installing it too. We publish a Python
`zipapp <https://docs.python.org/3/library/zipapp.html>`_, you can just download this from
`https://bootstrap.pypa.io/virtualenv.pyz <https://bootstrap.pypa.io/virtualenv.pyz>`_ and invoke this package
with a python interpreter:
In non-Windows systems it is discouraged to run ``pip`` as root including with ``sudo``.
Generally use your system package manager if it provides a package.
This avoids conflicts in versions and file locations between the system package manager and ``pip``.
See your distribution's package manager documentation for instructions on using it to install ``virtualenv``.
.. code-block:: console
Using ``pip install --user`` is less hazardous but can still cause trouble within the particular user account.
If a system package expects the system provided ``virtualenv`` and an incompatible version is installed with ``--user`` that package may have problems within that user account.
To install within your user account with ``pip`` (if you have pip 1.3 or greater installed):
python virtualenv.pyz --help
::
The root level zipapp is always the current latest release. To get the last supported zipapp against a given python
minor release use the link ``https://bootstrap.pypa.io/x.y/virtualenv.pyz``, e.g. for the last virtualenv supporting
Python 2.7 use `https://bootstrap.pypa.io/2.7/virtualenv.pyz <https://bootstrap.pypa.io/2.7/virtualenv.pyz>`_.
$ pip install --user virtualenv
.. _compatibility-requirements:
Note: The specific ``bin`` path may vary per distribution but is often ``~/.local/bin`` and must be added to your ``$PATH`` if not already present.
Python and OS Compatibility
---------------------------
Or to get the latest unreleased dev version:
virtualenv works with the following Python interpreter implementations:
::
- `CPython <https://www.python.org/>`_ versions 2.7, 3.4, 3.5, 3.6, 3.7, 3.8
- `PyPy <https://pypy.org/>`_ 2.7 and 3.4+.
$ pip install --user https://github.com/pypa/virtualenv/tarball/master
This means virtualenv works on the latest patch version of each of these minor versions. Previous patch versions are
supported on a best effort approach. virtualenv works on the following platforms:
To install version ``X.X.X`` globally from source:
::
$ pip install --user https://github.com/pypa/virtualenv/tarball/X.X.X
To *use* locally from source:
::
$ curl --location --output virtualenv-X.X.X.tar.gz https://github.com/pypa/virtualenv/tarball/X.X.X
$ tar xvfz virtualenv-X.X.X.tar.gz
$ cd pypa-virtualenv-YYYYYY
$ python virtualenv.py myVE
.. note::
The ``virtualenv.py`` script is *not* supported if run without the
necessary pip/setuptools/virtualenv distributions available locally. All
of the installation methods above include a ``virtualenv_support``
directory alongside ``virtualenv.py`` which contains a complete set of
pip and setuptools distributions, and so are fully supported.
- Unix/Linux,
- macOS,
- Windows.

View file

@ -1,331 +0,0 @@
Reference Guide
===============
``virtualenv`` Command
----------------------
.. _usage:
Usage
~~~~~
:command:`virtualenv [OPTIONS] ENV_DIR`
Where ``ENV_DIR`` is an absolute or relative path to a directory to create
the virtual environment in.
.. _options:
Options
~~~~~~~
.. program: virtualenv
.. option:: --version
show program's version number and exit
.. option:: -h, --help
show this help message and exit
.. option:: -v, --verbose
Increase verbosity.
.. option:: -q, --quiet
Decrease verbosity.
.. option:: -p PYTHON_EXE, --python=PYTHON_EXE
The Python interpreter to use, e.g.,
``--python=python2.5`` will use the python2.5 interpreter
to create the new environment. The default is the
interpreter that virtualenv was installed with
(like ``/usr/bin/python``)
.. option:: --clear
Clear out the non-root install and start from scratch.
.. option:: --system-site-packages
Give the virtual environment access to the global
site-packages.
.. option:: --always-copy
Always copy files rather than symlinking.
.. option:: --relocatable
Make an EXISTING virtualenv environment relocatable.
This fixes up scripts and makes all .pth files relative.
.. option:: --unzip-setuptools
Unzip Setuptools when installing it.
.. option:: --no-setuptools
Do not install setuptools in the new virtualenv.
.. option:: --no-pip
Do not install pip in the new virtualenv.
.. option:: --no-wheel
Do not install wheel in the new virtualenv.
.. option:: --extra-search-dir=DIR
Directory to look for setuptools/pip distributions in.
This option can be specified multiple times.
.. option:: --prompt=PROMPT
Provides an alternative prompt prefix for this
environment.
.. option:: --download
Download preinstalled packages from PyPI.
.. option:: --no-download
Do not download preinstalled packages from PyPI.
.. option:: --no-site-packages
DEPRECATED. Retained only for backward compatibility.
Not having access to global site-packages is now the
default behavior.
.. option:: --distribute
.. option:: --setuptools
Legacy; now have no effect. Before version 1.10 these could be used
to choose whether to install Distribute_ or Setuptools_ into the created
virtualenv. Distribute has now been merged into Setuptools, and the
latter is always installed.
.. _Distribute: https://pypi.org/project/distribute
.. _Setuptools: https://pypi.org/project/setuptools
Configuration
-------------
Environment Variables
~~~~~~~~~~~~~~~~~~~~~
Each command line option is automatically used to look for environment
variables with the name format ``VIRTUALENV_<UPPER_NAME>``. That means
the name of the command line options are capitalized and have dashes
(``'-'``) replaced with underscores (``'_'``).
For example, to automatically use a custom Python binary instead of the
one virtualenv is run with you can also set an environment variable::
$ export VIRTUALENV_PYTHON=/opt/python-3.3/bin/python
$ virtualenv ENV
It's the same as passing the option to virtualenv directly::
$ virtualenv --python=/opt/python-3.3/bin/python ENV
This also works for appending command line options, like ``--find-links``.
Just leave an empty space between the passed values, e.g.::
$ export VIRTUALENV_EXTRA_SEARCH_DIR="/path/to/dists /path/to/other/dists"
$ virtualenv ENV
is the same as calling::
$ virtualenv --extra-search-dir=/path/to/dists --extra-search-dir=/path/to/other/dists ENV
.. envvar:: VIRTUAL_ENV_DISABLE_PROMPT
Any virtualenv *activated* when this is set to a non-empty value will leave
the shell prompt unchanged during processing of the
:ref:`activate script <activate>`, rather than modifying it to indicate
the newly activated environment.
Configuration File
~~~~~~~~~~~~~~~~~~
virtualenv also looks for a standard ini config file. On Unix and Mac OS X
that's ``$HOME/.virtualenv/virtualenv.ini`` and on Windows, it's
``%APPDATA%\virtualenv\virtualenv.ini``.
The names of the settings are derived from the long command line option,
e.g. the option :option:`--python <-p>` would look like this::
[virtualenv]
python = /opt/python-3.3/bin/python
Appending options like :option:`--extra-search-dir` can be written on multiple
lines::
[virtualenv]
extra-search-dir =
/path/to/dists
/path/to/other/dists
Please have a look at the output of :option:`--help <-h>` for a full list
of supported options.
Extending Virtualenv
--------------------
Creating Your Own Bootstrap Scripts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
While this creates an environment, it doesn't put anything into the
environment. Developers may find it useful to distribute a script
that sets up a particular environment, for example a script that
installs a particular web application.
.. note::
A bootstrap script requires a ``virtualenv_support`` directory containing
``pip`` and ``setuptools`` wheels alongside it, just like the actual virtualenv
script. Running a bootstrap script without a ``virtualenv_support`` directory
is unsupported (but if you use ``--no-setuptools`` and manually install ``pip``
and ``setuptools`` in your virtualenv, it will work).
To create a script like this, call
:py:func:`virtualenv.create_bootstrap_script`, and write the
result to your new bootstrapping script.
.. py:function:: create_bootstrap_script(extra_text)
Creates a bootstrap script from ``extra_text``, which is like
this script but with extend_parser, adjust_options, and after_install hooks.
This returns a string that (written to disk of course) can be used
as a bootstrap script with your own customizations. The script
will be the standard virtualenv.py script, with your extra text
added (your extra text should be Python code).
If you include these functions, they will be called:
.. py:function:: extend_parser(optparse_parser)
You can add or remove options from the parser here.
.. py:function:: adjust_options(options, args)
You can change options here, or change the args (if you accept
different kinds of arguments, be sure you modify ``args`` so it is
only ``[DEST_DIR]``).
.. py:function:: after_install(options, home_dir)
After everything is installed, this function is called. This
is probably the function you are most likely to use. An
example would be::
def after_install(options, home_dir):
if sys.platform == 'win32':
bin = 'Scripts'
else:
bin = 'bin'
subprocess.call([join(home_dir, bin, 'easy_install'),
'MyPackage'])
subprocess.call([join(home_dir, bin, 'my-package-script'),
'setup', home_dir])
This example immediately installs a package, and runs a setup
script from that package.
Bootstrap Example
~~~~~~~~~~~~~~~~~
Here's a more concrete example of how you could use this::
import virtualenv, textwrap
output = virtualenv.create_bootstrap_script(textwrap.dedent("""
import os, subprocess
def after_install(options, home_dir):
etc = join(home_dir, 'etc')
if not os.path.exists(etc):
os.makedirs(etc)
subprocess.call([join(home_dir, 'bin', 'easy_install'),
'BlogApplication'])
subprocess.call([join(home_dir, 'bin', 'paster'),
'make-config', 'BlogApplication',
join(etc, 'blog.ini')])
subprocess.call([join(home_dir, 'bin', 'paster'),
'setup-app', join(etc, 'blog.ini')])
"""))
f = open('blog-bootstrap.py', 'w').write(output)
Another example is available `here`__.
.. __: https://github.com/socialplanning/fassembler/blob/master/fassembler/create-venv-script.py
Compatibility with the stdlib venv module
-----------------------------------------
Starting with Python 3.3, the Python standard library includes a ``venv``
module that provides similar functionality to ``virtualenv`` - however, the
mechanisms used by the two modules are very different.
Problems arise when environments get "nested" (a virtual environment is
created from within another one - for example, running the virtualenv tests
using tox, where tox creates a virtual environment to run the tests, and the
tests themselves create further virtual environments).
``virtualenv`` supports creating virtual environments from within another one
(the ``sys.real_prefix`` variable allows ``virtualenv`` to locate the "base"
environment) but stdlib-style ``venv`` environments don't use that mechanism,
so explicit support is needed for those environments.
A standard library virtual environment is most easily identified by checking
``sys.prefix`` and ``sys.base_prefix``. If these differ, the interpreter is
running in a virtual environment and the base interpreter is located in the
directory specified by ``sys.base_prefix``. Therefore, when
``sys.base_prefix`` is set, virtualenv gets the interpreter files from there
rather than from ``sys.prefix`` (in the same way as ``sys.real_prefix`` is
used for virtualenv-style environments). In practice, this is sufficient for
all platforms other than Windows.
On Windows from Python 3.7.2 onwards, a stdlib-style virtual environment does
not contain an actual Python interpreter executable, but rather a "redirector"
which launches the actual interpreter from the base environment (this
redirector is based on the same code as the standard ``py.exe`` launcher). As
a result, the virtualenv approach of copying the interpreter from the starting
environment fails. In order to correctly set up the virtualenv, therefore, we
need to be running from a "full" environment. To ensure that, we re-invoke the
``virtualenv.py`` script using the "base" interpreter, in the same way as we
do with the ``--python`` command line option.
The process of identifying the base interpreter is complicated by the fact
that the implementation changed between different Python versions. The
logic used is as follows:
1. If the (private) attribute ``sys._base_executable`` is present, this is
the base interpreter. This is the long-term solution and should be stable
in the future (the attribute may become public, and have the leading
underscore removed, in a Python 3.8, but that is not confirmed yet).
2. In the absence of ``sys._base_executable`` (only the case for Python 3.7.2)
we check for the existence of the environment variable
``__PYVENV_LAUNCHER__``. This is used by the redirector, and if it is
present, we know that we are in a stdlib-style virtual environment and need
to locate the base Python. In most cases, the base environment is located
at ``sys.base_prefix`` - however, in the case where the user creates a
virtualenv, and then creates a venv from that virtualenv,
``sys.base_prefix`` is not correct - in that case, though, we have
``sys.real_prefix`` (set by virtualenv) which *is* correct.
There is one further complication - as noted above, the environment variable
``__PYVENV_LAUNCHER__`` affects how the interpreter works, so before we
re-invoke the virtualenv script, we remove this from the environment.

230
docs/render_cli.py Normal file
View file

@ -0,0 +1,230 @@
from argparse import SUPPRESS
from collections import namedtuple
from contextlib import contextmanager
from docutils import nodes as n
from docutils.parsers.rst.directives import unchanged_required
from sphinx.util.docutils import SphinxDirective
from sphinxarg.parser import parse_parser
from virtualenv.run.plugin.base import ComponentBuilder
TableRow = namedtuple("TableRow", ["names", "default", "choices", "help"])
TextAsDefault = namedtuple("TextAsDefault", ["text"])
CUSTOM = {
"discovery": ComponentBuilder.entry_points_for("virtualenv.discovery"),
"creator": ComponentBuilder.entry_points_for("virtualenv.create"),
"seeder": ComponentBuilder.entry_points_for("virtualenv.seed"),
"activators": ComponentBuilder.entry_points_for("virtualenv.activate"),
}
class CliTable(SphinxDirective):
name = "table_cli"
option_spec = dict(module=unchanged_required, func=unchanged_required)
def run(self):
module_name, attr_name = self.options["module"], self.options["func"]
parser_creator = getattr(__import__(module_name, fromlist=[attr_name]), attr_name)
core_result = parse_parser(parser_creator())
core_result["action_groups"] = [i for i in core_result["action_groups"] if i["title"] not in CUSTOM]
content = []
for i in core_result["action_groups"]:
content.append(self._build_table(i["options"], i["title"], i["description"]))
for key, name_to_class in CUSTOM.items():
section = n.section("", ids=["section-{}".format(key)])
title = n.title("", key)
section += title
self.state.document.note_implicit_target(title)
content.append(section)
results = {}
for name, class_n in name_to_class.items():
with self._run_parser(class_n, key, name):
cmd = ["--{}".format(key), name]
parser_result = parse_parser(parser_creator(cmd))
opt_group = next(i["options"] for i in parser_result["action_groups"] if i["title"] == key)
results[name] = opt_group
core_names = set.intersection(*list({tuple(i["name"]) for i in v} for v in results.values()))
if core_names:
rows = [i for i in next(iter(results.values())) if tuple(i["name"]) in core_names]
content.append(
self._build_table(rows, title="core", description="options shared across all {}".format(key))
)
for name, group in results.items():
rows = [i for i in group if tuple(i["name"]) not in core_names]
if rows:
content.append(
self._build_table(rows, title=name, description="options specific to {} {}".format(key, name))
)
return content
@contextmanager
def _run_parser(self, class_n, key, name):
test_name = {"creator": "can_create", "activators": "supports"}
func_name = test_name.get(key)
try:
if func_name is not None:
prev = getattr(class_n, func_name)
def a(*args, **kwargs):
prev(*args, **kwargs)
if key == "activators":
return True
elif key == "creator":
if name == "venv":
from virtualenv.create.via_global_ref.venv import Meta
return Meta(True, True)
from virtualenv.create.via_global_ref.builtin.via_global_self_do import Meta
return Meta([], True, True)
raise RuntimeError
setattr(class_n, func_name, a)
yield
finally:
if func_name is not None:
# noinspection PyUnboundLocalVariable
setattr(class_n, func_name, prev)
def _build_table(self, options, title, description):
table = n.table()
table["classes"] += ["colwidths-auto"]
options_group = n.tgroup(cols=3)
table += options_group
for _ in range(3):
options_group += n.colspec()
body = self._make_table_body(self.build_rows(options), title, description)
options_group += body
return table
plugins = {
"creator": "virtualenv.create",
"seed": "virtualenv.seed",
"activators": "virtualenv.activate",
"discovery": "virtualenv.discovery",
}
@staticmethod
def build_rows(options):
result = []
for option in options:
names = option["name"]
default = option["default"]
if default is not None:
if isinstance(default, str) and default[0] == default[-1] and default[0] == '"':
default = default[1:-1]
if default == SUPPRESS:
default = None
choices = option.get("choices")
key = names[0].strip("-")
if key in CliTable.plugins:
choices = list(ComponentBuilder.entry_points_for(CliTable.plugins[key]).keys())
help_text = option["help"]
row = TableRow(names, default, choices, help_text)
result.append(row)
return result
def _make_table_body(self, rows, title, description):
t_body = n.tbody()
header_row = n.paragraph()
header_row += n.strong(text=title)
if description:
header_row += n.Text("")
header_row += n.Text(description)
t_body += n.row("", n.entry("", header_row, morecols=2))
for row in rows:
name_list = self._get_targeted_names(row)
default = CliTable._get_default(row)
help_text = CliTable._get_help_text(row)
row_node = n.row("", n.entry("", name_list), n.entry("", default), n.entry("", help_text))
t_body += row_node
return t_body
def _get_targeted_names(self, row):
names = [name.lstrip("-") for name in row.names]
target = n.target("", "", ids=names, names=names)
self.register_target_option(target)
first = True
for name, orig in zip(names, row.names):
if first:
first = False
else:
target += n.Text(", ")
self_ref = n.reference(refid=name)
self_ref += n.literal(text=orig)
target += self_ref
para = n.paragraph(text="")
para += target
return para
@staticmethod
def _get_help_text(row):
name = row.names[0]
if name in ("--creator", "--clear-app-data"):
content = row.help[: row.help.index("(") - 1]
else:
content = row.help
if name in ("--setuptools", "--pip", "--wheel"):
text = row.help
at = text.index(" bundle ")
help_body = n.paragraph("")
help_body += n.Text(text[: at + 1])
help_body += n.literal(text="bundle")
help_body += n.Text(text[at + 7 :])
else:
help_body = n.paragraph("", "", n.Text(content))
if row.choices is not None:
help_body += n.Text("; choice of: ")
first = True
for choice in row.choices:
if first:
first = False
else:
help_body += n.Text(", ")
help_body += n.literal(text=choice)
return help_body
@staticmethod
def _get_default(row):
default = row.default
name = row.names[0]
if name == "-p":
default_body = n.Text("the python executable virtualenv is installed into")
elif name == "--activators":
default_body = n.Text("comma separated list of activators supported")
elif name == "--creator":
default_body = n.paragraph("")
default_body += n.literal(text="builtin")
default_body += n.Text(" if exist, else ")
default_body += n.literal(text="venv")
else:
if default is None:
default_body = n.paragraph("", text="")
else:
default_body = n.literal(text=default if isinstance(default, str) else str(default))
return default_body
def register_target_option(self, target) -> None:
domain = self.env.get_domain("std")
self.state.document.note_explicit_target(target)
for key in target["ids"]:
domain.add_program_option(None, key, self.env.docname, key)
def literal_data(rawtext, app, type, slug, options):
"""Create a link to a BitBucket resource."""
of_class = type.split(".")
data = getattr(__import__(".".join(of_class[:-1]), fromlist=[of_class[-1]]), of_class[-1])
return [n.literal("", text=",".join(data))], []
__all__ = (
"CliTable",
"literal_data",
)

180
docs/user_guide.rst Normal file
View file

@ -0,0 +1,180 @@
User Guide
==========
Introduction
------------
Virtualenv has one basic command:
.. code-block:: console
virtualenv
this will create a python virtual environment of the same version as virtualenv is installed into under path
``venv``. The path where to generate the virtual environment can be changed via a positional argument being passed in,
see the :option:`dest` flag. The command line tool has quite a few of flags that modify the components behaviour, for a
full list make sure to check out :ref:`cli_flags`.
The tool works in two phases:
- **Phase 1** discovers a python interpreter to create a virtual environment from (by default this is the same python
as the one ``virtualenv`` is running from, however we can change this via the :option:`p` option).
- **Phase 2** creates a virtual environment at the specified destination (:option:`dest`), this can be broken down into
three further sub-steps:
- create a python that matches the target python interpreter from phase 1,
- install (bootstrap) seed packages (one or more of :pypi:`pip`, :pypi:`setuptools`, :pypi:`wheel`) in the created
virtual environment,
- install activation scripts into the binary directory of the virtual environment (these will allow end user to
*activate* the virtual environment from various shells).
The python in your new virtualenv is effectively isolated from the python that was used to create it.
Python discovery
----------------
The first thing we need to be able to create a virtual environment is a python interpreter. This will describe to the
tool what type of virtual environment you would like to create, think of it as: version, architecture, implementation.
``virtualenv`` being a python application has always at least one such available, the one ``virtualenv`` itself is
using it, and as such this is the default discovered element. This means that if you install ``virtualenv`` under
python ``3.8``, virtualenv will by default create virtual environments that are also of version ``3.8``.
Created python virtual environments are usually not self-contained. A complete python packaging is usually made up of
thousand of files, so it's not efficient to install the entire python again into a new folder. Instead virtual
environments are mere shells, that contain very little within itself, and borrow most from the system python (this is
what you installed, when you installed python itself). This does mean that if you upgrade your system python your
virtual environments *might* break, so watch out. The upside of this referring to the system python is that creating
virtual environments can be very fast.
Here we'll describe the builtin mechanism (note this can be extended though by plugins). The CLI flag :option:`p` or
:option:`python` allows you to specify a python specifier for what type of virtual environment you would like, the
format is either:
- a relative/absolute path to a Python interpreter,
- a specifier identifying the Python implementation, version, architecture in the following format:
.. code-block::
{python implementation name}{version}{architecture}
We have the following restrictions:
- the python implementation is all alphabetic characters (``python`` means any implementation, and if is missing it
defaults to ``python``),
- the version is a dot separated version number,
- the architecture is either ``-64`` or ``-32`` (missing means ``any``).
For example:
- ``python3.8.1`` means any python implementation having the version ``3.8.1``,
- ``3`` means any python implementation having the major version ``3``,
- ``cpython3`` means a ``CPython`` implementation havin the version ``3``,
- ``pypy2`` means a python interpreter with the ``PyPy`` implementation and major version ``2``.
Given the specifier ``virtualenv`` will apply the following strategy to discover/find the system executable:
- If we're on Windows look into the Windows registry, and check if we see any registered Python implementations that
match the specification. This is in line with expectation laid out inside
`PEP-514 <https://www.python.org/dev/peps/pep-0514/>`_
- Try to discover a matching python executable within the folders enumerated on the ``PATH`` environment variable.
In this case we'll try to find an executable that has a name roughly similar to the specification (for exact logic,
please see the implementation code).
.. warning::
As detailed above virtual environments usually just borrow things from the system Python, they don't actually contain
all the data from the system Python. The version of the python executable is hardcoded within the python exe itself.
Therefore if you upgrade your system Python, your virtual environment will still report the version before the
upgrade, even though now other than the executable all additional content (standard library, binary libs, etc) are
of the new version.
Baring any major incompatibilities (rarely the case) the virtual environment will continue working, but other than
the content embedded within the python executable it will behave like the upgraded version. If a such virtual
environment python is specified as the target python interpreter, we will create virtual environments that match the
new system Python version, not the version reported by the virtual environment.
Creators
--------
These are what actually setup the virtual environment, usually as a reference against the system python. virtualenv
at the moment has two types of virtual environments:
- ``venv`` - this delegates the creation process towards the ``venv`` module, as described in
`PEP 404 <https://www.python.org/dev/peps/pep-0405>`_. This is only available on Python interpreters having version
``3.4`` or later, and also has the downside that virtualenv **must** create a process to invoke that module (unless
virtualenv is installed in the system python), which can be an expensive operation (especially true on Windows).
- ``builtin`` - this means ``virtualenv`` is able to do the creation operation itself (by knowing exactly what files to
create and what system files needs to be referenced). The creator with name ``builtin`` is an alias on the first
creator that's of this type (we provide creators for various target environments, that all differ in actual create
operations, such as CPython 2 on Windows, PyPy2 on Windows, CPython3 on Posix, PyPy3 on Posix, and so on; for a full
list see :option:`creator`).
Seeders
-------
These will install for you some seed packages (one or more of the: :pypi:`pip`, :pypi:`setuptools`, :pypi:`wheel`) that
enables you to install additional python packages into the created virtual environment (by invoking pip). There are two
main seed mechanism available:
- ``pip`` - this method uses the bundled pip with virtualenv to install the seed packages (note, a new child process
needs to be created to do this).
- ``app-data`` - this method uses the user application data directory to create install images. These images are needed
to be created only once, and subsequent virtual environments can just link/copy those images into their pure python
library path (the ``site-packages`` folder). This allows all but the first virtual environment creation to be blazing
fast (a ``pip`` mechanism takes usually 98% of the virtualenv creation time, so by creating this install image that
we can just link into the virtual environments install directory we can achieve speedups of shaving the initial
1 minutes 10 seconds down to just 8 seconds in case of copy, or ``0.8`` seconds in case symlinks are available -
this is on Windows, Linux/macOS with symlinks this can be as low as ``100ms`` from 3+ seconds).
Activators
----------
These are activation scripts that will mangle with your shells settings to ensure that commands from within the python
virtual environment take priority over your system paths. For example if invoking ``pip`` from your shell returned the
system pythons pip before activation, once you do the activation this should refer to the virtual environments ``pip``.
Note, though that all we do is change priority; so if your virtual environments ``bin``/``Scripts`` folder does not
contain some executable, this will still resolve to the same executable it would have resolved before the activation.
For a list of shells we provide activators see :option:`activators`. The location of these is right alongside the python
executables ( usually ``Scripts`` folder on Windows, ``bin`` on POSIX), and are named as ``activate`` (and some
extension that's specific per activator; no extension is bash). You can invoke them, usually by source-ing (the source
command might vary by shell - e.g. bash is ``.``):
.. code-block:: console
source bin/activate
This is all it does; it's purely a convenience of prepending the virtual environments binary folder onto the ``PATH``
environment variable. Note you don't have to activate a virtual environment to use it. In this case though you would
need to type out the path to the executables, rather than relying on your shell to resolve them to your virtual
environment.
The ``activate`` script will also modify your shell prompt to indicate which environment is currently active. The script
also provisions a ``decativate`` command that will allow you to undo the operation:
.. code-block:: console
deactivate
.. note::
If using Powershell, the ``activate`` script is subject to the
`execution policies <http://technet.microsoft.com/en-us/library/dd347641.aspx>`_ on the system. By default Windows
7 and later, the system's execution policy is set to ``Restricted``, meaning no scripts like the ``activate`` script
are allowed to be executed.
However, that can't stop us from changing that slightly to allow it to be executed. You may relax the system
execution policy to allow running of local scripts without verifying the code signature using the following:
.. code-block:: powershell
Set-ExecutionPolicy RemoteSigned
Since the ``activate.ps1`` script is generated locally for each virtualenv, it is not considered a remote script and
can then be executed.
A longer explanation of this can be found within Allison Kaptur's 2013 blog post: `There's no magic: virtualenv
edition <https://www.recurse.com/blog/14-there-is-no-magic-virtualenv-edition>`_ explains how virtualenv uses bash and
Python and ``PATH`` and ``PYTHONHOME`` to isolate virtual environments' paths.

View file

@ -1,280 +0,0 @@
User Guide
==========
Usage
-----
Virtualenv has one basic command::
$ virtualenv ENV
Where ``ENV`` is a directory in which to place the new virtual environment. It has
a number of usual effects (modifiable by many :ref:`options`):
- :file:`ENV/lib/` and :file:`ENV/include/` are created, containing supporting
library files for a new virtualenv python. Packages installed in this
environment will live under :file:`ENV/lib/pythonX.X/site-packages/`.
- :file:`ENV/bin` is created, where executables live - noticeably a new
:command:`python`. Thus running a script with ``#! /path/to/ENV/bin/python``
would run that script under this virtualenv's python.
- The crucial packages pip_ and setuptools_ are installed, which allow other
packages to be easily installed to the environment. This associated pip
can be run from :file:`ENV/bin/pip`.
The python in your new virtualenv is effectively isolated from the python that
was used to create it.
.. _pip: https://pypi.org/project/pip
.. _setuptools: https://pypi.org/project/setuptools
.. _activate:
activate script
~~~~~~~~~~~~~~~
In a newly created virtualenv there will also be a :command:`activate` shell
script. For Windows systems, activation scripts are provided for
the Command Prompt and Powershell.
On Posix systems, this resides in :file:`ENV/bin/`, so you can run::
$ source /path/to/ENV/bin/activate
For some shells (e.g. the original Bourne Shell) you may need to use the
:command:`.` command, when :command:`source` does not exist. There are also
separate activate files for some other shells, like csh and fish.
:file:`bin/activate` should work for bash/zsh/dash.
This will change your ``$PATH`` so its first entry is the virtualenv's
``bin/`` directory. (You have to use ``source`` because it changes your
shell environment in-place.) This is all it does; it's purely a
convenience.
If you directly run a script or the python interpreter
from the virtualenv's ``bin/`` directory (e.g. ``path/to/ENV/bin/pip``
or ``/path/to/ENV/bin/python-script.py``) then ``sys.path`` will
automatically be set to use the Python libraries associated with the
virtualenv. But, unlike the activation scripts, the environment variables
``PATH`` and ``VIRTUAL_ENV`` will *not* be modified. This means that if
your Python script uses e.g. ``subprocess`` to run another Python script
(e.g. via a ``#!/usr/bin/env python`` shebang line) the second script
*may not be executed with the same Python binary as the first* nor have
the same libraries available to it. To avoid this happening your first
script will need to modify the environment variables in the same manner
as the activation scripts, before the second script is executed.
The ``activate`` script will also modify your shell prompt to indicate
which environment is currently active. To disable this behaviour, see
:envvar:`VIRTUAL_ENV_DISABLE_PROMPT`.
To undo these changes to your path (and prompt), just run::
$ deactivate
On Windows, the equivalent ``activate`` script is in the ``Scripts`` folder::
> \path\to\env\Scripts\activate
And type ``deactivate`` to undo the changes.
Based on your active shell (CMD.exe or Powershell.exe), Windows will use
either activate.bat or activate.ps1 (as appropriate) to activate the
virtual environment. If using Powershell, see the notes about code signing
below.
.. note::
If using Powershell, the ``activate`` script is subject to the
`execution policies`_ on the system. By default on Windows 7, the system's
execution policy is set to ``Restricted``, meaning no scripts like the
``activate`` script are allowed to be executed. But that can't stop us
from changing that slightly to allow it to be executed.
In order to use the script, you can relax your system's execution
policy to ``AllSigned``, meaning all scripts on the system must be
digitally signed to be executed. Since the virtualenv activation
script is signed by one of the authors (Jannis Leidel) this level of
the execution policy suffices. As an administrator run::
PS C:\> Set-ExecutionPolicy AllSigned
Then you'll be asked to trust the signer, when executing the script.
You will be prompted with the following::
PS C:\> virtualenv .\foo
New python executable in C:\foo\Scripts\python.exe
Installing setuptools................done.
Installing pip...................done.
PS C:\> .\foo\scripts\activate
Do you want to run software from this untrusted publisher?
File C:\foo\scripts\activate.ps1 is published by E=jannis@leidel.info,
CN=Jannis Leidel, L=Berlin, S=Berlin, C=DE, Description=581796-Gh7xfJxkxQSIO4E0
and is not trusted on your system. Only run scripts from trusted publishers.
[V] Never run [D] Do not run [R] Run once [A] Always run [?] Help
(default is "D"):A
(foo) PS C:\>
If you select ``[A] Always Run``, the certificate will be added to the
Trusted Publishers of your user account, and will be trusted in this
user's context henceforth. If you select ``[R] Run Once``, the script will
be run, but you will be prompted on a subsequent invocation. Advanced users
can add the signer's certificate to the Trusted Publishers of the Computer
account to apply to all users (though this technique is out of scope of this
document).
Alternatively, you may relax the system execution policy to allow running
of local scripts without verifying the code signature using the following::
PS C:\> Set-ExecutionPolicy RemoteSigned
Since the ``activate.ps1`` script is generated locally for each virtualenv,
it is not considered a remote script and can then be executed.
On xonsh, the equivalent ``activate`` script is called ``activate.xsh``, and
lives in either the ``bin/`` directory (on posix systems) or the ``Scripts\``
directory (on Windows). For example::
$ source /path/to/ENV/bin/activate.xsh
With xonsh, you may still run the ``deactivate`` command to undo the changes.
.. _`execution policies`: http://technet.microsoft.com/en-us/library/dd347641.aspx
Removing an Environment
~~~~~~~~~~~~~~~~~~~~~~~
Removing a virtual environment is simply done by deactivating it and deleting the
environment folder with all its contents::
(ENV)$ deactivate
$ rm -r /path/to/ENV
The :option:`--system-site-packages` Option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you build with ``virtualenv --system-site-packages ENV``, your virtual
environment will inherit packages from ``/usr/lib/python2.7/site-packages``
(or wherever your global site-packages directory is).
This can be used if you have control over the global site-packages directory,
and you want to depend on the packages there. If you want isolation from the
global system, do not use this flag.
If you need to change this option after creating a virtual environment, you can
add (to turn off) or remove (to turn on) the file ``no-global-site-packages.txt``
from ``lib/python3.7/`` or equivalent in the environments directory.
Windows Notes
~~~~~~~~~~~~~
Some paths within the virtualenv are slightly different on Windows: scripts and
executables on Windows go in ``ENV\Scripts\`` instead of ``ENV/bin/`` and
libraries go in ``ENV\Lib\`` rather than ``ENV/lib/``.
To create a virtualenv under a path with spaces in it on Windows, you'll need
the `win32api <https://github.com/mhammond/pywin32/>`_ library installed.
Using Virtualenv without ``bin/python``
---------------------------------------
Sometimes you can't or don't want to use the Python interpreter
created by the virtualenv. For instance, in a `mod_python
<http://www.modpython.org/>`_ or `mod_wsgi <http://www.modwsgi.org/>`_
environment, there is only one interpreter.
Luckily, it's easy. You must use the custom Python interpreter to
*install* libraries. But to *use* libraries, you just have to be sure
the path is correct. A script is available to correct the path. You
can setup the environment like::
activate_this = '/path/to/env/bin/activate_this.py'
exec(open(activate_this).read(), {'__file__': activate_this})
This will change ``sys.path`` and even change ``sys.prefix``, but also allow
you to use an existing interpreter. Items in your environment will show up
first on ``sys.path``, before global items. However, global items will
always be accessible (as if the :option:`--system-site-packages` flag had been
used in creating the environment, whether it was or not). Also, this cannot undo
the activation of other environments, or modules that have been imported.
You shouldn't try to, for instance, activate an environment before a web
request; you should activate *one* environment as early as possible, and not
do it again in that process.
Making Environments Relocatable
-------------------------------
**Note:** this option is somewhat experimental, and there are probably
caveats that have not yet been identified.
.. warning::
The ``--relocatable`` option currently has a number of issues,
and is not guaranteed to work in all circumstances. It is possible
that the option will be deprecated in a future version of ``virtualenv``.
Normally environments are tied to a specific path. That means that
you cannot move an environment around or copy it to another computer.
You can fix up an environment to make it relocatable with the
command::
$ virtualenv --relocatable ENV
This will make some of the files created by setuptools use relative paths,
and will change all the scripts to use ``activate_this.py`` instead of using
the location of the Python interpreter to select the environment.
**Note:** scripts which have been made relocatable will only work if
the virtualenv is activated, specifically the python executable from
the virtualenv must be the first one on the system PATH. Also note that
the activate scripts are not currently made relocatable by
``virtualenv --relocatable``.
**Note:** you must run this after you've installed *any* packages into
the environment. If you make an environment relocatable, then
install a new package, you must run ``virtualenv --relocatable``
again.
Also, this **does not make your packages cross-platform**. You can
move the directory around, but it can only be used on other similar
computers. Some known environmental differences that can cause
incompatibilities: a different version of Python, when one platform
uses UCS2 for its internal unicode representation and another uses
UCS4 (a compile-time option), obvious platform changes like Windows
vs. Linux, or Intel vs. ARM, and if you have libraries that bind to C
libraries on the system, if those C libraries are located somewhere
different (either different versions, or a different filesystem
layout).
If you use this flag to create an environment, currently, the
:option:`--system-site-packages` option will be implied.
The :option:`--extra-search-dir` option
---------------------------------------
This option allows you to provide your own versions of setuptools and/or
pip to use instead of the embedded versions that come with virtualenv.
To use this feature, pass one or more ``--extra-search-dir`` options to
virtualenv like this::
$ virtualenv --extra-search-dir=/path/to/distributions ENV
The ``/path/to/distributions`` path should point to a directory that contains
setuptools and/or pip wheels.
virtualenv will look for wheels in the specified directories, but will use
pip's standard algorithm for selecting the wheel to install, which looks for
the latest compatible wheel.
As well as the extra directories, the search order includes:
#. The ``virtualenv_support`` directory relative to virtualenv.py
#. The directory where virtualenv.py is located.
#. The current directory.

View file

@ -18,42 +18,10 @@ line-length = 120
[tool.towncrier]
package = "virtualenv"
package_dir = "" # we purposfully do not set this as src, forcing import from site-package that has version.py
filename = "docs/changes.rst"
directory = "docs/changelog"
template = "docs/changelog/template.jinja2"
title_format = "v{version} ({project_date})"
title_format = false
issue_format = "`#{issue} <https://github.com/pypa/virtualenv/issues/{issue}>`_"
underlines = ["-", "^"]
[[tool.towncrier.section]]
path = ""
[[tool.towncrier.type]]
directory = "bugfix"
name = "Bugfixes"
showcontent = true
[[tool.towncrier.type]]
directory = "feature"
name = "Features"
showcontent = true
[[tool.towncrier.type]]
directory = "deprecation"
name = "Deprecations (removal in next major release)"
showcontent = true
[[tool.towncrier.type]]
directory = "breaking"
name = "Backward incompatible changes"
showcontent = true
[[tool.towncrier.type]]
directory = "doc"
name = "Documentation"
showcontent = true
[[tool.towncrier.type]]
directory = "misc"
name = "Miscellaneous"
showcontent = true
template = "docs/changelog/template.jinja2"
# possible types, all default: feature, bugfix, doc, removal, misc

View file

@ -1,8 +0,0 @@
build:
image: latest
python:
version: 3.6
pip_install: true
extra_requirements:
- docs
formats: []

View file

@ -7,7 +7,7 @@ long_description_content_type = text/markdown
url = https://virtualenv.pypa.io/
author = Bernat Gabor
license = MIT
license_file = LICENSE.txt
license_file = LICENSE
platforms = any
classifiers =
Development Status :: 5 - Production/Stable
@ -24,6 +24,8 @@ classifiers =
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
Topic :: Software Development :: Libraries
Topic :: Software Development :: Testing
Topic :: Utilities
@ -75,15 +77,15 @@ virtualenv.create =
virtualenv.discovery =
builtin = virtualenv.discovery.builtin:Builtin
virtualenv.seed =
none = virtualenv.seed.none:NoneSeeder
pip = virtualenv.seed.embed.pip_invoke:PipInvoke
app-data = virtualenv.seed.via_app_data.via_app_data:FromAppData
[options.extras_require]
docs =
sphinx >= 2.0.0, < 3
towncrier >= 18.5.0
sphinx_rtd_theme >= 0.4.2, < 1
sphinx-argparse >= 0.2.5, <1
sphinx-rtd-theme >= 0.4.3, <1
towncrier >= 19.9.0rc1
testing =
packaging>=20.0;python_version>"3.4"
pytest >= 4.0.0, <6

View file

@ -7,17 +7,38 @@ import six
@six.add_metaclass(ABCMeta)
class Activator(object):
"""Generates an activate script for the virtual environment"""
def __init__(self, options):
"""Create a new activator generator.
:param options: the parsed options as defined within :meth:`add_parser_arguments`
"""
self.flag_prompt = options.prompt
@classmethod
def add_parser_arguments(cls, parser, interpreter):
"""add activator options"""
def supports(cls, interpreter):
"""Check if the activation script is supported in the given interpreter.
:param interpreter: the interpreter we need to support
:return: ``True`` if supported, ``False`` otherwise
"""
return True
@classmethod
def supports(cls, interpreter):
return True
def add_parser_arguments(cls, parser, interpreter):
"""
Add CLI arguments for this activation script.
:param parser: the CLI parser
:param interpreter: the interpreter this virtual environment is based of
"""
@abstractmethod
def generate(self, creator):
"""Generate the activate script for the given creator.
:param creator: the creator (based of :class:`virtualenv.create.creator.Creator`) we used to create this \
virtual environment
"""
raise NotImplementedError

View file

@ -20,6 +20,10 @@ class VirtualEnvConfigParser(ArgumentParser):
kwargs["prog"] = "virtualenv"
super(VirtualEnvConfigParser, self).__init__(*args, **kwargs)
self._fixed = set()
self._elements = None
self._verbosity = None
self._options = None
self._interpreter = None
def _fix_defaults(self):
for action in self._actions:
@ -52,7 +56,7 @@ class VirtualEnvConfigParser(ArgumentParser):
class HelpFormatter(ArgumentDefaultsHelpFormatter):
def __init__(self, prog):
super(HelpFormatter, self).__init__(prog, max_help_position=37, width=240)
super(HelpFormatter, self).__init__(prog, max_help_position=35, width=240)
def _get_help_string(self, action):
# noinspection PyProtectedMember

View file

@ -28,7 +28,14 @@ DEBUG_SCRIPT = HERE / "debug.py"
@add_metaclass(ABCMeta)
class Creator(object):
"""A class that given a python Interpreter creates a virtual environment"""
def __init__(self, options, interpreter):
"""Construct a new virtual environment creator.
:param options: the CLI option as parsed from :meth:`add_parser_arguments`
:param interpreter: the interpreter to create virtual environment from
"""
self.interpreter = interpreter
self._debug = None
self.dest = Path(options.dest)
@ -47,8 +54,24 @@ class Creator(object):
("clear", self.clear),
]
@classmethod
def can_create(cls, interpreter):
"""Determine if we can create a virtual environment.
:param interpreter: the interpreter in question
:return: ``None`` if we can't create, any other object otherwise that will be forwarded to \
:meth:`add_parser_arguments`
"""
return True
@classmethod
def add_parser_arguments(cls, parser, interpreter, meta):
"""Add CLI arguments for the creator.
:param parser: the CLI parser
:param interpreter: the interpreter we're asked to create virtual environment for
:param meta: value as returned by :meth:`can_create`
"""
parser.add_argument(
"dest", help="directory to create virtualenv at", type=cls.validate_dest, default="venv", nargs="?",
)
@ -60,6 +83,11 @@ class Creator(object):
default=False,
)
@abstractmethod
def create(self):
"""Perform the virtual environment creation."""
raise NotImplementedError
@classmethod
def validate_dest(cls, raw_value):
"""No path separator in the path, valid chars and must be write-able"""
@ -132,15 +160,6 @@ class Creator(object):
self.create()
self.set_pyenv_cfg()
@abstractmethod
def create(self):
raise NotImplementedError
@classmethod
def can_create(cls, interpreter):
"""Default is that we can"""
return True
def set_pyenv_cfg(self):
self.pyenv_cfg.content = OrderedDict()
self.pyenv_cfg["home"] = self.interpreter.system_exec_prefix
@ -150,6 +169,9 @@ class Creator(object):
@property
def debug(self):
"""
:return: debug information about the virtual environment (only valid after :meth:`create` has run)
"""
if self._debug is None and self.exe is not None:
self._debug = get_env_debug_info(self.exe, self.debug_script())
return self._debug

View file

@ -15,7 +15,7 @@ from .py_spec import PythonSpec
class Builtin(Discover):
def __init__(self, options):
super(Builtin, self).__init__()
super(Builtin, self).__init__(options)
self.python_spec = options.python
@classmethod

View file

@ -7,20 +7,39 @@ import six
@six.add_metaclass(ABCMeta)
class Discover(object):
def __init__(self):
self._has_run = False
self._interpreter = None
"""Discover and provide the requested Python interpreter"""
@classmethod
def add_parser_arguments(cls, parser):
"""Add CLI arguments for this discovery mechanisms.
:param parser: the CLI parser
"""
raise NotImplementedError
# noinspection PyUnusedLocal
def __init__(self, options):
"""Create a new discovery mechanism.
:param options: the parsed options as defined within :meth:`add_parser_arguments`
"""
self._has_run = False
self._interpreter = None
@abstractmethod
def run(self):
"""Discovers an interpreter.
:return: the interpreter ready to use for virtual environment creation
"""
raise NotImplementedError
@property
def interpreter(self):
"""
:return: the interpreter as returned by :meth:`run`, cached
"""
if self._has_run is False:
self._interpreter = self.run()
self._has_run = True

View file

@ -23,41 +23,53 @@ def run_via_cli(args):
return session
# noinspection PyProtectedMember
def session_via_cli(args):
parser = build_parser(args)
parser.parse_args(args, namespace=parser._options)
creator, seeder, activators = tuple(e.create(parser._options) for e in parser._elements) # create types
session = Session(parser._verbosity, parser._interpreter, creator, seeder, activators)
return session
# noinspection PyProtectedMember
def build_parser(args=None):
parser = VirtualEnvConfigParser()
add_version_flag(parser)
options, verbosity = _do_report_setup(parser, args)
discover = get_discover(parser, args, options)
interpreter = discover.interpreter
parser._options, parser._verbosity = _do_report_setup(parser, args)
discover = get_discover(parser, args, parser._options)
parser._interpreter = interpreter = discover.interpreter
if interpreter is None:
raise RuntimeError("failed to find interpreter for {}".format(discover))
elements = [
parser._elements = [
CreatorSelector(interpreter, parser),
SeederSelector(interpreter, parser),
ActivationSelector(interpreter, parser),
]
parser.parse_known_args(args, namespace=options)
for element in elements:
element.handle_selected_arg_parse(options)
parser.parse_known_args(args, namespace=parser._options)
for element in parser._elements:
element.handle_selected_arg_parse(parser._options)
parser.enable_help()
parser.parse_args(args, namespace=options)
creator, seeder, activators = tuple(e.create(options) for e in elements) # create types
session = Session(verbosity, interpreter, creator, seeder, activators)
return session
return parser
def add_version_flag(parser):
import virtualenv
parser.add_argument(
"--version", action="version", version="%(prog)s {} from {}".format(__version__, virtualenv.__file__)
"--version",
action="version",
version="%(prog)s {} from {}".format(__version__, virtualenv.__file__),
help="display the version of the virtualenv package and it's location, then exit",
)
def _do_report_setup(parser, args):
level_map = ", ".join("{}:{}".format(c, logging.getLevelName(l)) for c, l in sorted(list(LEVELS.items())))
msg = "verbosity = verbose - quiet, default {}, count mapping = {{{}}}"
verbosity_group = parser.add_argument_group(msg.format(logging.getLevelName(LEVELS[3]), level_map))
level_map = ", ".join("{}={}".format(logging.getLevelName(l), c) for c, l in sorted(list(LEVELS.items())))
msg = "verbosity = verbose - quiet, default {}, mapping => {}"
verbosity_group = parser.add_argument_group(
title="verbosity", description=msg.format(logging.getLevelName(LEVELS[3]), level_map)
)
verbosity = verbosity_group.add_mutually_exclusive_group()
verbosity.add_argument("-v", "--verbose", action="count", dest="verbose", help="increase verbosity", default=2)
verbosity.add_argument("-q", "--quiet", action="count", dest="quiet", help="decrease verbosity", default=0)

View file

@ -13,11 +13,11 @@ class ActivationSelector(ComponentBuilder):
(k, v) for k, v in self.options("virtualenv.activate").items() if v.supports(interpreter)
)
super(ActivationSelector, self).__init__(interpreter, parser, "activators", possible)
self.parser.description = "options for activation scripts"
self.active = None
def add_selector_arg_parse(self, name, choices):
self.default = ",".join(choices)
self.parser.add_argument(
"--{}".format(name),
default=self.default,

View file

@ -30,7 +30,7 @@ class ComponentBuilder(PluginLoader):
self.name = name
self._impl_class = None
self.possible = possible
self.parser = parser.add_argument_group("{} options".format(name))
self.parser = parser.add_argument_group(title=name)
self.add_selector_arg_parse(name, list(self.possible))
@classmethod
@ -51,6 +51,7 @@ class ComponentBuilder(PluginLoader):
return selected
def populate_selected_argparse(self, selected):
self.parser.description = "options for {} {}".format(self.name, selected)
self._impl_class.add_parser_arguments(self.parser, self.interpreter)
def create(self, options):

View file

@ -40,17 +40,23 @@ class CreatorSelector(ComponentBuilder):
def add_selector_arg_parse(self, name, choices):
# prefer the built-in venv if present, otherwise fallback to first defined type
choices = sorted(choices, key=lambda a: 0 if a == "builtin" else 1)
default_value = self._get_default(choices)
self.parser.add_argument(
"--{}".format(name),
choices=choices,
default=next(iter(choices)),
default=default_value,
required=False,
help="create environment via{}".format(
"" if self.builtin_key is None else " (builtin = {})".format(self.builtin_key)
),
)
@staticmethod
def _get_default(choices):
return next(iter(choices))
def populate_selected_argparse(self, selected):
self.parser.description = "options for {} {}".format(self.name, selected)
self._impl_class.add_parser_arguments(self.parser, self.interpreter, self.key_to_meta[selected])
def create(self, options):

View file

@ -9,10 +9,12 @@ class Discovery(PluginLoader):
def get_discover(parser, args, options):
discover_types = Discovery.entry_points_for("virtualenv.discovery")
discovery_parser = parser.add_argument_group("target interpreter identifier")
discovery_parser = parser.add_argument_group(
title="discovery", description="discover and provide a target interpreter"
)
discovery_parser.add_argument(
"--discovery",
choices=list(discover_types.keys()),
choices=_get_default_discovery(discover_types),
default=next(i for i in discover_types.keys()),
required=False,
help="interpreter discovery method",
@ -23,3 +25,7 @@ def get_discover(parser, args, options):
options, _ = parser.parse_known_args(args, namespace=options)
discover = discover_class(options)
return discover
def _get_default_discovery(discover_types):
return list(discover_types.keys())

View file

@ -12,20 +12,19 @@ class SeederSelector(ComponentBuilder):
self.parser.add_argument(
"--{}".format(name),
choices=choices,
default="app-data",
default=self._get_default(),
required=False,
help="seed packages install method",
)
self.parser.add_argument(
"--without-pip",
help="if set forces the none seeder, used for compatibility with venv",
action="store_true",
dest="without_pip",
"--no-seed", "--without-pip", help="do not install seed packages", action="store_true", dest="no_seed",
)
@staticmethod
def _get_default():
return "app-data"
def handle_selected_arg_parse(self, options):
if options.without_pip is True:
setattr(options, self.name, "none")
return super(SeederSelector, self).handle_selected_arg_parse(options)
def create(self, options):

View file

@ -14,7 +14,7 @@ class BaseEmbed(Seeder):
packages = ["pip", "setuptools", "wheel"]
def __init__(self, options):
super(BaseEmbed, self).__init__(options, enabled=options.without_pip is False)
super(BaseEmbed, self).__init__(options, enabled=options.no_seed is False)
self.download = options.download
self.extra_search_dir = [i.resolve() for i in options.extra_search_dir if i.exists()]
@ -41,7 +41,7 @@ class BaseEmbed(Seeder):
"--download",
dest="download",
action="store_true",
help="download latest {} from PyPI".format("/".join(cls.packages)),
help="pass to enable download of the latest {} from PyPI".format("/".join(cls.packages)),
default=False,
)
group.add_argument(
@ -49,7 +49,7 @@ class BaseEmbed(Seeder):
"--never-download",
dest="download",
action="store_false",
help="download latest {} from PyPI".format("/".join(cls.packages)),
help="pass to disable download of the latest {} from PyPI".format("/".join(cls.packages)),
default=True,
)
parser.add_argument(
@ -57,7 +57,7 @@ class BaseEmbed(Seeder):
metavar="d",
type=Path,
nargs="+",
help="a location containing wheels candidates to install from",
help="a path containing wheels the seeder may also use beside bundled (can be set 1+ times)",
default=[],
)
for package in cls.packages:

View file

@ -21,6 +21,8 @@ class PipInvoke(BaseEmbed):
super(PipInvoke, self).__init__(options)
def run(self, creator):
if not self.enabled:
return
with self.get_pip_install_cmd(creator.exe, creator.interpreter.version_release_str) as cmd:
with pip_wheel_env_run(creator.interpreter.version_release_str) as env:
logging.debug("pip seed by running: %s", LogCmd(cmd, env))

View file

@ -1,15 +0,0 @@
from __future__ import absolute_import, unicode_literals
from virtualenv.seed.seeder import Seeder
class NoneSeeder(Seeder):
def __init__(self, options):
super(NoneSeeder, self).__init__(options, False)
@classmethod
def add_parser_arguments(cls, parser, interpreter):
pass
def run(self, creator):
pass

View file

@ -7,13 +7,32 @@ import six
@six.add_metaclass(ABCMeta)
class Seeder(object):
"""A seeder will install some seed packages into a virtual environment."""
# noinspection PyUnusedLocal
def __init__(self, options, enabled):
"""
:param options: the parsed options as defined within :meth:`add_parser_arguments`
:param enabled: a flag weather the seeder is enabled or not
"""
self.enabled = enabled
@classmethod
def add_parser_arguments(cls, parser, interpreter):
"""
Add CLI arguments for this seed mechanisms.
:param parser: the CLI parser
:param interpreter: the interpreter this virtual environment is based of
"""
raise NotImplementedError
@abstractmethod
def run(self, creator):
"""Perform the seed operation.
:param creator: the creator (based of :class:`virtualenv.create.creator.Creator`) we used to create this \
virtual environment
"""
raise NotImplementedError

View file

@ -30,11 +30,13 @@ class FromAppData(BaseEmbed):
"--clear-app-data",
dest="clear_app_data",
action="store_true",
help="clear the app data folder",
help="clear the app data folder of seed images ({})".format((default_data_dir() / "seed-v1").path),
default=False,
)
def run(self, creator):
if not self.enabled:
return
base_cache = self.app_data_dir / creator.interpreter.version_release_str
with self._get_seed_wheels(creator, base_cache) as name_to_whl:
pip_version = name_to_whl["pip"].stem.split("-")[1]

View file

@ -61,7 +61,7 @@ def zipapp(zipapp_build_env, tmp_path_factory):
@pytest.fixture(scope="session")
def zipapp_test_env(tmp_path_factory):
base_path = tmp_path_factory.mktemp("zipapp-test")
session = run_via_cli(["-v", "--activators", "", "--seed", "none", str(base_path / "env")])
session = run_via_cli(["-v", "--activators", "", "--without-pip", str(base_path / "env")])
yield session.creator.exe
shutil.rmtree(str(base_path))
@ -82,6 +82,6 @@ def test_zipapp_help(call_zipapp, capsys):
assert not err
@pytest.mark.parametrize("seeder", ["none", "app-data", "pip"])
@pytest.mark.parametrize("seeder", ["app-data", "pip"])
def test_zipapp_create(call_zipapp, seeder):
call_zipapp("--seeder", seeder)

View file

@ -207,7 +207,7 @@ def raise_on_non_source_class():
@pytest.fixture(scope="session")
def activation_python(tmp_path_factory, special_char_name, current_fastest):
dest = os.path.join(six.ensure_text(str(tmp_path_factory.mktemp("activation-tester-env"))), special_char_name)
session = run_via_cli(["--seed", "none", dest, "--prompt", special_char_name, "--creator", current_fastest, "-vv"])
session = run_via_cli(["--without-pip", dest, "--prompt", special_char_name, "--creator", current_fastest, "-vv"])
pydoc_test = session.creator.purelib / "pydoc_test.py"
pydoc_test.write_text('"""This is pydoc_test.py"""')
yield session

View file

@ -220,7 +220,7 @@ def test_create_clear_resets(tmp_path, creator, clear, caplog):
if creator == "venv" and clear is False:
pytest.skip("venv without clear might fail")
marker = tmp_path / "magic"
cmd = [str(tmp_path), "--seeder", "none", "--creator", creator, "-vvv"]
cmd = [str(tmp_path), "--seeder", "app-data", "--without-pip", "--creator", creator, "-vvv"]
run_via_cli(cmd)
marker.write_text("") # if we a marker file this should be gone on a clear run, remain otherwise
@ -233,7 +233,7 @@ def test_create_clear_resets(tmp_path, creator, clear, caplog):
@pytest.mark.parametrize("creator", CURRENT_CREATORS)
@pytest.mark.parametrize("prompt", [None, "magic"])
def test_prompt_set(tmp_path, creator, prompt):
cmd = [str(tmp_path), "--seeder", "none", "--creator", creator]
cmd = [str(tmp_path), "--seeder", "app-data", "--without-pip", "--creator", creator]
if prompt is not None:
cmd.extend(["--prompt", "magic"])
@ -268,8 +268,7 @@ def test_cross_major(cross_python, coverage_env, tmp_path, current_fastest):
"-p",
six.ensure_text(cross_python.executable),
six.ensure_text(str(tmp_path)),
"--seeder",
"none",
"--no-seed",
"--activators",
"",
"--creator",

15
tox.ini
View file

@ -10,7 +10,9 @@ envlist =
py27,
pypy,
pypy3,
coverage
coverage,
readme,
docs,
isolated_build = true
skip_missing_interpreters = true
@ -73,7 +75,7 @@ basepython = python3.8
description = build documentation
extras = docs
commands =
sphinx-build -d "{envtmpdir}/doctree" -W docs "{toxworkdir}/docs_out" --color -bhtml {posargs}
sphinx-build -d "{envtmpdir}/doctree" docs "{toxworkdir}/docs_out" --color -b html {posargs}
python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html"))'
[testenv:readme]
@ -101,7 +103,7 @@ description = format the code base to adhere to our styles, and complain about w
basepython = python3.8
passenv = *
deps = {[testenv]deps}
pre-commit >= 1.17.0, <2
pre-commit >= 2.0.0, <3
skip_install = True
commands =
pre-commit run --all-files --show-diff-on-failure
@ -114,7 +116,7 @@ force_grid_wrap = 0
line_length = 120
known_standard_library = ConfigParser
known_first_party = virtualenv
known_third_party = _subprocess,appdirs,coverage,filelock,git,packaging,pytest,setuptools,six
known_third_party = _subprocess,appdirs,coverage,docutils,filelock,git,packaging,pytest,setuptools,six,sphinx,sphinx_rtd_theme,sphinxarg
[flake8]
max-complexity = 22
@ -128,6 +130,9 @@ max-line-length = 120
description = generate a DEV environment
extras = testing, docs
usedevelop = True
deps =
{[testenv]deps}
docutils-stubs >= 0.0.21, <1
commands =
python -m pip list --format=columns
python -c 'import sys; print(sys.executable)'
@ -140,7 +145,7 @@ passenv = *
deps =
{[testenv]deps}
gitpython >= 2.1.10, < 3
towncrier >= 18.5.0
towncrier >= 19.9.0rc1
packaging >= 17.1
changedir = {toxinidir}/tasks
commands =