Esempio n. 1
0
 def test_packaging_req_to_rez_req(self):
     """
     """
     self.assertEqual(
         rez.utils.pip.packaging_req_to_rez_req(
             packaging_Requirement("package>1")),
         Requirement("package-1.1+"))
     self.assertEqual(
         rez.utils.pip.packaging_req_to_rez_req(
             packaging_Requirement("package")), Requirement("package"))
     self.assertEqual(
         rez.utils.pip.packaging_req_to_rez_req(
             packaging_Requirement("package[extra]")),
         Requirement("package"))
Esempio n. 2
0
    def reconstruct(req, marker_str=None, conditional_extras=None):
        new_req_str = req.name

        if req.specifier:
            new_req_str += " (%s)" % str(req.specifier)

        if marker_str is None and req.marker:
            marker_str = str(req.marker)

        if marker_str:
            new_req_str += " ; " + marker_str

        new_req = packaging_Requirement(new_req_str)
        setattr(new_req, "conditional_extras", conditional_extras)
        return new_req
Esempio n. 3
0
    def test_normalize_requirement(self):
        """
        """
        def assertRequirements(requirement, expected, conditional_extras):
            """
            """
            result = rez.utils.pip.normalize_requirement(requirement)
            self.assertEqual([str(req) for req in result],
                             [str(req) for req in expected])
            for index, req in enumerate(result):
                self.assertEqual(req.conditional_extras,
                                 conditional_extras[index])

        assertRequirements("packageA", [packaging_Requirement("packageA")],
                           [None])
        assertRequirements("mypkg ; extra == 'dev'",
                           [packaging_Requirement("mypkg")], [set(["dev"])])
        assertRequirements(
            'win-inet-pton ; (sys_platform == "win32" and python_version == "2.7") and extra == \'socks\'',
            [
                packaging_Requirement(
                    'win-inet-pton; (sys_platform == "win32" and python_version == "2.7")'
                )
            ], [set(["socks"])])

        # PySocks (!=1.5.7,<2.0,>=1.5.6) ; extra == 'socks'
        assertRequirements(
            "PySocks (!=1.5.7,<2.0,>=1.5.6) ; extra == 'socks'",
            [packaging_Requirement("PySocks!=1.5.7,<2.0,>=1.5.6")],
            [set(["socks"])])
        # certifi ; extra == 'secure'
        assertRequirements("certifi ; extra == 'secure'",
                           [packaging_Requirement("certifi")],
                           [set(["secure"])])
        # coverage (>=4.4)
        assertRequirements('coverage (>=4.4)',
                           [packaging_Requirement("coverage (>=4.4)")], [None])
        # colorama ; sys_platform == "win32"
        assertRequirements(
            'colorama ; sys_platform == "win32"',
            [packaging_Requirement('colorama ; sys_platform == "win32"')],
            [None])
        # pathlib2-2.3.4: {u'environment': u'python_version<"3.5"', u'requires': [u'scandir']}, {u'requires': [u'six']}
        assertRequirements(
            {
                u"environment": u'python_version<"3.5"',
                u"requires": [u"scandir"]
            }, [packaging_Requirement('scandir; python_version < "3.5"')],
            [None])

        # bleach-3.1.0: {u'requires': [u'six (>=1.9.0)', u'webencodings']}
        assertRequirements({u"requires": [u"six (>=1.9.0)", u"webencodings"]},
                           [
                               packaging_Requirement("six (>=1.9.0)"),
                               packaging_Requirement("webencodings")
                           ], [None, None])

        assertRequirements(
            {
                u"requires":
                [u"six (>=1.9.0)", u'webencodings; sys_platform == "win32"']
            }, [
                packaging_Requirement("six (>=1.9.0)"),
                packaging_Requirement('webencodings; sys_platform == "win32"')
            ], [None, None])

        assertRequirements({
            u"requires": [u"packageA"],
            u"extra": "doc"
        }, [packaging_Requirement("packageA")], [set(["doc"])])

        assertRequirements("mypkg ; extra == 'dev' or extra == 'doc'",
                           [packaging_Requirement("mypkg")],
                           [set(["dev", "doc"])])

        assertRequirements(
            'mypkg ; extra == "dev" and sys_platform == "win32"',
            [packaging_Requirement('mypkg; sys_platform == "win32"')],
            [set(["dev"])])

        assertRequirements(
            'mypkg ; sys_platform == "win32" and extra == "test"',
            [packaging_Requirement('mypkg; sys_platform == "win32"')],
            [set(["test"])])
