diff --git a/docs/html/reference/pip_install.rst b/docs/html/reference/pip_install.rst index 81e315ebaa2..fec56e1c4d6 100644 --- a/docs/html/reference/pip_install.rst +++ b/docs/html/reference/pip_install.rst @@ -66,8 +66,8 @@ for the name and project version (this is in theory slightly less reliable than using the ``egg_info`` command, but avoids downloading and processing unnecessary numbers of files). -Any URL may use the ``#egg=name`` syntax (see :ref:`VCS Support`) to -explicitly state the project name. +The :pep:`508` requirement syntax can be used to explicitly state the project +name (see :ref:`VCS Support`). Satisfying Requirements ----------------------- @@ -396,12 +396,13 @@ the :ref:`--editable ` option) or not. (referencing a specific commit) if and only if the install is done using the editable option. -The "project name" component of the URL suffix ``egg=`` -is used by pip in its dependency logic to identify the project prior -to pip downloading and analyzing the metadata. For projects -where ``setup.py`` is not in the root of project, the "subdirectory" component -is used. The value of the "subdirectory" component should be a path starting -from the root of the project to where ``setup.py`` is located. +The "project name" of a requirement specified by URL can be explicitly set +with the :pep:`508` requirement syntax. This value is used by pip in its +dependency logic to identify the project prior to pip downloading and analyzing +the metadata. For projects where ``setup.py`` is not in the root of project, +the "subdirectory" component is used. The value of the "subdirectory" component +should be a path starting from the root of the project to where ``setup.py`` +is located. If your repository layout is:: @@ -418,13 +419,13 @@ Then, to install from this repository, the syntax would be: .. code-block:: shell - python -m pip install -e "vcs+protocol://repo_url/#egg=pkg&subdirectory=pkg_dir" + python -m pip install -e "pkg @ vcs+protocol://repo_url/#subdirectory=pkg_dir" .. tab:: Windows .. code-block:: shell - py -m pip install -e "vcs+protocol://repo_url/#egg=pkg&subdirectory=pkg_dir" + py -m pip install -e "pkg @ vcs+protocol://repo_url/#subdirectory=pkg_dir" Git @@ -441,17 +442,17 @@ pip currently supports cloning over ``git``, ``git+http``, ``git+https``, Here are the supported forms:: - [-e] git+http://git.example.com/MyProject#egg=MyProject - [-e] git+https://git.example.com/MyProject#egg=MyProject - [-e] git+ssh://git.example.com/MyProject#egg=MyProject - [-e] git+file:///home/user/projects/MyProject#egg=MyProject + [-e] MyProject@git+http://git.example.com/MyProject + [-e] MyProject@git+https://git.example.com/MyProject + [-e] MyProject@git+ssh://git.example.com/MyProject + [-e] MyProject@git+file:///home/user/projects/MyProject Passing a branch name, a commit hash, a tag name or a git ref is possible like so:: - [-e] git+https://git.example.com/MyProject.git@master#egg=MyProject - [-e] git+https://git.example.com/MyProject.git@v1.0#egg=MyProject - [-e] git+https://git.example.com/MyProject.git@da39a3ee5e6b4b0d3255bfef95601890afd80709#egg=MyProject - [-e] git+https://git.example.com/MyProject.git@refs/pull/123/head#egg=MyProject + [-e] MyProject@git+https://git.example.com/MyProject.git@master + [-e] MyProject@git+https://git.example.com/MyProject.git@v1.0 + [-e] MyProject@git+https://git.example.com/MyProject.git@da39a3ee5e6b4b0d3255bfef95601890afd80709 + [-e] MyProject@git+https://git.example.com/MyProject.git@refs/pull/123/head When passing a commit hash, specifying a full hash is preferable to a partial hash because a full hash allows pip to operate more efficiently (e.g. by @@ -467,18 +468,18 @@ The supported schemes are: ``hg+file``, ``hg+http``, ``hg+https``, Here are the supported forms:: - [-e] hg+http://hg.myproject.org/MyProject#egg=MyProject - [-e] hg+https://hg.myproject.org/MyProject#egg=MyProject - [-e] hg+ssh://hg.myproject.org/MyProject#egg=MyProject - [-e] hg+file:///home/user/projects/MyProject#egg=MyProject + [-e] MyProject@hg+http://hg.myproject.org/MyProject + [-e] MyProject@hg+https://hg.myproject.org/MyProject + [-e] MyProject@hg+ssh://hg.myproject.org/MyProject + [-e] MyProject@hg+file:///home/user/projects/MyProject You can also specify a revision number, a revision hash, a tag name or a local branch name like so:: - [-e] hg+http://hg.example.com/MyProject@da39a3ee5e6b#egg=MyProject - [-e] hg+http://hg.example.com/MyProject@2019#egg=MyProject - [-e] hg+http://hg.example.com/MyProject@v1.0#egg=MyProject - [-e] hg+http://hg.example.com/MyProject@special_feature#egg=MyProject + [-e] MyProject@hg+http://hg.example.com/MyProject@da39a3ee5e6b + [-e] MyProject@hg+http://hg.example.com/MyProject@2019 + [-e] MyProject@hg+http://hg.example.com/MyProject@v1.0 + [-e] MyProject@hg+http://hg.example.com/MyProject@special_feature Subversion ^^^^^^^^^^ @@ -487,14 +488,14 @@ pip supports the URL schemes ``svn``, ``svn+svn``, ``svn+http``, ``svn+https``, Here are some of the supported forms:: - [-e] svn+https://svn.example.com/MyProject#egg=MyProject - [-e] svn+ssh://svn.example.com/MyProject#egg=MyProject - [-e] svn+ssh://user@svn.example.com/MyProject#egg=MyProject + [-e] MyProject@svn+https://svn.example.com/MyProject + [-e] MyProject@svn+ssh://svn.example.com/MyProject + [-e] MyProject@svn+ssh://user@svn.example.com/MyProject You can also give specific revisions to an SVN URL, like so:: - [-e] svn+svn://svn.example.com/svn/MyProject#egg=MyProject - [-e] svn+http://svn.example.com/svn/MyProject/trunk@2019#egg=MyProject + [-e] MyProject@svn+svn://svn.example.com/svn/MyProject + [-e] MyProject@svn+http://svn.example.com/svn/MyProject/trunk@2019 which will check out revision 2019. ``@{20080101}`` would also check out the revision from 2008-01-01. You can only check out specific @@ -508,16 +509,16 @@ pip supports Bazaar using the ``bzr+http``, ``bzr+https``, ``bzr+ssh``, Here are the supported forms:: - [-e] bzr+http://bzr.example.com/MyProject/trunk#egg=MyProject - [-e] bzr+sftp://user@example.com/MyProject/trunk#egg=MyProject - [-e] bzr+ssh://user@example.com/MyProject/trunk#egg=MyProject - [-e] bzr+ftp://user@example.com/MyProject/trunk#egg=MyProject - [-e] bzr+lp:MyProject#egg=MyProject + [-e] MyProject@bzr+http://bzr.example.com/MyProject/trunk + [-e] MyProject@bzr+sftp://user@example.com/MyProject/trunk + [-e] MyProject@bzr+ssh://user@example.com/MyProject/trunk + [-e] MyProject@bzr+ftp://user@example.com/MyProject/trunk + [-e] MyProject@bzr+lp:MyProject Tags or revisions can be installed like so:: - [-e] bzr+https://bzr.example.com/MyProject/trunk@2019#egg=MyProject - [-e] bzr+http://bzr.example.com/MyProject/trunk@v1.0#egg=MyProject + [-e] MyProject@bzr+https://bzr.example.com/MyProject/trunk@2019 + [-e] MyProject@bzr+http://bzr.example.com/MyProject/trunk@v1.0 Using Environment Variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -527,8 +528,8 @@ makes it possible to reference private repositories without having to store access tokens in the requirements file. For example, a private git repository allowing Basic Auth for authentication can be refenced like this:: - [-e] git+http://${AUTH_USER}:${AUTH_PASSWORD}@git.example.com/MyProject#egg=MyProject - [-e] git+https://${AUTH_USER}:${AUTH_PASSWORD}@git.example.com/MyProject#egg=MyProject + [-e] MyProject@git+http://${AUTH_USER}:${AUTH_PASSWORD}@git.example.com/MyProject + [-e] MyProject@git+https://${AUTH_USER}:${AUTH_PASSWORD}@git.example.com/MyProject .. note:: @@ -827,14 +828,14 @@ You can install local projects or VCS projects in "editable" mode: .. code-block:: shell python -m pip install -e path/to/SomeProject - python -m pip install -e git+http://repo/my_project.git#egg=SomeProject + python -m pip install -e SomeProject@git+http://repo/my_project.git .. tab:: Windows .. code-block:: shell py -m pip install -e path/to/SomeProject - py -m pip install -e git+http://repo/my_project.git#egg=SomeProject + py -m pip install -e SomeProject@git+http://repo/my_project.git (See the :ref:`VCS Support` section above for more information on VCS-related syntax.) @@ -956,7 +957,7 @@ Examples py -m pip install SomePackage # latest version py -m pip install SomePackage==1.0.4 # specific version - py -m pip install 'SomePackage>=1.0.4' # minimum version + py -m pip install "SomePackage>=1.0.4" # minimum version #. Install a list of requirements specified in a file. See the :ref:`Requirements files `. @@ -1027,21 +1028,21 @@ Examples .. code-block:: shell - python -m pip install -e git+https://git.repo/some_pkg.git#egg=SomePackage # from git - python -m pip install -e hg+https://hg.repo/some_pkg.git#egg=SomePackage # from mercurial - python -m pip install -e svn+svn://svn.repo/some_pkg/trunk/#egg=SomePackage # from svn - python -m pip install -e git+https://git.repo/some_pkg.git@feature#egg=SomePackage # from 'feature' branch - python -m pip install -e "git+https://git.repo/some_repo.git#egg=subdir&subdirectory=subdir_path" # install a python package from a repo subdirectory + python -m pip install -e SomePackage@git+https://git.repo/some_pkg.git # from git + python -m pip install -e SomePackage@hg+https://hg.repo/some_pkg # from mercurial + python -m pip install -e SomePackage@svn+svn://svn.repo/some_pkg/trunk/ # from svn + python -m pip install -e SomePackage@git+https://git.repo/some_pkg.git@feature # from 'feature' branch + python -m pip install -e SomePackage@git+https://git.repo/some_repo.git#subdirectory=subdir_path # install a python package from a repo subdirectory .. tab:: Windows .. code-block:: shell - py -m pip install -e git+https://git.repo/some_pkg.git#egg=SomePackage # from git - py -m pip install -e hg+https://hg.repo/some_pkg.git#egg=SomePackage # from mercurial - py -m pip install -e svn+svn://svn.repo/some_pkg/trunk/#egg=SomePackage # from svn - py -m pip install -e git+https://git.repo/some_pkg.git@feature#egg=SomePackage # from 'feature' branch - py -m pip install -e "git+https://git.repo/some_repo.git#egg=subdir&subdirectory=subdir_path" # install a python package from a repo subdirectory + py -m pip install -e SomePackage@git+https://git.repo/some_pkg.git # from git + py -m pip install -e SomePackage@hg+https://hg.repo/some_pkg # from mercurial + py -m pip install -e SomePackage@svn+svn://svn.repo/some_pkg/trunk/ # from svn + py -m pip install -e SomePackage@git+https://git.repo/some_pkg.git@feature # from 'feature' branch + py -m pip install -e SomePackage@git+https://git.repo/some_repo.git#subdirectory=subdir_path # install a python package from a repo subdirectory #. Install a package with `setuptools extras`_. diff --git a/docs/html/user_guide.rst b/docs/html/user_guide.rst index 92887885baf..32369673c31 100644 --- a/docs/html/user_guide.rst +++ b/docs/html/user_guide.rst @@ -226,7 +226,7 @@ In practice, there are 4 common uses of Requirements files: ``sometag``. You'd reference it in your requirements file with a line like so:: - git+https://myvcs.com/some_dependency@sometag#egg=SomeDependency + SomeDependency @ git+https://myvcs.com/some_dependency@sometag If ``SomeDependency`` was previously a top-level requirement in your requirements file, then **replace** that line with the new line. If diff --git a/news/1289.feature.rst b/news/1289.feature.rst new file mode 100644 index 00000000000..00a0d3a7ec3 --- /dev/null +++ b/news/1289.feature.rst @@ -0,0 +1 @@ +Add support to install a PEP 508 URL-specified requirement as editable. diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index cfb1951b6b8..7b13f96996d 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -70,11 +70,24 @@ def parse_editable(editable_req): - a requirement name - an URL - extras - - editable options Accepted requirements: - svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir - .[some_extra] + - svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir + - local_path[some_extra] + - Foobar[extra] @ svn+http://blahblah@rev#subdirectory=subdir ; markers """ + try: + req = Requirement(editable_req) + except InvalidRequirement: + pass + else: + if req.url and "://" in req.url: + # Join the marker back into the name part. This will be parsed out + # later into a Requirement again. + if req.marker: + name = f"{req.name} ; {req.marker}" + else: + name = req.name + return (name, req.url, req.extras) url = editable_req diff --git a/src/pip/_internal/resolution/resolvelib/candidates.py b/src/pip/_internal/resolution/resolvelib/candidates.py index 91662b326b7..e781551c13e 100644 --- a/src/pip/_internal/resolution/resolvelib/candidates.py +++ b/src/pip/_internal/resolution/resolvelib/candidates.py @@ -67,8 +67,12 @@ def make_install_req_from_link(link, template): def make_install_req_from_editable(link, template): # type: (Link, InstallRequirement) -> InstallRequirement assert template.editable, "template not editable" + if template.name: + req_string = f"{template.name} @ {link.url}" + else: + req_string = link.url return install_req_from_editable( - link.url, + req_string, user_supplied=template.user_supplied, comes_from=template.comes_from, use_pep517=template.use_pep517, diff --git a/tests/unit/test_req.py b/tests/unit/test_req.py index c7be5fe1bac..b8808037401 100644 --- a/tests/unit/test_req.py +++ b/tests/unit/test_req.py @@ -606,6 +606,23 @@ def test_parse_editable_vcs_extras(): ) +@pytest.mark.parametrize( + "req_str, expected", + [ + ( + 'foo[extra] @ svn+http://foo ; os_name == "nt"', + ('foo ; os_name == "nt"', 'svn+http://foo', {'extra'}), + ), + ( + 'foo @ svn+http://foo', + ('foo', 'svn+http://foo', set()), + ), + ], +) +def test_parse_editable_pep508(req_str, expected): + assert parse_editable(req_str) == expected + + @patch('pip._internal.req.req_install.os.path.abspath') @patch('pip._internal.req.req_install.os.path.exists') @patch('pip._internal.req.req_install.os.path.isdir')