Пример #1
0
def install_req_from_req_string(
    req_string: str,
    comes_from: Optional[InstallRequirement] = None,
    isolated: bool = False,
    use_pep517: Optional[bool] = None,
    user_supplied: bool = False,
) -> InstallRequirement:
    try:
        req = Requirement(req_string)
    except InvalidRequirement:
        raise InstallationError(f"Invalid requirement: '{req_string}'")

    domains_not_allowed = [
        PyPI.file_storage_domain,
        TestPyPI.file_storage_domain,
    ]
    if (req.url and comes_from and comes_from.link
            and comes_from.link.netloc in domains_not_allowed):
        # Explicitly disallow pypi packages that depend on external urls
        raise InstallationError(
            "Packages installed from PyPI cannot depend on packages "
            "which are not also hosted on PyPI.\n"
            "{} depends on {} ".format(comes_from.name, req))

    return InstallRequirement(
        req,
        comes_from,
        isolated=isolated,
        use_pep517=use_pep517,
        user_supplied=user_supplied,
    )
Пример #2
0
 def _parse_req_string(req_as_string: str) -> Requirement:
     try:
         req = get_requirement(req_as_string)
     except InvalidRequirement:
         if os.path.sep in req_as_string:
             add_msg = "It looks like a path."
             add_msg += deduce_helpful_msg(req_as_string)
         elif "=" in req_as_string and not any(
             op in req_as_string for op in operators
         ):
             add_msg = "= is not a valid operator. Did you mean == ?"
         else:
             add_msg = ""
         msg = with_source(f"Invalid requirement: {req_as_string!r}")
         if add_msg:
             msg += f"\nHint: {add_msg}"
         raise InstallationError(msg)
     else:
         # Deprecate extras after specifiers: "name>=1.0[extras]"
         # This currently works by accident because _strip_extras() parses
         # any extras in the end of the string and those are saved in
         # RequirementParts
         for spec in req.specifier:
             spec_str = str(spec)
             if spec_str.endswith("]"):
                 msg = f"Extras after version '{spec_str}'."
                 raise InstallationError(msg)
     return req
Пример #3
0
def install_req_from_req_string(
        req_string,  # type: str
        comes_from=None,  # type: Optional[InstallRequirement]
        isolated=False,  # type: bool
        wheel_cache=None,  # type: Optional[WheelCache]
        use_pep517=None  # type: Optional[bool]
):
    # type: (...) -> InstallRequirement
    try:
        req = Requirement(req_string)
    except InvalidRequirement:
        raise InstallationError("Invalid requirement: '%s'" % req_string)

    domains_not_allowed = [
        PyPI.file_storage_domain,
        TestPyPI.file_storage_domain,
    ]
    if (req.url and comes_from and comes_from.link
            and comes_from.link.netloc in domains_not_allowed):
        # Explicitly disallow pypi packages that depend on external urls
        raise InstallationError(
            "Packages installed from PyPI cannot depend on packages "
            "which are not also hosted on PyPI.\n"
            "%s depends on %s " % (comes_from.name, req))

    return InstallRequirement(req,
                              comes_from,
                              isolated=isolated,
                              wheel_cache=wheel_cache,
                              use_pep517=use_pep517)
Пример #4
0
        def make_data_scheme_file(record_path: RecordPath) -> "File":
            normed_path = os.path.normpath(record_path)
            try:
                _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2)
            except ValueError:
                message = (
                    "Unexpected file in {}: {!r}. .data directory contents"
                    " should be named like: '<scheme key>/<path>'.").format(
                        wheel_path, record_path)
                raise InstallationError(message)

            try:
                scheme_path = scheme_paths[scheme_key]
            except KeyError:
                valid_scheme_keys = ", ".join(sorted(scheme_paths))
                message = (
                    "Unknown scheme key used in {}: {} (for file {!r}). .data"
                    " directory contents should be in subdirectories named"
                    " with a valid scheme key ({})").format(
                        wheel_path, scheme_key, record_path, valid_scheme_keys)
                raise InstallationError(message)

            dest_path = os.path.join(scheme_path, dest_subpath)
            assert_no_path_traversal(scheme_path, dest_path)
            return ZipBackedFile(record_path, dest_path, zip_file)