Esempio n. 4
0
def normalize_requirement(requirement):
    """Normalize a package requirement.

    Requirements from distlib packages can be a mix of string- or dict- based
    formats, as shown here:

    * https://www.python.org/dev/peps/pep-0508/#environment-markers
    * https://legacy.python.org/dev/peps/pep-0426/#environment-markers

    There's another confusing case that this code deals with. Consider these two
    requirements:

        # means: reportlab is a requirement of this package when the 'pdf' extra is requested
        Requires-Dist: reportlab; extra == 'pdf'

        means: this package requires libexample, with its 'test' extras
        Requires-Dist: libexample[test]

    See https://packaging.python.org/specifications/core-metadata/#provides-extra-multiple-use

    The packaging lib doesn't do a good job of expressing this - the first form
    of extras use just gets embedded in the environment marker. This function
    parses the extra from the marker, and stores it onto the resulting
    `packaging.Requirement` object in a 'conditional_extras' attribute. It also
    removes the extra from the marker (otherwise the marker cannot evaluate).
    Even though you can specify `environment` in `packaging.Marker.evaluate`,
    you can only supply a single 'extra' key in the env, so this can't be used
    to correctly evaluate if multiple extras were requested.

    Args:
        requirement (str or dict): Requirement, for eg from
            `distlib.database.InstalledDistribution.run_requires`.

    Returns:
        List of `packaging.requirements.Requirement`: Normalized requirements.
        Note that a list is returned, because the PEP426 format can define
        multiple requirements.
    """
    def reconstruct(req, marker_str=None, conditional_extras=None):
        new_req_str = req.name

        if req.specifier:
            new_req_str += " (%s)" % str(req.specifier)

        if marker_str is None and req.marker:
            marker_str = str(req.marker)

        if marker_str:
            new_req_str += " ; " + marker_str

        new_req = packaging_Requirement(new_req_str)
        setattr(new_req, "conditional_extras", conditional_extras)
        return new_req

    # PEP426 dict syntax
    # So only metadata that are of version 2.0 will be in dict. The other versions
    # (1.0, 1.1, 1.2, 2.1) will be strings.
    if isinstance(requirement, dict):
        result = []
        requires = requirement["requires"]
        extra = requirement.get("extra")
        marker_str = requirement.get("environment")

        # conditional extra, equivalent to: 'foo ; extra = "doc"'
        if extra:
            conditional_extras = set([extra])
        else:
            conditional_extras = None

        for req_str in requires:
            req = packaging_Requirement(req_str)
            new_req = reconstruct(req, marker_str, conditional_extras)
            result.append(new_req)

        return result

    # string-based syntax
    req = packaging_Requirement(requirement)

    # detect case: "mypkg ; extra == 'dev'"
    # note: packaging lib already delimits with whitespace
    marker_str = str(req.marker)
    marker_parts = marker_str.split()

    # already in PEP508, packaging lib- friendly format
    if "extra" not in marker_parts:
        setattr(req, "conditional_extras", None)
        return [req]

    # Parse conditional extras out of marker
    conditional_extras = set()
    marker_str = marker_str.replace(" and ", " \nand ")
    marker_str = marker_str.replace(" or ", " \nor ")
    lines = marker_str.split('\n')
    lines = [x.strip() for x in lines]
    new_marker_lines = []

    for line in lines:
        if "extra" in line.split():
            extra = line.split()[-1]
            extra = extra.replace('"', '')
            extra = extra.replace("'", '')
            conditional_extras.add(extra)
        else:
            new_marker_lines.append(line)

    # reconstruct requirement in new syntax
    if new_marker_lines:
        new_marker_parts = ' '.join(new_marker_lines).split()
        if new_marker_parts[0] in ("and", "or"):
            new_marker_parts = new_marker_parts[1:]
        new_marker_str = ' '.join(new_marker_parts)
    else:
        new_marker_str = ''

    new_req = reconstruct(req, new_marker_str, conditional_extras)
    return [new_req]