rez.utils.pip

Python packaging related utilities.

rez.utils.pip.convert_distlib_to_setuptools(installed_dist)[source]

Get the setuptools equivalent of a distlib installed dist.

Parameters

(distlib.database.InstalledDistribution (installed_dist) – Distribution to convert.

Returns

Equivalent setuptools dist object.

Return type

pkg_resources.DistInfoDistribution

rez.utils.pip.get_marker_sys_requirements(marker)[source]

Get the system requirements that an environment marker introduces.

Consider:

‘foo (>1.2) ; python_version == “3” and platform_machine == “x86_64”’

This example would cause a requirement on python, platform, and arch (platform as a consequence of requirement on arch).

See: * vendor/packaging/markers.py:line=76 * https://www.python.org/dev/peps/pep-0508/#id23

Parameters

marker (str) – Environment marker string, eg ‘python_version == “3”’.

Returns

System requirements (unversioned).

Return type

List of str

rez.utils.pip.get_rez_requirements(installed_dist, python_version, name_casings=None)[source]

Get requirements of the given dist, in rez-compatible format.

Example result:

{

“requires”: [“foo-1.2+<2”], “variant_requires”: [“future”, “python-2.7”], “metadata”: {

# metadata pertinent to rez …

}

}

Each requirement has had its package name converted to the rez equivalent. The ‘variant_requires’ key contains requirements specific to the current variant.

TODO: Currently there is no way to reflect extras that may have been chosen for this pip package. We need to wait for rez “package features” before this will be possible. You probably shouldn’t use extras presently.

Parameters
  • installed_dist (distlib.database.InstalledDistribution) – Distribution to convert.

  • python_version (Version) – Python version used to perform the installation.

  • name_casings (list of str) – A list of pip package names in their correct casings (eg, ‘Foo’ rather than ‘foo’). Any requirement whose name case-insensitive-matches a name in this list, is set to that name. This is needed because pip package names are case insensitive, but rez is case-sensitive. So a package may list a requirement for package ‘foo’, when in fact the package that pip has downloaded is called ‘Foo’. Be sure to provide names in PIP format, not REZ format (the pip package ‘foo-bah’ will be converted to ‘foo_bah’ in rez).

Returns

See example above.

Return type

Dict

rez.utils.pip.is_pure_python_package(installed_dist)[source]

Determine if a dist is pure python.

Parameters

installed_dist (distlib.database.InstalledDistribution) – Distribution to test.

Returns

True if dist is pure python

Return type

bool

rez.utils.pip.normalize_requirement(requirement)[source]

Normalize a package requirement.

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

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.

Parameters

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

Returns

Normalized requirements. Note that a list is returned, because the PEP426 format can define multiple requirements.

Return type

List of packaging.requirements.Requirement

rez.utils.pip.packaging_req_to_rez_req(packaging_req)[source]

Convert packaging requirement object to equivalent rez requirement.

Note that environment markers are ignored.

Parameters

packaging_req (packaging.requirements.Requirement) – Packaging requirement.

Returns

Equivalent rez requirement object.

Return type

Requirement

rez.utils.pip.pip_specifier_to_rez_requirement(specifier)[source]

Convert PEP440 version specifier to rez equivalent.

See https://www.python.org/dev/peps/pep-0440/#version-specifiers

Note that version numbers in the specifier are converted to rez equivalents at the same time. Thus a specifier like ‘<1.ALPHA2’ becomes ‘<1.a2’.

Note that the conversion is not necessarily exact - there are cases in PEP440 that have no equivalent in rez versioning. Most of these are specifiers that involve pre/post releases, which don’t exist in rez (or rather, they do exist in the sense that ‘1.0.post1’ is a valid rez version number, but it has no special meaning).

Note also that the specifier is being converted into rez format, but in a way that still expresses how _pip_ interprets the specifier. For example, ‘==1’ is a valid version range in rez, but ‘==1’ has a different meaning to pip than it does to rez (‘1.0’ matches ‘==1’ in pip, but not in rez). This is why ‘==1’ is converted to ‘1+<1.1’ in rez, rather than ‘==1’.

Example conversions:

PEP440 | rez |

|-------------|————-| | ==1 | 1+<1.1 | | ==1.* | 1 | | >1 | 1.1+ | | <1 | <1 | | >=1 | 1+ | | <=1 | <1.1 | | ~=1.2 | 1.2+<2 | | ~=1.2.3 | 1.2.3+<1.3 | | !=1 | <1|1.1+ | | !=1.2 | <1.2|1.2.1+ | | !=1.* | <1|2+ | | !=1.2.* | <1.2|1.3+ |

Parameters

specifier (package.SpecifierSet) – Pip specifier.

Returns

Equivalent rez version range.

Return type

VersionRange

rez.utils.pip.pip_to_rez_package_name(dist_name)[source]

Convert a distribution name to a rez compatible name.

The rez package name can’t be simply set to the dist name, because some pip packages have hyphen in the name. In rez this is not a valid package name (it would be interpreted as the start of the version).

Example: my-pkg-1.2 is ‘my’, version ‘pkg-1.2’.

Parameters

dist_name (str) – Distribution name to convert.

Returns

Rez-compatible package name.

Return type

str

rez.utils.pip.pip_to_rez_version(dist_version, allow_legacy=True)[source]

Convert a distribution version to a rez compatible version.

TODO [AJ] needs a table of example conversions.

The python version schema specification isn’t 100% compatible with rez.

1: version epochs (they make no sense to rez, so they’d just get stripped

of the leading N!;

2: python versions are case insensitive, so they should probably be

lowercased when converted to a rez version.

3: local versions are also not compatible with rez

The canonical public version identifiers MUST comply with the following scheme: [N!]N(.N)*[{a|b|rc}N][.postN][.devN]

Epoch segment: N! - skip Release segment: N(.N)* 0 as is Pre-release segment: {a|b|c|rc|alpha|beta|pre|preview}N - always lowercase Post-release segment: .{post|rev|r}N - always lowercase Development release segment: .devN - always lowercase

Local version identifiers MUST comply with the following scheme: <public version identifier>[+<local version label>] - use - instead of +

Parameters
  • dist_version (str) – The distribution version to be converted.

  • allow_legacy (bool) – Flag to allow/disallow PEP440 incompatibility.

Returns

Rez-compatible equivalent version string.

Return type

str

Raises
  • InvalidVersion – When legacy mode is not allowed and a PEP440

  • incompatible version is detected.