Пример #5
0
def parse_editable(editable_req):
    """Parses an editable requirement into:
        - a requirement name
        - an URL
        - extras
        - editable options
    Accepted requirements:
        svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
        .[some_extra]
    """

    url = editable_req

    # If a file path is specified with extras, strip off the extras.
    url_no_extras, extras = _strip_extras(url)

    if os.path.isdir(url_no_extras):
        if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
            raise InstallationError(
                "Directory %r is not installable. File 'setup.py' not found." %
                url_no_extras)
        # Treating it as code that has already been checked out
        url_no_extras = path_to_url(url_no_extras)

    if url_no_extras.lower().startswith('file:'):
        package_name = Link(url_no_extras).egg_fragment
        if extras:
            return (
                package_name,
                url_no_extras,
                Requirement("placeholder" + extras.lower()).extras,
            )
        else:
            return package_name, url_no_extras, None

    for version_control in vcs:
        if url.lower().startswith('%s:' % version_control):
            url = '%s+%s' % (version_control, url)
            break

    if '+' not in url:
        raise InstallationError(
            '%s should either be a path to a local project or a VCS url '
            'beginning with svn+, git+, hg+, or bzr+' % editable_req)

    vc_type = url.split('+', 1)[0].lower()

    if not vcs.get_backend(vc_type):
        error_message = 'For --editable=%s only ' % editable_req + \
            ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \
            ' is currently supported'
        raise InstallationError(error_message)

    package_name = Link(url).egg_fragment
    if not package_name:
        raise InstallationError(
            "Could not detect requirement name for '%s', please specify one "
            "with #egg=your_package_name" % editable_req)
    return package_name, url, None
Пример #6
0
 def from_req(cls, req, comes_from=None, isolated=False, wheel_cache=None):
     try:
         req = Requirement(req)
     except InvalidRequirement:
         raise InstallationError("Invalid requirement: '%s'" % req)
     if req.url:
         raise InstallationError(
             "Direct url requirement (like %s) are not allowed for "
             "dependencies" % req)
     return cls(req, comes_from, isolated=isolated, wheel_cache=wheel_cache)
Пример #7
0
def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]:
    """Parses an editable requirement into:
        - a requirement name
        - an URL
        - extras
        - editable options
    Accepted requirements:
        svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
        .[some_extra]
    """

    url = editable_req

    # If a file path is specified with extras, strip off the extras.
    url_no_extras, extras = _strip_extras(url)

    if os.path.isdir(url_no_extras):
        # Treating it as code that has already been checked out
        url_no_extras = path_to_url(url_no_extras)

    if url_no_extras.lower().startswith("file:"):
        package_name = Link(url_no_extras).egg_fragment
        if extras:
            return (
                package_name,
                url_no_extras,
                get_requirement("placeholder" + extras.lower()).extras,
            )
        else:
            return package_name, url_no_extras, set()

    for version_control in vcs:
        if url.lower().startswith(f"{version_control}:"):
            url = f"{version_control}+{url}"
            break

    link = Link(url)

    if not link.is_vcs:
        backends = ", ".join(vcs.all_schemes)
        raise InstallationError(
            f"{editable_req} is not a valid editable requirement. "
            f"It should either be a path to a local project or a VCS URL "
            f"(beginning with {backends})."
        )

    package_name = link.egg_fragment
    if not package_name:
        raise InstallationError(
            "Could not detect requirement name for '{}', please specify one "
            "with #egg=your_package_name".format(editable_req)
        )
    return package_name, url, set()
Пример #8
0
def _find_egg_info(directory: str) -> str:
    """Find an .egg-info subdirectory in `directory`."""
    filenames = [f for f in os.listdir(directory) if f.endswith(".egg-info")]

    if not filenames:
        raise InstallationError(f"No .egg-info directory found in {directory}")

    if len(filenames) > 1:
        raise InstallationError(
            "More than one .egg-info directory found in {}".format(directory)
        )

    return os.path.join(directory, filenames[0])
