def install_req_from_editable(
        editable_req,  # type: str
        comes_from=None,  # type: Optional[str]
        use_pep517=None,  # type: Optional[bool]
        isolated=False,  # type: bool
        options=None,  # type: Optional[Dict[str, Any]]
        wheel_cache=None,  # type: Optional[WheelCache]
        constraint=False  # type: bool
):
    # type: (...) -> InstallRequirement
    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,
        use_pep517=use_pep517,
        isolated=isolated,
        options=options if options else {},
        wheel_cache=wheel_cache,
        extras=extras_override or (),
    )
Beispiel #2
0
    def _link_for_candidate(self, link, candidate):
        # type: (Link, str) -> Link
        root = self.get_path_for_link(link)
        path = os.path.join(root, candidate)

        return Link(path_to_url(path))
def parse_editable(editable_req):
    # type: (str) -> Tuple[Optional[str], str, Optional[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):
        if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
            msg = ('File "setup.py" not found. Directory cannot be installed '
                   'in editable mode: {}'.format(
                       os.path.abspath(url_no_extras)))
            pyproject_path = make_pyproject_path(url_no_extras)
            if os.path.isfile(pyproject_path):
                msg += ('\n(A "pyproject.toml" file was found, but editable '
                        'mode currently requires a setup.py based build.)')
            raise InstallationError(msg)

        # 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
def install_req_from_line(
        name,  # type: str
        comes_from=None,  # type: Optional[Union[str, InstallRequirement]]
        use_pep517=None,  # type: Optional[bool]
        isolated=False,  # type: bool
        options=None,  # type: Optional[Dict[str, Any]]
        wheel_cache=None,  # type: Optional[WheelCache]
        constraint=False  # type: bool
):
    # type: (...) -> InstallRequirement
    """Creates an InstallRequirement from a name, which might be a
    requirement, directory containing 'setup.py', filename, or URL.
    """
    if is_url(name):
        marker_sep = '; '
    else:
        marker_sep = ';'
    if marker_sep in name:
        name, markers_as_string = name.split(marker_sep, 1)
        markers_as_string = markers_as_string.strip()
        if not markers_as_string:
            markers = None
        else:
            markers = Marker(markers_as_string)
    else:
        markers = None
    name = name.strip()
    req_as_string = None
    path = os.path.normpath(os.path.abspath(name))
    link = None
    extras_as_string = None

    if is_url(name):
        link = Link(name)
    else:
        p, extras_as_string = _strip_extras(path)
        looks_like_dir = os.path.isdir(p) and (os.path.sep in name or
                                               (os.path.altsep is not None
                                                and os.path.altsep in name)
                                               or name.startswith('.'))
        if looks_like_dir:
            if not is_installable_dir(p):
                raise InstallationError(
                    "Directory %r is not installable. Neither 'setup.py' "
                    "nor 'pyproject.toml' found." % name)
            link = Link(path_to_url(p))
        elif is_archive_file(p):
            if not os.path.isfile(p):
                logger.warning(
                    'Requirement %r looks like a filename, but the '
                    'file does not exist', name)
            link = Link(path_to_url(p))

    # it's a local file, dir, or url
    if link:
        # Handle relative file URLs
        if link.scheme == 'file' and re.search(r'\.\./', link.url):
            link = Link(
                path_to_url(os.path.normpath(os.path.abspath(link.path))))
        # wheel file
        if link.is_wheel:
            wheel = Wheel(link.filename)  # can raise InvalidWheelFilename
            req_as_string = "%s==%s" % (wheel.name, wheel.version)
        else:
            # set the req to the egg fragment.  when it's not there, this
            # will become an 'unnamed' requirement
            req_as_string = link.egg_fragment

    # a requirement specifier
    else:
        req_as_string = name

    if extras_as_string:
        extras = Requirement("placeholder" + extras_as_string.lower()).extras
    else:
        extras = ()
    if req_as_string is not None:
        try:
            req = 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 = ""
            raise InstallationError("Invalid requirement: '%s'\n%s" %
                                    (req_as_string, add_msg))
    else:
        req = None

    return InstallRequirement(
        req,
        comes_from,
        link=link,
        markers=markers,
        use_pep517=use_pep517,
        isolated=isolated,
        options=options if options else {},
        wheel_cache=wheel_cache,
        constraint=constraint,
        extras=extras,
    )
