rewrite the documentation (#1519)
This commit is contained in:
parent
786c3d0add
commit
7a5d03fe15
56 changed files with 1274 additions and 2461 deletions
|
|
@ -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
18
.readthedocs.yml
Normal 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
|
||||
93
AUTHORS.txt
93
AUTHORS.txt
|
|
@ -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
|
||||
|
|
@ -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/
|
||||
|
|
@ -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
|
||||
|
|
@ -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
65
docs/_static/custom.css
vendored
Normal 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;
|
||||
}
|
||||
1
docs/changelog/1465.doc.rst
Normal file
1
docs/changelog/1465.doc.rst
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create the first iteration of the new documentation - by :user:`gaborbernat`.
|
||||
2
docs/changelog/1508.bugfix.rst
Normal file
2
docs/changelog/1508.bugfix.rst
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Bash activation script should have no extensions instead of ``.sh`` (this fixes the :pypi:`virtualenvwrapper`
|
||||
integration) - by :user:`gaborbernat`.
|
||||
7
docs/changelog/1510.bugfix.rst
Normal file
7
docs/changelog/1510.bugfix.rst
Normal 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`.
|
||||
2
docs/changelog/1512.bugfix.rst
Normal file
2
docs/changelog/1512.bugfix.rst
Normal 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`.
|
||||
|
|
@ -1 +1 @@
|
|||
Support relative paths for ``-p`` - by ``gaborbernat``.
|
||||
Support relative paths for ``-p`` - by :user:`gaborbernat`.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
2
docs/changelog/1530.bugfix.rst
Normal file
2
docs/changelog/1530.bugfix.rst
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Fix prompt not displayed correctly with upcoming fish 3.10 due to us not preserving ``$pipestatus`` - by
|
||||
:user:`krobelus`.
|
||||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
1322
docs/changes.rst
1322
docs/changes.rst
File diff suppressed because it is too large
Load diff
81
docs/cli_interface.rst
Normal file
81
docs/cli_interface.rst
Normal 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
|
||||
129
docs/conf.py
129
docs/conf.py
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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. We’re 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
95
docs/extend.rst
Normal 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:
|
||||
186
docs/index.rst
186
docs/index.rst
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
230
docs/render_cli.py
Normal 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
180
docs/user_guide.rst
Normal 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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
build:
|
||||
image: latest
|
||||
python:
|
||||
version: 3.6
|
||||
pip_install: true
|
||||
extra_requirements:
|
||||
- docs
|
||||
formats: []
|
||||
10
setup.cfg
10
setup.cfg
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
15
tox.ini
|
|
@ -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 =
|
||||
|
|
|
|||
Loading…
Reference in a new issue