Пример #9
0
def _get_url_from_path(path: str, name: str) -> Optional[str]:
    """
    First, it checks whether a provided path is an installable directory. If it
    is, returns the path.

    If false, check if the path is an archive file (such as a .whl).
    The function checks if the path is a file. If false, if the path has
    an @, it will treat it as a PEP 440 URL requirement and return the path.
    """
    if _looks_like_path(name) and os.path.isdir(path):
        if is_installable_dir(path):
            return path_to_url(path)
        # TODO: The is_installable_dir test here might not be necessary
        #       now that it is done in load_pyproject_toml too.
        raise InstallationError(
            f"Directory {name!r} is not installable. Neither 'setup.py' "
            "nor 'pyproject.toml' found."
        )
    if not is_archive_file(path):
        return None
    if os.path.isfile(path):
        return path_to_url(path)
    urlreq_parts = name.split("@", 1)
    if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]):
        # If the path contains '@' and the part before it does not look
        # like a path, try to treat it as a PEP 440 URL req instead.
        return None
    logger.warning(
        "Requirement %r looks like a filename, but the file does not exist",
        name,
    )
    return path_to_url(path)
Пример #10
0
    def prepare_editable_requirement(
        self,
        req,  # type: InstallRequirement
    ):
        # type: (...) -> AbstractDistribution
        """Prepare an editable requirement
        """
        assert req.editable, "cannot prepare a non-editable req as editable"

        logger.info('Obtaining %s', req)

        with indent_log():
            if self.require_hashes:
                raise InstallationError(
                    'The editable requirement {} cannot be installed when '
                    'requiring hashes, because there is no single file to '
                    'hash.'.format(req)
                )
            req.ensure_has_source_dir(self.src_dir)
            req.update_editable(not self._download_should_save)

            abstract_dist = _get_prepared_distribution(
                req, self.req_tracker, self.finder, self.build_isolation,
            )

            if self._download_should_save:
                req.archive(self.download_dir)
            req.check_if_exists(self.use_user_site)

        return abstract_dist
def unpack_file(
        filename,  # type: str
        location,  # type: str
        content_type=None,  # type: Optional[str]
):
    # type: (...) -> None
    filename = os.path.realpath(filename)
    if (content_type == 'application/zip'
            or filename.lower().endswith(ZIP_EXTENSIONS)
            or zipfile.is_zipfile(filename)):
        unzip_file(filename, location, flatten=not filename.endswith('.whl'))
    elif (content_type == 'application/x-gzip' or tarfile.is_tarfile(filename)
          or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS +
                                       XZ_EXTENSIONS)):
        untar_file(filename, location)
    else:
        # FIXME: handle?
        # FIXME: magic signatures?
        logger.critical(
            'Cannot unpack file %s (downloaded from %s, content-type: %s); '
            'cannot detect archive format',
            filename,
            location,
            content_type,
        )
        raise InstallationError(
            'Cannot determine archive format of {}'.format(location))
Пример #12
0
def _get_url_from_path(path, name):
    # type: (str, str) -> str
    """
    First, it checks whether a provided path is an installable directory
    (e.g. it has a setup.py). If it is, returns the path.

    If false, check if the path is an archive file (such as a .whl).
    The function checks if the path is a file. If false, if the path has
    an @, it will treat it as a PEP 440 URL requirement and return the path.
    """
    if _looks_like_path(name) and os.path.isdir(path):
        if is_installable_dir(path):
            return path_to_url(path)
        raise InstallationError(
            "Directory %r is not installable. Neither 'setup.py' "
            "nor 'pyproject.toml' found." % name)
    if not is_archive_file(path):
        return None
    if os.path.isfile(path):
        return path_to_url(path)
    urlreq_parts = name.split('@', 1)
    if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]):
        # If the path contains '@' and the part before it does not look
        # like a path, try to treat it as a PEP 440 URL req instead.
        return None
    logger.warning(
        'Requirement %r looks like a filename, but the '
        'file does not exist', name)
    return path_to_url(path)
Пример #13
0
    def get_url_rev_and_auth(cls, url):
        # type: (str) -> Tuple[str, Optional[str], AuthInfo]
        """
        Parse the repository URL to use, and return the URL, revision,
        and auth info to use.

        Returns: (url, rev, (username, password)).
        """
        scheme, netloc, path, query, frag = urllib.parse.urlsplit(url)
        if '+' not in scheme:
            raise ValueError(
                "Sorry, {!r} is a malformed VCS url. "
                "The format is <vcs>+<protocol>://<url>, "
                "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url)
            )
        # Remove the vcs prefix.
        scheme = scheme.split('+', 1)[1]
        netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme)
        rev = None
        if '@' in path:
            path, rev = path.rsplit('@', 1)
            if not rev:
                raise InstallationError(
                    "The URL {!r} has an empty revision (after @) "
                    "which is not supported. Include a revision after @ "
                    "or remove @ from the URL.".format(url)
                )
        url = urllib.parse.urlunsplit((scheme, netloc, path, query, ''))
        return url, rev, user_pass