Beispiel #5
0
    def build(
            self,
            requirements,  # type: Iterable[InstallRequirement]
            session,  # type: insSession
            autobuilding=False  # type: bool
    ):
        # type: (...) -> List[InstallRequirement]
        """Build wheels.

        :param unpack: If True, replace the sdist we built from with the
            newly built wheel, in preparation for installation.
        :return: True if all the wheels built correctly.
        """
        buildset = []
        format_control = self.finder.format_control
        # Whether a cache directory is available for autobuilding=True.
        cache_available = bool(self._wheel_dir or self.wheel_cache.cache_dir)

        for req in requirements:
            ephem_cache = should_use_ephemeral_cache(
                req,
                format_control=format_control,
                autobuilding=autobuilding,
                cache_available=cache_available,
            )
            if ephem_cache is None:
                continue

            buildset.append((req, ephem_cache))

        if not buildset:
            return []

        # Is any wheel build not using the ephemeral cache?
        if any(not ephem_cache for _, ephem_cache in buildset):
            have_directory_for_build = self._wheel_dir or (
                autobuilding and self.wheel_cache.cache_dir)
            assert have_directory_for_build

        # TODO by @pradyunsg
        # Should break up this method into 2 separate methods.

        # Build the wheels.
        logger.info(
            'Building wheels for collected packages: %s',
            ', '.join([req.name for (req, _) in buildset]),
        )
        _cache = self.wheel_cache  # shorter name
        with indent_log():
            build_success, build_failure = [], []
            for req, ephem in buildset:
                python_tag = None
                if autobuilding:
                    python_tag = pep425tags.implementation_tag
                    if ephem:
                        output_dir = _cache.get_ephem_path_for_link(req.link)
                    else:
                        output_dir = _cache.get_path_for_link(req.link)
                    try:
                        ensure_dir(output_dir)
                    except OSError as e:
                        logger.warning("Building wheel for %s failed: %s",
                                       req.name, e)
                        build_failure.append(req)
                        continue
                else:
                    output_dir = self._wheel_dir
                wheel_file = self._build_one(
                    req,
                    output_dir,
                    python_tag=python_tag,
                )
                if wheel_file:
                    build_success.append(req)
                    if autobuilding:
                        # XXX: This is mildly duplicative with prepare_files,
                        # but not close enough to pull out to a single common
                        # method.
                        # The code below assumes temporary source dirs -
                        # prevent it doing bad things.
                        if req.source_dir and not os.path.exists(
                                os.path.join(req.source_dir,
                                             ins_DELETE_MARKER_FILENAME)):
                            raise AssertionError(
                                "bad source dir - missing marker")
                        # Delete the source we built the wheel from
                        req.remove_temporary_source()
                        # set the build directory again - name is known from
                        # the work prepare_files did.
                        req.source_dir = req.build_location(
                            self.preparer.build_dir)
                        # Update the link for this.
                        req.link = Link(path_to_url(wheel_file))
                        assert req.link.is_wheel
                        # extract the wheel into the dir
                        unpack_url(
                            req.link,
                            req.source_dir,
                            None,
                            False,
                            session=session,
                        )
                else:
                    build_failure.append(req)

        # notify success/failure
        if build_success:
            logger.info(
                'Successfully built %s',
                ' '.join([req.name for req in build_success]),
            )
        if build_failure:
            logger.info(
                'Failed to build %s',
                ' '.join([req.name for req in build_failure]),
            )
        # Return a list of requirements that failed to build
        return build_failure
