|
12 | 12 | import operator
|
13 | 13 | import os
|
14 | 14 | import shutil
|
| 15 | +import site |
15 | 16 | from optparse import SUPPRESS_HELP
|
16 | 17 |
|
17 | 18 | from pip._vendor import pkg_resources
|
|
31 | 32 | from pip._internal.operations.check import check_install_conflicts
|
32 | 33 | from pip._internal.req import RequirementSet, install_given_reqs
|
33 | 34 | from pip._internal.req.req_tracker import RequirementTracker
|
34 |
| -from pip._internal.utils.filesystem import check_path_owner |
| 35 | +from pip._internal.utils.filesystem import check_path_owner, test_writable_dir |
35 | 36 | from pip._internal.utils.misc import (
|
36 | 37 | ensure_dir,
|
37 | 38 | get_installed_version,
|
@@ -292,17 +293,16 @@ def run(self, options, args):
|
292 | 293 |
|
293 | 294 | options.src_dir = os.path.abspath(options.src_dir)
|
294 | 295 | install_options = options.install_options or []
|
| 296 | + |
| 297 | + options.use_user_site = decide_user_install( |
| 298 | + options.use_user_site, |
| 299 | + prefix_path=options.prefix_path, |
| 300 | + target_dir=options.target_dir, |
| 301 | + root_path=options.root_path, |
| 302 | + isolated_mode=options.isolated_mode, |
| 303 | + ) |
| 304 | + |
295 | 305 | if options.use_user_site:
|
296 |
| - if options.prefix_path: |
297 |
| - raise CommandError( |
298 |
| - "Can not combine '--user' and '--prefix' as they imply " |
299 |
| - "different installation locations" |
300 |
| - ) |
301 |
| - if virtualenv_no_global(): |
302 |
| - raise InstallationError( |
303 |
| - "Can not perform a '--user' install. User site-packages " |
304 |
| - "are not visible in this virtualenv." |
305 |
| - ) |
306 | 306 | install_options.append('--user')
|
307 | 307 | install_options.append('--prefix=')
|
308 | 308 |
|
@@ -594,6 +594,70 @@ def get_lib_location_guesses(*args, **kwargs):
|
594 | 594 | return [scheme['purelib'], scheme['platlib']]
|
595 | 595 |
|
596 | 596 |
|
| 597 | +def site_packages_writable(**kwargs): |
| 598 | + return all( |
| 599 | + test_writable_dir(d) for d in set(get_lib_location_guesses(**kwargs)) |
| 600 | + ) |
| 601 | + |
| 602 | + |
| 603 | +def decide_user_install( |
| 604 | + use_user_site, # type: Optional[bool] |
| 605 | + prefix_path=None, # type: Optional[str] |
| 606 | + target_dir=None, # type: Optional[str] |
| 607 | + root_path=None, # type: Optional[str] |
| 608 | + isolated_mode=False, # type: bool |
| 609 | +): |
| 610 | + # type: (...) -> bool |
| 611 | + """Determine whether to do a user install based on the input options. |
| 612 | +
|
| 613 | + If use_user_site is False, no additional checks are done. |
| 614 | + If use_user_site is True, it is checked for compatibility with other |
| 615 | + options. |
| 616 | + If use_user_site is None, the default behaviour depends on the environment, |
| 617 | + which is provided by the other arguments. |
| 618 | + """ |
| 619 | + if use_user_site is False: |
| 620 | + logger.debug("Non-user install by explicit request") |
| 621 | + return False |
| 622 | + |
| 623 | + if use_user_site is True: |
| 624 | + if prefix_path: |
| 625 | + raise CommandError( |
| 626 | + "Can not combine '--user' and '--prefix' as they imply " |
| 627 | + "different installation locations" |
| 628 | + ) |
| 629 | + if virtualenv_no_global(): |
| 630 | + raise InstallationError( |
| 631 | + "Can not perform a '--user' install. User site-packages " |
| 632 | + "are not visible in this virtualenv." |
| 633 | + ) |
| 634 | + logger.debug("User install by explicit request") |
| 635 | + return True |
| 636 | + |
| 637 | + # If we are here, user installs have not been explicitly requested/avoided |
| 638 | + assert use_user_site is None |
| 639 | + |
| 640 | + # user install incompatible with --prefix/--target |
| 641 | + if prefix_path or target_dir: |
| 642 | + logger.debug("Non-user install due to --prefix or --target option") |
| 643 | + return False |
| 644 | + |
| 645 | + # If user installs are not enabled, choose a non-user install |
| 646 | + if not site.ENABLE_USER_SITE: |
| 647 | + logger.debug("Non-user install because user site-packages disabled") |
| 648 | + return False |
| 649 | + |
| 650 | + # If we have permission for a non-user install, do that, |
| 651 | + # otherwise do a user install. |
| 652 | + if site_packages_writable(root=root_path, isolated=isolated_mode): |
| 653 | + logger.debug("Non-user install because site-packages writeable") |
| 654 | + return False |
| 655 | + |
| 656 | + logger.info("Defaulting to user installation because normal site-packages " |
| 657 | + "is not writeable") |
| 658 | + return True |
| 659 | + |
| 660 | + |
597 | 661 | def create_env_error_message(error, show_traceback, using_user_site):
|
598 | 662 | """Format an error message for an EnvironmentError
|
599 | 663 |
|
|
0 commit comments