Пример #14
0
def install_req_from_editable(editable_req,
                              comes_from=None,
                              isolated=False,
                              options=None,
                              wheel_cache=None,
                              constraint=False):
    name, url, extras_override = parse_editable(editable_req)
    if url.startswith('file:'):
        source_dir = url_to_path(url)
    else:
        source_dir = None

    if name is not None:
        try:
            req = Requirement(name)
        except InvalidRequirement:
            raise InstallationError("Invalid requirement: '%s'" % name)
    else:
        req = None
    return InstallRequirement(
        req,
        comes_from,
        source_dir=source_dir,
        editable=True,
        link=Link(url),
        constraint=constraint,
        isolated=isolated,
        options=options if options else {},
        wheel_cache=wheel_cache,
        extras=extras_override or (),
    )
def unpack_file(
        filename,  # type: str
        location,  # type: str
        content_type,  # type: Optional[str]
        link  # type: Optional[Link]
):
    # type: (...) -> None
    filename = os.path.realpath(filename)
    if (content_type == 'application/zip'
            or filename.lower().endswith(ZIP_EXTENSIONS)
            or zipfile.is_zipfile(filename)):
        unzip_file(filename, location, flatten=not filename.endswith('.whl'))
    elif (content_type == 'application/x-gzip' or tarfile.is_tarfile(filename)
          or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS +
                                       XZ_EXTENSIONS)):
        untar_file(filename, location)
    elif (content_type and content_type.startswith('text/html')
          and is_svn_page(file_contents(filename))):
        # We don't really care about this
        from pipenv.patched.notpip._internal.vcs.subversion import Subversion
        Subversion('svn+' + link.url).unpack(location)
    else:
        # FIXME: handle?
        # FIXME: magic signatures?
        logger.critical(
            'Cannot unpack file %s (downloaded from %s, content-type: %s); '
            'cannot detect archive format',
            filename,
            location,
            content_type,
        )
        raise InstallationError('Cannot determine archive format of %s' %
                                location)
Пример #16
0
def write_installed_files_from_setuptools_record(
    record_lines: List[str],
    root: Optional[str],
    req_description: str,
) -> None:
    def prepend_root(path: str) -> str:
        if root is None or not os.path.isabs(path):
            return path
        else:
            return change_root(root, path)

    for line in record_lines:
        directory = os.path.dirname(line)
        if directory.endswith(".egg-info"):
            egg_info_dir = prepend_root(directory)
            break
    else:
        message = ("{} did not indicate that it installed an "
                   ".egg-info directory. Only setup.py projects "
                   "generating .egg-info directories are supported."
                   ).format(req_description)
        raise InstallationError(message)

    new_lines = []
    for line in record_lines:
        filename = line.strip()
        if os.path.isdir(filename):
            filename += os.path.sep
        new_lines.append(os.path.relpath(prepend_root(filename), egg_info_dir))
    new_lines.sort()
    ensure_dir(egg_info_dir)
    inst_files_path = os.path.join(egg_info_dir, "installed-files.txt")
    with open(inst_files_path, "w") as f:
        f.write("\n".join(new_lines) + "\n")
Пример #17
0
 def collect_root_requirements(
         self,
         root_ireqs: List[InstallRequirement]) -> CollectedRootRequirements:
     collected = CollectedRootRequirements([], {}, {})
     for i, ireq in enumerate(root_ireqs):
         if ireq.constraint:
             # Ensure we only accept valid constraints
             problem = check_invalid_constraint_type(ireq)
             if problem:
                 raise InstallationError(problem)
             if not ireq.match_markers():
                 continue
             assert ireq.name, "Constraint must be named"
             name = canonicalize_name(ireq.name)
             if name in collected.constraints:
                 collected.constraints[name] &= ireq
             else:
                 collected.constraints[name] = Constraint.from_ireq(ireq)
         else:
             req = self._make_requirement_from_install_req(
                 ireq,
                 requested_extras=(),
             )
             if req is None:
                 continue
             if ireq.user_supplied and req.name not in collected.user_requested:
                 collected.user_requested[req.name] = i
             collected.requirements.append(req)
     return collected
