55from subprocess import check_call # nosec
66
77from . import click
8- from .exceptions import IncompatibleRequirements , UnsupportedConstraint
8+ from ._compat import DEV_PKGS , stdlib_pkgs
9+ from .exceptions import IncompatibleRequirements
910from .utils import (
1011 flat_map ,
1112 format_requirement ,
1213 get_hashes_from_ireq ,
14+ is_url_requirement ,
1315 key_from_ireq ,
1416 key_from_req ,
1517)
1618
17- from piptools ._compat import DEV_PKGS , stdlib_pkgs
18-
1919PACKAGES_TO_IGNORE = (
2020 ["-markerlib" , "pip" , "pip-tools" , "pip-review" , "pkg-resources" ]
2121 + list (stdlib_pkgs )
@@ -77,14 +77,10 @@ def merge(requirements, ignore_conflicts):
7777 by_key = {}
7878
7979 for ireq in requirements :
80- if ireq .link is not None and ireq .link .url .startswith ('file:' ) and not ireq .editable :
81- msg = (
82- "pip-compile does not support file URLs as packages, unless they are "
83- "editable. Perhaps add -e option?"
84- )
85- raise UnsupportedConstraint (msg , ireq )
86-
87- key = ireq .link or key_from_req (ireq .req )
80+ # Limitation: URL requirements are merged by precise string match, so
81+ # "file:///example.zip#egg=example", "file:///example.zip", and
82+ # "example==1.0" will not merge with each other
83+ key = key_from_ireq (ireq )
8884
8985 if not ignore_conflicts :
9086 existing_ireq = by_key .get (key )
@@ -96,16 +92,35 @@ def merge(requirements, ignore_conflicts):
9692
9793 # TODO: Always pick the largest specifier in case of a conflict
9894 by_key [key ] = ireq
99-
10095 return by_key .values ()
10196
10297
98+ def diff_key_from_ireq (ireq ):
99+ """
100+ Calculate a key for comparing a compiled requirement with installed modules.
101+ For URL requirements, only provide a useful key if the url includes
102+ #egg=name==version, which will set ireq.req.name and ireq.specifier.
103+ Otherwise return ireq.link so the key will not match and the package will
104+ reinstall. Reinstall is necessary to ensure that packages will reinstall
105+ if the URL is changed but the version is not.
106+ """
107+ if is_url_requirement (ireq ):
108+ if (
109+ ireq .req
110+ and (getattr (ireq .req , "key" , None ) or getattr (ireq .req , "name" , None ))
111+ and ireq .specifier
112+ ):
113+ return key_from_ireq (ireq )
114+ return str (ireq .link )
115+ return key_from_ireq (ireq )
116+
117+
103118def diff (compiled_requirements , installed_dists ):
104119 """
105120 Calculate which packages should be installed or uninstalled, given a set
106121 of compiled requirements and a list of currently installed modules.
107122 """
108- requirements_lut = {r . link or key_from_req ( r . req ): r for r in compiled_requirements }
123+ requirements_lut = {diff_key_from_ireq ( r ): r for r in compiled_requirements }
109124
110125 satisfied = set () # holds keys
111126 to_install = set () # holds InstallRequirement objects
0 commit comments