Beispiel #6
0
    def find_all_candidates(self, project_name):
        # type: (str) -> List[Optional[InstallationCandidate]]
        """Find all available InstallationCandidate for project_name

        This checks index_urls and find_links.
        All versions found are returned as an InstallationCandidate list.

        See _link_package_versions for details on which files are accepted
        """
        index_locations = self._get_index_urls_locations(project_name)
        index_file_loc, index_url_loc = self._sort_locations(index_locations)
        fl_file_loc, fl_url_loc = self._sort_locations(
            self.find_links,
            expand_dir=True,
        )

        file_locations = (Link(url) for url in itertools.chain(
            index_file_loc,
            fl_file_loc,
        ))

        # We trust every url that the user has given us whether it was given
        #   via --index-url or --find-links.
        # We want to filter out any thing which does not have a secure origin.
        url_locations = [
            link for link in itertools.chain(
                (Link(url) for url in index_url_loc),
                (Link(url) for url in fl_url_loc),
            ) if self._validate_secure_origin(logger, link)
        ]

        logger.debug('%d location(s) to search for versions of %s:',
                     len(url_locations), project_name)

        for location in url_locations:
            logger.debug('* %s', location)

        canonical_name = canonicalize_name(project_name)
        formats = self.format_control.get_allowed_formats(canonical_name)
        search = Search(project_name, canonical_name, formats)
        find_links_versions = self._package_versions(
            # We trust every directly linked archive in find_links
            (Link(url, '-f') for url in self.find_links),
            search)

        page_versions = []
        for page in self._get_pages(url_locations, project_name):
            logger.debug('Analyzing links from page %s', page.url)
            with indent_log():
                page_versions.extend(
                    self._package_versions(page.iter_links(), search))

        file_versions = self._package_versions(file_locations, search)
        if file_versions:
            file_versions.sort(reverse=True)
            logger.debug(
                'Local files found: %s', ', '.join([
                    url_to_path(candidate.location.url)
                    for candidate in file_versions
                ]))

        # This is an intentional priority ordering
        return file_versions + find_links_versions + page_versions
    def __init__(
        self,
        req,  # type: Optional[Requirement]
        comes_from,  # type: Optional[Union[str, InstallRequirement]]
        source_dir=None,  # type: Optional[str]
        editable=False,  # type: bool
        link=None,  # type: Optional[Link]
        update=True,  # type: bool
        markers=None,  # type: Optional[Marker]
        use_pep517=None,  # type: Optional[bool]
        isolated=False,  # type: bool
        options=None,  # type: Optional[Dict[str, Any]]
        wheel_cache=None,  # type: Optional[WheelCache]
        constraint=False,  # type: bool
        extras=()  # type: Iterable[str]
    ):
        # type: (...) -> None
        assert req is None or isinstance(req, Requirement), req
        self.req = req
        self.comes_from = comes_from
        self.constraint = constraint
        if source_dir is not None:
            self.source_dir = os.path.normpath(os.path.abspath(source_dir))
        else:
            self.source_dir = None
        self.editable = editable

        self._wheel_cache = wheel_cache
        if link is None and req and req.url:
            # PEP 508 URL requirement
            link = Link(req.url)
        self.link = self.original_link = link

        if extras:
            self.extras = extras
        elif req:
            self.extras = {
                pkg_resources.safe_extra(extra) for extra in req.extras
            }
        else:
            self.extras = set()
        if markers is None and req:
            markers = req.marker
        self.markers = markers

        self._egg_info_path = None  # type: Optional[str]
        # This holds the pkg_resources.Distribution object if this requirement
        # is already available:
        self.satisfied_by = None
        # This hold the pkg_resources.Distribution object if this requirement
        # conflicts with another installed distribution:
        self.conflicts_with = None
        # Temporary build location
        self._temp_build_dir = TempDirectory(kind="req-build")
        # Used to store the global directory where the _temp_build_dir should
        # have been created. Cf _correct_build_location method.
        self._ideal_build_dir = None  # type: Optional[str]
        # True if the editable should be updated:
        self.update = update
        # Set to True after successful installation
        self.install_succeeded = None  # type: Optional[bool]
        # UninstallPathSet of uninstalled distribution (for possible rollback)
        self.uninstalled_pathset = None
        self.options = options if options else {}
        # Set to True after successful preparation of this requirement
        self.prepared = False
        self.is_direct = False

        self.isolated = isolated
        self.build_env = NoOpBuildEnvironment()  # type: BuildEnvironment

        # For PEP 517, the directory where we request the project metadata
        # gets stored. We need this to pass to build_wheel, so the backend
        # can ensure that the wheel matches the metadata (see the PEP for
        # details).
        self.metadata_directory = None  # type: Optional[str]

        # The static build requirements (from pyproject.toml)
        self.pyproject_requires = None  # type: Optional[List[str]]

        # Build requirements that we will check are available
        self.requirements_to_check = []  # type: List[str]

        # The PEP 517 backend we should use to build the project
        self.pep517_backend = None  # type: Optional[Pep517HookCaller]

        # Are we using PEP 517 for this requirement?
        # After pyproject.toml has been loaded, the only valid values are True
        # and False. Before loading, None is valid (meaning "use the default").
        # Setting an explicit value before loading pyproject.toml is supported,
        # but after loading this flag should be treated as read only.
        self.use_pep517 = use_pep517