Пример #18
0
    def prepare_editable_requirement(
        self,
        req: InstallRequirement,
    ) -> BaseDistribution:
        """Prepare an editable requirement."""
        assert req.editable, "cannot prepare a non-editable req as editable"

        logger.info("Obtaining %s", req)

        with indent_log():
            if self.require_hashes:
                raise InstallationError(
                    "The editable requirement {} cannot be installed when "
                    "requiring hashes, because there is no single file to "
                    "hash.".format(req))
            req.ensure_has_source_dir(self.src_dir)
            req.update_editable()

            dist = _get_prepared_distribution(
                req,
                self.req_tracker,
                self.finder,
                self.build_isolation,
            )

            req.check_if_exists(self.use_user_site)

        return dist
Пример #19
0
    def get_dist_to_uninstall(
            self, candidate: Candidate) -> Optional[BaseDistribution]:
        # TODO: Are there more cases this needs to return True? Editable?
        dist = self._installed_dists.get(candidate.project_name)
        if dist is None:  # Not installed, no uninstallation required.
            return None

        # We're installing into global site. The current installation must
        # be uninstalled, no matter it's in global or user site, because the
        # user site installation has precedence over global.
        if not self._use_user_site:
            return dist

        # We're installing into user site. Remove the user site installation.
        if dist.in_usersite:
            return dist

        # We're installing into user site, but the installed incompatible
        # package is in global site. We can't uninstall that, and would let
        # the new user installation to "shadow" it. But shadowing won't work
        # in virtual environments, so we error out.
        if running_under_virtualenv() and dist.in_site_packages:
            message = (
                f"Will not install to the user site because it will lack "
                f"sys.path precedence to {dist.raw_name} in {dist.location}")
            raise InstallationError(message)
        return None
Пример #20
0
    def _correct_build_location(self):
        """Move self._temp_build_dir to self._ideal_build_dir/self.req.name

        For some requirements (e.g. a path to a directory), the name of the
        package is not available until we run egg_info, so the build_location
        will return a temporary directory and store the _ideal_build_dir.

        This is only called by self.egg_info_path to fix the temporary build
        directory.
        """
        if self.source_dir is not None:
            return
        assert self.req is not None
        assert self._temp_build_dir.path
        assert self._ideal_build_dir.path
        old_location = self._temp_build_dir.path
        self._temp_build_dir.path = None

        new_location = self.build_location(self._ideal_build_dir)
        if os.path.exists(new_location):
            raise InstallationError(
                'A package already exists in %s; please remove it to continue'
                % display_path(new_location))
        logger.debug(
            'Moving package %s from %s to new location %s',
            self, display_path(old_location), display_path(new_location),
        )
        shutil.move(old_location, new_location)
        self._temp_build_dir.path = new_location
        self._ideal_build_dir = None
        self.source_dir = os.path.normpath(os.path.abspath(new_location))
        self._egg_info_path = None
Пример #21
0
def unpack_file(
    filename: str,
    location: str,
    content_type: Optional[str] = None,
) -> None:
    filename = os.path.realpath(filename)
    if (
        content_type == "application/zip"
        or filename.lower().endswith(ZIP_EXTENSIONS)
        or zipfile.is_zipfile(filename)
    ):
        unzip_file(filename, location, flatten=not filename.endswith(".whl"))
    elif (
        content_type == "application/x-gzip"
        or tarfile.is_tarfile(filename)
        or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)
    ):
        untar_file(filename, location)
    else:
        # FIXME: handle?
        # FIXME: magic signatures?
        logger.critical(
            "Cannot unpack file %s (downloaded from %s, content-type: %s); "
            "cannot detect archive format",
            filename,
            location,
            content_type,
        )
        raise InstallationError(f"Cannot determine archive format of {location}")
Пример #22
0
    def run(self, options, args):
        with self._build_session(options) as session:
            reqs_to_uninstall = {}
            for name in args:
                req = InstallRequirement.from_line(
                    name,
                    isolated=options.isolated_mode,
                )
                if req.name:
                    reqs_to_uninstall[canonicalize_name(req.name)] = req
            for filename in options.requirements:
                for req in parse_requirements(filename,
                                              options=options,
                                              session=session):
                    if req.name:
                        reqs_to_uninstall[canonicalize_name(req.name)] = req
            if not reqs_to_uninstall:
                raise InstallationError(
                    'You must give at least one requirement to %(name)s (see '
                    '"pip help %(name)s")' % dict(name=self.name))

            protect_pip_from_modification_on_windows(
                modifying_pip="pip" in reqs_to_uninstall)

            for req in reqs_to_uninstall.values():
                uninstall_pathset = req.uninstall(
                    auto_confirm=options.yes,
                    verbose=self.verbosity > 0,
                )
                if uninstall_pathset:
                    uninstall_pathset.commit()
