From 21df86f1974cfd73c3c8a83dfceb7c02694d13d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 11 Apr 2020 18:40:55 +0200 Subject: [PATCH 1/6] Add REQUESTED support to install_wheel --- src/pip/_internal/operations/install/wheel.py | 10 ++++++++++ tests/unit/test_wheel.py | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py index 36877ca5e76..d2ff3544f7c 100644 --- a/src/pip/_internal/operations/install/wheel.py +++ b/src/pip/_internal/operations/install/wheel.py @@ -339,6 +339,7 @@ def install_unpacked_wheel( pycompile=True, # type: bool warn_script_location=True, # type: bool direct_url=None, # type: Optional[DirectUrl] + requested=False, # type: bool ): # type: (...) -> None """Install a wheel. @@ -645,6 +646,13 @@ def _generate_file(path, **kwargs): direct_url_file.write(direct_url.to_json().encode("utf-8")) generated.append(direct_url_path) + # Record the REQUESTED file + if requested: + requested_path = os.path.join(dest_info_dir, 'REQUESTED') + with open(requested_path, "w"): + pass + generated.append(requested_path) + # Record details of all files installed record_path = os.path.join(dest_info_dir, 'RECORD') with open(record_path, **csv_io_kwargs('r')) as record_file: @@ -671,6 +679,7 @@ def install_wheel( warn_script_location=True, # type: bool _temp_dir_for_testing=None, # type: Optional[str] direct_url=None, # type: Optional[DirectUrl] + requested=False, # type: bool ): # type: (...) -> None with TempDirectory( @@ -686,4 +695,5 @@ def install_wheel( pycompile=pycompile, warn_script_location=warn_script_location, direct_url=direct_url, + requested=requested, ) diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py index 2834b18f087..388e8f01b7a 100644 --- a/tests/unit/test_wheel.py +++ b/tests/unit/test_wheel.py @@ -301,6 +301,19 @@ def test_std_install_with_custom_umask(self, data, tmpdir, finally: os.umask(prev_umask) + def test_std_install_requested(self, data, tmpdir): + self.prep(data, tmpdir) + wheel.install_wheel( + self.name, + self.wheelpath, + scheme=self.scheme, + req_description=str(self.req), + requested=True, + ) + self.assert_installed() + requested_path = os.path.join(self.dest_dist_info, 'REQUESTED') + assert os.path.isfile(requested_path) + def test_std_install_with_direct_url(self, data, tmpdir): """Test that install_wheel creates direct_url.json metadata when provided with a direct_url argument. Also test that the RECORDS From c9a445762c403926196f80ec814b9e4c4a4021ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sun, 12 Apr 2020 12:40:38 +0200 Subject: [PATCH 2/6] Mark top level requirements as REQUESTED --- news/7811.feature | 2 + src/pip/_internal/req/req_install.py | 1 + tests/functional/test_install_requested.py | 94 ++++++++++++++++++++++ tests/unit/test_wheel.py | 2 +- 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 news/7811.feature create mode 100644 tests/functional/test_install_requested.py diff --git a/news/7811.feature b/news/7811.feature new file mode 100644 index 00000000000..b06aaa5bf27 --- /dev/null +++ b/news/7811.feature @@ -0,0 +1,2 @@ +Generate PEP 376 REQUESTED metadata for top level requirements installed +by pip. diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 3b28209b1bd..48ec9442114 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -809,6 +809,7 @@ def install( pycompile=pycompile, warn_script_location=warn_script_location, direct_url=direct_url, + requested=self.is_direct, ) self.install_succeeded = True return diff --git a/tests/functional/test_install_requested.py b/tests/functional/test_install_requested.py new file mode 100644 index 00000000000..6caec988eb5 --- /dev/null +++ b/tests/functional/test_install_requested.py @@ -0,0 +1,94 @@ +def _assert_requested_present(script, result, name, version): + dist_info = script.site_packages / name + "-" + version + ".dist-info" + requested = dist_info / "REQUESTED" + assert dist_info in result.files_created + assert requested in result.files_created + + +def _assert_requested_absent(script, result, name, version): + dist_info = script.site_packages / name + "-" + version + ".dist-info" + requested = dist_info / "REQUESTED" + assert dist_info in result.files_created + assert requested not in result.files_created + + +def test_install_requested_basic(script, data, with_wheel): + result = script.pip( + "install", "--no-index", "-f", data.find_links, "require_simple" + ) + _assert_requested_present(script, result, "require_simple", "1.0") + # dependency is not REQUESTED + _assert_requested_absent(script, result, "simple", "3.0") + + +def test_install_requested_requirements(script, data, with_wheel): + script.scratch_path.joinpath("requirements.txt").write_text( + "require_simple\n" + ) + result = script.pip( + "install", + "--no-index", + "-f", + data.find_links, + "-r", + script.scratch_path / "requirements.txt", + ) + _assert_requested_present(script, result, "require_simple", "1.0") + _assert_requested_absent(script, result, "simple", "3.0") + + +def test_install_requested_dep_in_requirements(script, data, with_wheel): + script.scratch_path.joinpath("requirements.txt").write_text( + "require_simple\nsimple<3\n" + ) + result = script.pip( + "install", + "--no-index", + "-f", + data.find_links, + "-r", + script.scratch_path / "requirements.txt", + ) + _assert_requested_present(script, result, "require_simple", "1.0") + # simple must have REQUESTED because it is in requirements.txt + _assert_requested_present(script, result, "simple", "2.0") + + +def test_install_requested_reqs_and_constraints(script, data, with_wheel): + script.scratch_path.joinpath("requirements.txt").write_text( + "require_simple\n" + ) + script.scratch_path.joinpath("constraints.txt").write_text("simple<3\n") + result = script.pip( + "install", + "--no-index", + "-f", + data.find_links, + "-r", + script.scratch_path / "requirements.txt", + "-c", + script.scratch_path / "constraints.txt", + ) + _assert_requested_present(script, result, "require_simple", "1.0") + # simple must not have REQUESTED because it is merely a constraint + _assert_requested_absent(script, result, "simple", "2.0") + + +def test_install_requested_in_reqs_and_constraints(script, data, with_wheel): + script.scratch_path.joinpath("requirements.txt").write_text( + "require_simple\nsimple\n" + ) + script.scratch_path.joinpath("constraints.txt").write_text("simple<3\n") + result = script.pip( + "install", + "--no-index", + "-f", + data.find_links, + "-r", + script.scratch_path / "requirements.txt", + "-c", + script.scratch_path / "constraints.txt", + ) + _assert_requested_present(script, result, "require_simple", "1.0") + # simple must have REQUESTED because it is in requirements.txt + _assert_requested_present(script, result, "simple", "2.0") diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py index 388e8f01b7a..12eafaa0388 100644 --- a/tests/unit/test_wheel.py +++ b/tests/unit/test_wheel.py @@ -310,7 +310,7 @@ def test_std_install_requested(self, data, tmpdir): req_description=str(self.req), requested=True, ) - self.assert_installed() + self.assert_installed(0o644) requested_path = os.path.join(self.dest_dist_info, 'REQUESTED') assert os.path.isfile(requested_path) From a226f65cf5eb25af75513e34e5a55723bf228bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sun, 12 Apr 2020 12:43:50 +0200 Subject: [PATCH 3/6] A constraint is not a top level requirement --- src/pip/_internal/cli/req_command.py | 2 +- src/pip/_internal/req/req_set.py | 9 ++++++--- src/pip/_internal/resolution/legacy/resolver.py | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pip/_internal/cli/req_command.py b/src/pip/_internal/cli/req_command.py index 1bc59c175c7..5b9aa2c3c76 100644 --- a/src/pip/_internal/cli/req_command.py +++ b/src/pip/_internal/cli/req_command.py @@ -310,7 +310,7 @@ def get_requirements( parsed_req, isolated=options.isolated_mode, ) - req_to_add.is_direct = True + req_to_add.is_direct = False requirements.append(req_to_add) for req in args: diff --git a/src/pip/_internal/req/req_set.py b/src/pip/_internal/req/req_set.py index d64bb78a327..6a779b06fbf 100644 --- a/src/pip/_internal/req/req_set.py +++ b/src/pip/_internal/req/req_set.py @@ -107,9 +107,8 @@ def add_requirement( ) # This next bit is really a sanity check. - assert install_req.is_direct == (parent_req_name is None), ( - "a direct req shouldn't have a parent and also, " - "a non direct req should have a parent" + assert not install_req.is_direct or parent_req_name is None, ( + "a direct req shouldn't have a parent" ) # Unnamed requirements are scanned again and the requirement won't be @@ -165,6 +164,10 @@ def add_requirement( # If we're now installing a constraint, mark the existing # object for real installation. existing_req.constraint = False + # If we're now installing a top level requirement, mark the existing + # object as top level. + if install_req.is_direct: + existing_req.is_direct = True existing_req.extras = tuple(sorted( set(existing_req.extras) | set(install_req.extras) )) diff --git a/src/pip/_internal/resolution/legacy/resolver.py b/src/pip/_internal/resolution/legacy/resolver.py index 6eafc56eaa8..afda2380203 100644 --- a/src/pip/_internal/resolution/legacy/resolver.py +++ b/src/pip/_internal/resolution/legacy/resolver.py @@ -195,7 +195,7 @@ def _is_upgrade_allowed(self, req): return True else: assert self.upgrade_strategy == "only-if-needed" - return req.is_direct + return req.is_direct or req.constraint def _set_req_to_reinstall(self, req): # type: (InstallRequirement) -> None From b9a19f6be072ba0398aaa2cce23052296a002b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Mon, 13 Apr 2020 11:09:05 +0200 Subject: [PATCH 4/6] Rename is_direct to user_supplied --- src/pip/_internal/cli/req_command.py | 8 ++++---- src/pip/_internal/req/req_install.py | 7 +++++-- src/pip/_internal/req/req_set.py | 6 +++--- src/pip/_internal/resolution/legacy/resolver.py | 4 ++-- src/pip/_internal/resolution/resolvelib/resolver.py | 2 +- tests/unit/test_req.py | 10 +++++----- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/pip/_internal/cli/req_command.py b/src/pip/_internal/cli/req_command.py index 5b9aa2c3c76..204ea4545c3 100644 --- a/src/pip/_internal/cli/req_command.py +++ b/src/pip/_internal/cli/req_command.py @@ -310,7 +310,7 @@ def get_requirements( parsed_req, isolated=options.isolated_mode, ) - req_to_add.is_direct = False + req_to_add.user_supplied = False requirements.append(req_to_add) for req in args: @@ -318,7 +318,7 @@ def get_requirements( req, None, isolated=options.isolated_mode, use_pep517=options.use_pep517, ) - req_to_add.is_direct = True + req_to_add.user_supplied = True requirements.append(req_to_add) for req in options.editables: @@ -327,7 +327,7 @@ def get_requirements( isolated=options.isolated_mode, use_pep517=options.use_pep517, ) - req_to_add.is_direct = True + req_to_add.user_supplied = True requirements.append(req_to_add) # NOTE: options.require_hashes may be set if --require-hashes is True @@ -340,7 +340,7 @@ def get_requirements( isolated=options.isolated_mode, use_pep517=options.use_pep517 ) - req_to_add.is_direct = True + req_to_add.user_supplied = True requirements.append(req_to_add) # If any requirement has hash options, enable hash checking. diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 48ec9442114..eb8735ba555 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -171,7 +171,10 @@ def __init__( self.hash_options = hash_options if hash_options else {} # Set to True after successful preparation of this requirement self.prepared = False - self.is_direct = False + # User supplied requirement are explicitly requested for installation + # by the user via CLI arguments or requirements files, as opposed to, + # e.g. dependencies, extras or constraints. + self.user_supplied = False # Set by the legacy resolver when the requirement has been downloaded # TODO: This introduces a strong coupling between the resolver and the @@ -809,7 +812,7 @@ def install( pycompile=pycompile, warn_script_location=warn_script_location, direct_url=direct_url, - requested=self.is_direct, + requested=self.user_supplied, ) self.install_succeeded = True return diff --git a/src/pip/_internal/req/req_set.py b/src/pip/_internal/req/req_set.py index 6a779b06fbf..78b065cf4cf 100644 --- a/src/pip/_internal/req/req_set.py +++ b/src/pip/_internal/req/req_set.py @@ -107,7 +107,7 @@ def add_requirement( ) # This next bit is really a sanity check. - assert not install_req.is_direct or parent_req_name is None, ( + assert not install_req.user_supplied or parent_req_name is None, ( "a direct req shouldn't have a parent" ) @@ -166,8 +166,8 @@ def add_requirement( existing_req.constraint = False # If we're now installing a top level requirement, mark the existing # object as top level. - if install_req.is_direct: - existing_req.is_direct = True + if install_req.user_supplied: + existing_req.user_supplied = True existing_req.extras = tuple(sorted( set(existing_req.extras) | set(install_req.extras) )) diff --git a/src/pip/_internal/resolution/legacy/resolver.py b/src/pip/_internal/resolution/legacy/resolver.py index afda2380203..7fdff1bc4f3 100644 --- a/src/pip/_internal/resolution/legacy/resolver.py +++ b/src/pip/_internal/resolution/legacy/resolver.py @@ -195,7 +195,7 @@ def _is_upgrade_allowed(self, req): return True else: assert self.upgrade_strategy == "only-if-needed" - return req.is_direct or req.constraint + return req.user_supplied or req.constraint def _set_req_to_reinstall(self, req): # type: (InstallRequirement) -> None @@ -419,7 +419,7 @@ def add_req(subreq, extras_requested): # 'unnamed' requirements will get added here # 'unnamed' requirements can only come from being directly # provided by the user. - assert req_to_install.is_direct + assert req_to_install.user_supplied requirement_set.add_requirement( req_to_install, parent_req_name=None, ) diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py index 0e55357b824..bd474b7021d 100644 --- a/src/pip/_internal/resolution/resolvelib/resolver.py +++ b/src/pip/_internal/resolution/resolvelib/resolver.py @@ -118,7 +118,7 @@ def resolve(self, root_reqs, check_supported_wheels): else: constraints[name] = req.specifier else: - if req.is_direct and req.name: + if req.user_supplied and req.name: user_requested.add(canonicalize_name(req.name)) requirements.append( self.factory.make_requirement_from_install_req(req) diff --git a/tests/unit/test_req.py b/tests/unit/test_req.py index 2da0b62dbfc..1aee7fcdf0a 100644 --- a/tests/unit/test_req.py +++ b/tests/unit/test_req.py @@ -56,7 +56,7 @@ def get_processed_req_from_line(line, fname='file', lineno=1): parsed_req = handle_requirement_line(parsed_line) assert parsed_req is not None req = install_req_from_parsed_requirement(parsed_req) - req.is_direct = True + req.user_supplied = True return req @@ -109,7 +109,7 @@ def test_no_reuse_existing_build_dir(self, data): pass reqset = RequirementSet() req = install_req_from_line('simple') - req.is_direct = True + req.user_supplied = True reqset.add_requirement(req) finder = make_test_finder(find_links=[data.find_links]) with self._basic_resolver(finder) as resolver: @@ -133,7 +133,7 @@ def test_environment_marker_extras(self, data): req = install_req_from_editable( data.packages.joinpath("LocalEnvironMarker") ) - req.is_direct = True + req.user_supplied = True reqset.add_requirement(req) finder = make_test_finder(find_links=[data.find_links]) with self._basic_resolver(finder) as resolver: @@ -626,10 +626,10 @@ def test_exclusive_environment_markers(): """Make sure RequirementSet accepts several excluding env markers""" eq36 = install_req_from_line( "Django>=1.6.10,<1.7 ; python_version == '3.6'") - eq36.is_direct = True + eq36.user_supplied = True ne36 = install_req_from_line( "Django>=1.6.10,<1.8 ; python_version != '3.6'") - ne36.is_direct = True + ne36.user_supplied = True req_set = RequirementSet() req_set.add_requirement(eq36) From aa0c1674987651fac32a810275a0866e5e3a891c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 16 May 2020 11:38:57 +0200 Subject: [PATCH 5/6] Make sure user_supplied is propagated where needed --- src/pip/_internal/cli/req_command.py | 10 +++++----- src/pip/_internal/req/constructors.py | 20 +++++++++++++++---- src/pip/_internal/req/req_install.py | 5 +++-- .../resolution/resolvelib/candidates.py | 3 +++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/pip/_internal/cli/req_command.py b/src/pip/_internal/cli/req_command.py index 204ea4545c3..a215d459314 100644 --- a/src/pip/_internal/cli/req_command.py +++ b/src/pip/_internal/cli/req_command.py @@ -309,25 +309,25 @@ def get_requirements( req_to_add = install_req_from_parsed_requirement( parsed_req, isolated=options.isolated_mode, + user_supplied=False, ) - req_to_add.user_supplied = False requirements.append(req_to_add) for req in args: req_to_add = install_req_from_line( req, None, isolated=options.isolated_mode, use_pep517=options.use_pep517, + user_supplied=True, ) - req_to_add.user_supplied = True requirements.append(req_to_add) for req in options.editables: req_to_add = install_req_from_editable( req, + user_supplied=True, isolated=options.isolated_mode, use_pep517=options.use_pep517, ) - req_to_add.user_supplied = True requirements.append(req_to_add) # NOTE: options.require_hashes may be set if --require-hashes is True @@ -338,9 +338,9 @@ def get_requirements( req_to_add = install_req_from_parsed_requirement( parsed_req, isolated=options.isolated_mode, - use_pep517=options.use_pep517 + use_pep517=options.use_pep517, + user_supplied=True, ) - req_to_add.user_supplied = True requirements.append(req_to_add) # If any requirement has hash options, enable hash checking. diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 7ca3370110f..46b1daa902e 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -219,7 +219,8 @@ def install_req_from_editable( use_pep517=None, # type: Optional[bool] isolated=False, # type: bool options=None, # type: Optional[Dict[str, Any]] - constraint=False # type: bool + constraint=False, # type: bool + user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement @@ -228,6 +229,7 @@ def install_req_from_editable( return InstallRequirement( parts.requirement, comes_from=comes_from, + user_supplied=user_supplied, editable=True, link=parts.link, constraint=constraint, @@ -382,6 +384,7 @@ def install_req_from_line( options=None, # type: Optional[Dict[str, Any]] constraint=False, # type: bool line_source=None, # type: Optional[str] + user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement """Creates an InstallRequirement from a name, which might be a @@ -400,6 +403,7 @@ def install_req_from_line( hash_options=options.get("hashes", {}) if options else {}, constraint=constraint, extras=parts.extras, + user_supplied=user_supplied, ) @@ -407,7 +411,8 @@ def install_req_from_req_string( req_string, # type: str comes_from=None, # type: Optional[InstallRequirement] isolated=False, # type: bool - use_pep517=None # type: Optional[bool] + use_pep517=None, # type: Optional[bool] + user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement try: @@ -429,14 +434,19 @@ def install_req_from_req_string( ) return InstallRequirement( - req, comes_from, isolated=isolated, use_pep517=use_pep517 + req, + comes_from, + isolated=isolated, + use_pep517=use_pep517, + user_supplied=user_supplied, ) def install_req_from_parsed_requirement( parsed_req, # type: ParsedRequirement isolated=False, # type: bool - use_pep517=None # type: Optional[bool] + use_pep517=None, # type: Optional[bool] + user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement if parsed_req.is_editable: @@ -446,6 +456,7 @@ def install_req_from_parsed_requirement( use_pep517=use_pep517, constraint=parsed_req.constraint, isolated=isolated, + user_supplied=user_supplied, ) else: @@ -457,5 +468,6 @@ def install_req_from_parsed_requirement( options=parsed_req.options, constraint=parsed_req.constraint, line_source=parsed_req.line_source, + user_supplied=user_supplied, ) return req diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index eb8735ba555..83de2cd135d 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -110,7 +110,8 @@ def __init__( global_options=None, # type: Optional[List[str]] hash_options=None, # type: Optional[Dict[str, List[str]]] constraint=False, # type: bool - extras=() # type: Iterable[str] + extras=(), # type: Iterable[str] + user_supplied=False, # type: bool ): # type: (...) -> None assert req is None or isinstance(req, Requirement), req @@ -174,7 +175,7 @@ def __init__( # User supplied requirement are explicitly requested for installation # by the user via CLI arguments or requirements files, as opposed to, # e.g. dependencies, extras or constraints. - self.user_supplied = False + self.user_supplied = user_supplied # Set by the legacy resolver when the requirement has been downloaded # TODO: This introduces a strong coupling between the resolver and the diff --git a/src/pip/_internal/resolution/resolvelib/candidates.py b/src/pip/_internal/resolution/resolvelib/candidates.py index 695b42a96cc..9229b1aa61d 100644 --- a/src/pip/_internal/resolution/resolvelib/candidates.py +++ b/src/pip/_internal/resolution/resolvelib/candidates.py @@ -47,6 +47,7 @@ def make_install_req_from_link(link, template): line = link.url ireq = install_req_from_line( line, + user_supplied=template.user_supplied, comes_from=template.comes_from, use_pep517=template.use_pep517, isolated=template.isolated, @@ -68,6 +69,7 @@ def make_install_req_from_editable(link, template): assert template.editable, "template not editable" return install_req_from_editable( link.url, + user_supplied=template.user_supplied, comes_from=template.comes_from, use_pep517=template.use_pep517, isolated=template.isolated, @@ -91,6 +93,7 @@ def make_install_req_from_dist(dist, template): line = "{}=={}".format(project_name, dist.parsed_version) ireq = install_req_from_line( line, + user_supplied=template.user_supplied, comes_from=template.comes_from, use_pep517=template.use_pep517, isolated=template.isolated, From 21bf4f51f8c78b2a6850364e66c3faf91a165e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 16 May 2020 12:45:43 +0200 Subject: [PATCH 6/6] Uniform use of user supplied vocabulary --- news/7811.feature | 2 +- src/pip/_internal/req/req_set.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/news/7811.feature b/news/7811.feature index b06aaa5bf27..0b471405a9c 100644 --- a/news/7811.feature +++ b/news/7811.feature @@ -1,2 +1,2 @@ -Generate PEP 376 REQUESTED metadata for top level requirements installed +Generate PEP 376 REQUESTED metadata for user supplied requirements installed by pip. diff --git a/src/pip/_internal/req/req_set.py b/src/pip/_internal/req/req_set.py index 78b065cf4cf..ab4b6f849b4 100644 --- a/src/pip/_internal/req/req_set.py +++ b/src/pip/_internal/req/req_set.py @@ -108,7 +108,7 @@ def add_requirement( # This next bit is really a sanity check. assert not install_req.user_supplied or parent_req_name is None, ( - "a direct req shouldn't have a parent" + "a user supplied req shouldn't have a parent" ) # Unnamed requirements are scanned again and the requirement won't be @@ -164,8 +164,8 @@ def add_requirement( # If we're now installing a constraint, mark the existing # object for real installation. existing_req.constraint = False - # If we're now installing a top level requirement, mark the existing - # object as top level. + # If we're now installing a user supplied requirement, + # mark the existing object as such. if install_req.user_supplied: existing_req.user_supplied = True existing_req.extras = tuple(sorted(