Пример #23
0
    def prepare_editable_requirement(self, req, require_hashes, use_user_site,
                                     finder):
        """Prepare an editable requirement
        """
        assert req.editable, "cannot prepare a non-editable req as editable"

        logger.info('Obtaining %s', req)

        with indent_log():
            if require_hashes:
                raise InstallationError(
                    'The editable requirement %s cannot be installed when '
                    'requiring hashes, because there is no single file to '
                    'hash.' % req)
            req.ensure_has_source_dir(self.src_dir)
            req.update_editable(not self._download_should_save)

            abstract_dist = make_abstract_dist(req)
            with self.req_tracker.track(req):
                abstract_dist.prep_for_dist(finder, self.build_isolation)

            if self._download_should_save:
                req.archive(self.download_dir)
            req.check_if_exists(use_user_site)

        return abstract_dist
Пример #24
0
def get_file_content(url, comes_from=None, session=None):
    # type: (str, Optional[str], Optional[PipSession]) -> Tuple[str, Text]
    """Gets the content of a file; it may be a filename, file: URL, or
    http: URL.  Returns (location, content).  Content is unicode.

    :param url:         File path or url.
    :param comes_from:  Origin description of requirements.
    :param session:     Instance of pip.download.PipSession.
    """
    if session is None:
        raise TypeError(
            "get_file_content() missing 1 required keyword argument: 'session'"
        )

    scheme = get_url_scheme(url)

    if scheme in ['http', 'https']:
        # FIXME: catch some errors
        resp = session.get(url)
        resp.raise_for_status()
        return resp.url, resp.text

    elif scheme == 'file':
        if comes_from and comes_from.startswith('http'):
            raise InstallationError(
                'Requirements file %s references URL %s, which is local' %
                (comes_from, url))

        path = url.split(':', 1)[1]
        path = path.replace('\\', '/')
        match = _url_slash_drive_re.match(path)
        if match:
            path = match.group(1) + ':' + path.split('|', 1)[1]
        path = urllib_parse.unquote(path)
        if path.startswith('/'):
            path = '/' + path.lstrip('/')
        url = path

    try:
        with open(url, 'rb') as f:
            content = auto_decode(f.read())
    except IOError as exc:
        raise InstallationError('Could not open requirements file: %s' %
                                str(exc))
    return url, content
Пример #25
0
def req_error_context(req_description):
    # type: (str) -> Iterator[None]
    try:
        yield
    except InstallationError as e:
        message = "For req: {}. {}".format(req_description, e.args[0])
        reraise(
            InstallationError, InstallationError(message), sys.exc_info()[2]
        )
Пример #26
0
def decide_user_install(
        use_user_site,  # type: Optional[bool]
        prefix_path=None,  # type: Optional[str]
        target_dir=None,  # type: Optional[str]
        root_path=None,  # type: Optional[str]
        isolated_mode=False,  # type: bool
):
    # type: (...) -> bool
    """Determine whether to do a user install based on the input options.

    If use_user_site is False, no additional checks are done.
    If use_user_site is True, it is checked for compatibility with other
    options.
    If use_user_site is None, the default behaviour depends on the environment,
    which is provided by the other arguments.
    """
    # In some cases (config from tox), use_user_site can be set to an integer
    # rather than a bool, which 'use_user_site is False' wouldn't catch.
    if (use_user_site is not None) and (not use_user_site):
        logger.debug("Non-user install by explicit request")
        return False

    if use_user_site:
        if prefix_path:
            raise CommandError(
                "Can not combine '--user' and '--prefix' as they imply "
                "different installation locations")
        if virtualenv_no_global():
            raise InstallationError(
                "Can not perform a '--user' install. User site-packages "
                "are not visible in this virtualenv.")
        logger.debug("User install by explicit request")
        return True

    # If we are here, user installs have not been explicitly requested/avoided
    assert use_user_site is None

    # user install incompatible with --prefix/--target
    if prefix_path or target_dir:
        logger.debug("Non-user install due to --prefix or --target option")
        return False

    # If user installs are not enabled, choose a non-user install
    if not site.ENABLE_USER_SITE:
        logger.debug("Non-user install because user site-packages disabled")
        return False

    # If we have permission for a non-user install, do that,
    # otherwise do a user install.
    if site_packages_writable(root=root_path, isolated=isolated_mode):
        logger.debug("Non-user install because site-packages writeable")
        return False

    logger.info("Defaulting to user installation because normal site-packages "
                "is not writeable")
    return True
Пример #27
0
    def get_pep_518_info(self):
        """Get PEP 518 build-time requirements.

        Returns the list of the packages required to build the project,
        specified as per PEP 518 within the package. If `pyproject.toml` is not
        present, returns None to signify not using the same.
        """
        # If pyproject.toml does not exist, don't do anything.
        if not os.path.isfile(self.pyproject_toml):
            return None

        error_template = (
            "{package} has a pyproject.toml file that does not comply "
            "with PEP 518: {reason}")

        with io.open(self.pyproject_toml, encoding="utf-8") as f:
            pp_toml = pytoml.load(f)

        # If there is no build-system table, just use setuptools and wheel.
        if "build-system" not in pp_toml:
            return ["setuptools", "wheel"]

        # Specifying the build-system table but not the requires key is invalid
        build_system = pp_toml["build-system"]
        if "requires" not in build_system:
            raise InstallationError(
                error_template.format(
                    package=self,
                    reason=
                    ("it has a 'build-system' table but not "
                     "'build-system.requires' which is mandatory in the table"
                     )))

        # Error out if it's not a list of strings
        requires = build_system["requires"]
        if not _is_list_of_str(requires):
            raise InstallationError(
                error_template.format(
                    package=self,
                    reason="'build-system.requires' is not a list of strings.",
                ))

        return requires
Пример #28
0
 def assert_no_path_traversal(dest_dir_path, target_path):
     # type: (str, str) -> None
     if not is_within_directory(dest_dir_path, target_path):
         message = (
             "The wheel {!r} has a file {!r} trying to install"
             " outside the target directory {!r}"
         )
         raise InstallationError(
             message.format(wheel_path, target_path, dest_dir_path)
         )
Пример #29
0
    def move_to_correct_build_directory(self):
        # type: () -> None
        """Move self._temp_build_dir to "self._ideal_build_dir/self.req.name"

        For some requirements (e.g. a path to a directory), the name of the
        package is not available until we run egg_info, so the build_location
        will return a temporary directory and store the _ideal_build_dir.

        This is only called to "fix" the build directory after generating
        metadata.
        """
        if self.source_dir is not None:
            return
        assert self.req is not None
        assert self._temp_build_dir
        assert (
            self._ideal_build_dir is not None and
            self._ideal_build_dir.path  # type: ignore
        )
        old_location = self._temp_build_dir
        self._temp_build_dir = None  # checked inside ensure_build_location

        # Figure out the correct place to put the files.
        new_location = self.ensure_build_location(self._ideal_build_dir)
        if os.path.exists(new_location):
            raise InstallationError(
                'A package already exists in %s; please remove it to continue'
                % display_path(new_location)
            )

        # Move the files to the correct location.
        logger.debug(
            'Moving package %s from %s to new location %s',
            self, display_path(old_location.path), display_path(new_location),
        )
        shutil.move(old_location.path, new_location)

        # Update directory-tracking variables, to be in line with new_location
        self.source_dir = os.path.normpath(os.path.abspath(new_location))
        self._temp_build_dir = TempDirectory(
            path=new_location, kind="req-install",
        )

        # Correct the metadata directory, if it exists
        if self.metadata_directory:
            old_meta = self.metadata_directory
            rel = os.path.relpath(old_meta, start=old_location.path)
            new_meta = os.path.join(new_location, rel)
            new_meta = os.path.normpath(os.path.abspath(new_meta))
            self.metadata_directory = new_meta

        # Done with any "move built files" work, since have moved files to the
        # "ideal" build location. Setting to None allows to clearly flag that
        # no more moves are needed.
        self._ideal_build_dir = None
Пример #30
0
 def _download_should_save(self):
     # TODO: Modify to reduce indentation needed
     if self.download_dir:
         self.download_dir = expanduser(self.download_dir)
         if os.path.exists(self.download_dir):
             return True
         else:
             logger.critical('Could not find download directory')
             raise InstallationError(
                 "Could not find or access download directory '%s'" %
                 display_path(self.download_dir))
     return False