Source code for okonomiyaki.file_formats._egg_info

import posixpath
import re
import zipfile2

from ..bundled.traitlets import (
    HasTraits, Instance, List, Long, Unicode
)
from ..errors import (
    InvalidRequirementString, InvalidEggName, InvalidMetadata,
    InvalidMetadataField, MissingMetadata, UnsupportedMetadata
)
from ..platforms import EPDPlatform, PythonImplementation
from ..platforms.legacy import LegacyEPDPlatform
from ..utils import compute_sha256, parse_assignments
from ..utils.py3compat import StringIO, string_types
from ..utils.traitlets import NoneOrInstance, NoneOrUnicode
from ..versions import EnpkgVersion, MetadataVersion
from ._blacklist import (
    EGG_PLATFORM_BLACK_LIST, EGG_PYTHON_TAG_BLACK_LIST,
    may_be_in_platform_blacklist, may_be_in_python_tag_blacklist,
    may_be_in_pkg_info_blacklist
)
from ._package_info import PackageInfo, _keep_position, _read_pkg_info


_EGG_NAME_RE = re.compile("""
    (?P<name>[\.\w]+)
    -
    (?P<version>[^-]+)
    -
    (?P<build>\d+)
    \.egg$""", re.VERBOSE)

_PYVER_RE = re.compile("(?P<major>\d+)\.(?P<minor>\d+)")

EGG_INFO_PREFIX = "EGG-INFO"

# Those may need to be public, depending on how well we can hide their
# locations or not.
_INFO_JSON_LOCATION = posixpath.join(EGG_INFO_PREFIX, "info.json")
_SPEC_DEPEND_LOCATION = posixpath.join(EGG_INFO_PREFIX, "spec", "depend")
_SPEC_LIB_DEPEND_LOCATION = posixpath.join(EGG_INFO_PREFIX, "spec",
                                           "lib-depend")
_SPEC_SUMMARY_LOCATION = posixpath.join(EGG_INFO_PREFIX, "spec", "summary")
_USR_PREFIX_LOCATION = posixpath.join(EGG_INFO_PREFIX, "usr")

_TAG_METADATA_VERSION = "metadata_version"
_TAG_NAME = "name"
_TAG_VERSION = "version"
_TAG_BUILD = "build"
_TAG_ARCH = "arch"
_TAG_OSDIST = "osdist"
_TAG_PLATFORM = "platform"
_TAG_PYTHON = "python"
_TAG_PYTHON_PEP425_TAG = "python_tag"
_TAG_ABI_PEP425_TAG = "abi_tag"
_TAG_PLATFORM_PEP425_TAG = "platform_tag"
_TAG_PACKAGES = "packages"

M = MetadataVersion.from_string

_METADATA_VERSION_TO_KEYS = {
    M("1.1"): (
        _TAG_METADATA_VERSION, _TAG_NAME, _TAG_VERSION, _TAG_BUILD, _TAG_ARCH,
        _TAG_PLATFORM, _TAG_OSDIST, _TAG_PYTHON, _TAG_PACKAGES
    ),
}
_METADATA_VERSION_TO_KEYS[M("1.2")] = \
    _METADATA_VERSION_TO_KEYS[M("1.1")] + (_TAG_PYTHON_PEP425_TAG, )

_METADATA_VERSION_TO_KEYS[M("1.3")] = (
    _METADATA_VERSION_TO_KEYS[M("1.2")] +
    (_TAG_ABI_PEP425_TAG, _TAG_PLATFORM_PEP425_TAG)
)

_UNSUPPORTED = "unsupported"

_PYVER_RE = re.compile("(?P<major>\d).(?P<minor>\d)")


def _are_compatible(left, right):
    """Return True if both arguments are compatible metadata versions.

    Parameters
    ----------
    left: MetadataVersion
    right: MetadataVersion
    """
    return left.major == right.major


def _highest_compatible(metadata_version):
    """ Returns the highest metadata version supporting that is compatible with
    the given version.
    """
    compatible_versions = [
        m for m in _METADATA_VERSION_TO_KEYS
        if _are_compatible(m, metadata_version)
    ]

    if len(compatible_versions) > 0:
        return max(compatible_versions)
    else:
        raise UnsupportedMetadata(metadata_version)


def split_egg_name(s):
    m = _EGG_NAME_RE.match(s)
    if m is None:
        raise InvalidEggName(s)
    else:
        name, version, build = m.groups()
        return name, version, int(build)


def parse_rawspec(spec_string):
    spec = parse_assignments(StringIO(spec_string.replace('\r', '')))

    metadata_version_string = spec.get(_TAG_METADATA_VERSION)
    if metadata_version_string is not None:
        metadata_version = MetadataVersion.from_string(metadata_version_string)
    else:
        metadata_version = None

    if metadata_version is None:
        raise InvalidMetadataField('metadata_version', metadata_version_string)
    elif metadata_version not in _METADATA_VERSION_TO_KEYS:
        metadata_version = _highest_compatible(metadata_version)

    res = {}

    keys = _METADATA_VERSION_TO_KEYS.get(metadata_version)
    for key in keys:
        try:
            res[key] = spec[key]
        except KeyError:
            raise InvalidMetadataField(key, InvalidMetadataField.undefined)
    return res


def egg_name(name, version, build):
    """
    Return the egg filename (including the .egg extension) for the given
    arguments
    """
    return "{0}-{1}-{2}.egg".format(name, version, build)


def is_egg_name_valid(s):
    """
    Return True if the given string is a valid egg name (not including the
    .egg, e.g. 'Qt-4.8.5-2')
    """
    return _EGG_NAME_RE.match(s) is not None


_INVALID_REQUIREMENTS = {
    "numpy-1.8.0": "numpy 1.8.0",
}


def _translate_invalid_requirement(s):
    return _INVALID_REQUIREMENTS.get(s, s)


class Requirement(HasTraits):
    """
    Model for entries in the package metadata inside EGG-INFO/spec/depend
    """
    name = Unicode()
    version_string = Unicode()
    build_number = Long(-1)

    def __init__(self, name="", version_string="", build_number=-1):
        self.name = name
        self.version_string = version_string
        self.build_number = build_number
        super(Requirement, self).__init__(self, name, version_string,
                                          build_number)

    @property
    def strictness(self):
        if len(self.version_string) == 0:
            return 1
        elif self.build_number < 0:
            return 2
        else:
            return 3

    @classmethod
    def from_string(cls, s, strictness=2):
        """
        Create a Requirement from string following a name-version-build
        format.

        Parameters
        ----------
        s: str
            Egg name, e.g. 'Qt-4.8.5-2'.
        strictness: int
            Control strictness of string representation
        """
        name, version, build = split_egg_name("{0}.egg".format(s))
        if strictness >= 3:
            build_number = build
        else:
            build_number = -1

        if strictness >= 2:
            version_string = version
        else:
            version_string = ""

        return cls(name=name, version_string=version_string,
                   build_number=build_number)

    @classmethod
    def from_spec_string(cls, s):
        """
        Create a Requirement from a spec string (as used in
        EGG-INFO/spec/depend).
        """
        s = _translate_invalid_requirement(s)
        parts = s.split()
        if len(parts) == 1:
            name = parts[0]
            if "-" in name:
                raise InvalidRequirementString(name)
            return cls(name=name)
        elif len(parts) == 2:
            name, version = parts
            parts = version.split("-")
            if len(parts) == 2:
                upstream, build_number = parts
                build_number = int(build_number)
            else:
                upstream, build_number = version, -1
            return cls(name=name, version_string=upstream,
                       build_number=build_number)
        else:
            raise InvalidRequirementString(name)

    def __str__(self):
        if len(self.version_string) > 0:
            if self.build_number > 0:
                return "{0} {1}-{2}".format(self.name,
                                            self.version_string,
                                            self.build_number)
            else:
                return "{0} {1}".format(self.name, self.version_string)
        else:
            return self.name

    @property
    def _key(self):
        return (self.name, self.version_string, self.build_number)

    def __eq__(self, other):
        if not isinstance(other, type(self)):
            return False
        return self._key == other._key

    def __ne__(self, other):
        return not (self == other)

    def __hash__(self):
        return hash(self._key)


_METADATA_TEMPLATES = {
    M("1.1"): """\
metadata_version = '1.1'
name = {name!r}
version = {version!r}
build = {build}

arch = {arch!r}
platform = {platform!r}
osdist = {osdist!r}
python = {python!r}
packages = {packages}
""",
    M("1.2"): """\
metadata_version = '1.2'
name = {name!r}
version = {version!r}
build = {build}

arch = {arch!r}
platform = {platform!r}
osdist = {osdist!r}
python = {python!r}
python_tag = {python_tag!r}
packages = {packages}
""",
    M("1.3"): """\
metadata_version = '1.3'
name = {name!r}
version = {version!r}
build = {build}

arch = {arch!r}
platform = {platform!r}
osdist = {osdist!r}
python = {python!r}

python_tag = {python_tag!r}
abi_tag = {abi_tag!r}
platform_tag = {platform_tag!r}

packages = {packages}
"""
}


def _guess_python_tag(pyver):
    """ Guess python_tag from the given python string ("MAJOR.MINOR", e.g. "2.7").

    None may be returned (for egg that don't depend on python)
    """
    if pyver in (None, ""):
        return None
    else:
        m = _PYVER_RE.search(pyver)
        if m is None:
            msg = "python_tag cannot be guessed for python = {0}"
            raise InvalidMetadata(msg.format(pyver))
        else:
            major = m.groupdict()["major"]
            minor = m.groupdict()["minor"]

            return "cp" + major + minor


_METADATA_DEFAULT_VERSION_STRING = "1.3"
_METADATA_DEFAULT_VERSION = M(_METADATA_DEFAULT_VERSION_STRING)


def _epd_platform_from_raw_spec(raw_spec):
    """ Create an EPDPlatform instance from the metadata info returned by
    parse_rawspec.

    if no platform is defined ('platform' and 'osdist' set to None), then
    None is returned.
    """
    arch_string = raw_spec[_TAG_ARCH]
    platform_string = raw_spec[_TAG_PLATFORM]
    osdist_string = raw_spec[_TAG_OSDIST]
    if platform_string is None and osdist_string is None:
        return None
    else:
        return EPDPlatform._from_spec_depend_data(
            platform_string, osdist_string, arch_string
        )


class LegacySpecDepend(HasTraits):
    """
    This models the EGG-INFO/spec/depend content.
    """
    # Name is taken from egg path, so may be upper case
    name = Unicode()
    """
    Egg name
    """
    version = Unicode()
    """
    Upstream version (as a string).
    """
    build = Long()
    """
    Build number
    """

    python = NoneOrUnicode()
    """
    Python version
    """

    python_tag = NoneOrUnicode()
    """
    Python tag (as defined in PEP 425).
    """

    abi_tag = NoneOrUnicode()
    """
    ABI tag (as defined in PEP 425), except that 'none' is None.
    """

    platform_tag = NoneOrUnicode()
    """
    Platform tag (as defined in PEP 425), except that 'any' is None.
    """

    packages = List(Instance(Requirement))
    """
    List of dependencies for this egg
    """

    _epd_legacy_platform = NoneOrInstance(LegacyEPDPlatform)

    _metadata_version = Instance(MetadataVersion)

    @classmethod
    def _from_data(cls, data, epd_platform):
        args = data.copy()
        args[_TAG_METADATA_VERSION] = M(
            args.get(_TAG_METADATA_VERSION, _METADATA_DEFAULT_VERSION_STRING)
        )

        if epd_platform is None:
            _epd_legacy_platform = None
        else:
            _epd_legacy_platform = LegacyEPDPlatform(epd_platform)
        args["_epd_legacy_platform"] = _epd_legacy_platform

        args[_TAG_PACKAGES] = [
            Requirement.from_spec_string(s)
            for s in args.get(_TAG_PACKAGES, [])
        ]

        return cls(**args)

    @classmethod
    def from_egg(cls, path_or_file):
        sha256 = None
        if isinstance(path_or_file, string_types):
            if (
                may_be_in_platform_blacklist(path_or_file)
                or may_be_in_python_tag_blacklist(path_or_file)
            ):
                sha256 = compute_sha256(path_or_file)
        else:
            with _keep_position(path_or_file.fp):
                sha256 = compute_sha256(path_or_file.fp)
        return cls._from_egg(path_or_file, sha256)

    @classmethod
    def _from_egg(cls, path_or_file, sha256):
        def _create_spec_depend(zp):
            epd_platform_string = EGG_PLATFORM_BLACK_LIST.get(sha256)
            if epd_platform_string is None:
                epd_platform = None
            else:
                epd_platform = EPDPlatform.from_epd_string(epd_platform_string)

            try:
                spec_depend_string = zp.read(_SPEC_DEPEND_LOCATION).decode()
            except KeyError:
                msg = ("File {0!r} is not an Enthought egg (is missing {1})"
                       .format(path_or_file, _SPEC_DEPEND_LOCATION))
                raise MissingMetadata(msg)
            else:
                data, epd_platform = _normalized_info_from_string(
                    spec_depend_string, epd_platform, sha256
                )
                return cls._from_data(data, epd_platform)

        if isinstance(path_or_file, string_types):
            with zipfile2.ZipFile(path_or_file) as zp:
                return _create_spec_depend(zp)
        else:
            return _create_spec_depend(path_or_file)

    @classmethod
    def from_string(cls, spec_depend_string):
        data, epd_platform = _normalized_info_from_string(spec_depend_string)
        return cls._from_data(data, epd_platform)

    @property
    def arch(self):
        """
        Egg architecture.
        """
        if self._epd_legacy_platform is None:
            return None
        else:
            return self._epd_legacy_platform.arch._legacy_name

    @property
    def egg_name(self):
        """
        Full egg name (including .egg extension).
        """
        return egg_name(self.name, self.version, self.build)

    @property
    def osdist(self):
        if self._epd_legacy_platform is None:
            return None
        else:
            return self._epd_legacy_platform.osdist

    @property
    def platform(self):
        """
        The legacy platform name (sys.platform).
        """
        if self._epd_legacy_platform is None:
            return None
        else:
            return self._epd_legacy_platform.platform

    @property
    def metadata_version(self):
        return self._metadata_version

    @metadata_version.setter
    def metadata_version(self, value):
        self._metadata_version = value

    def _to_dict(self):
        raw_data = {
            _TAG_NAME: self.name,
            _TAG_VERSION: self.version,
            _TAG_BUILD: self.build,
            _TAG_ARCH: self.arch,
            _TAG_PLATFORM: self.platform,
            _TAG_OSDIST: self.osdist,
            _TAG_PACKAGES: [str(p) for p in self.packages],
            _TAG_PYTHON: self.python,
            _TAG_PYTHON_PEP425_TAG: self.python_tag,
            _TAG_ABI_PEP425_TAG: self.abi_tag,
            _TAG_PLATFORM_PEP425_TAG: self.platform_tag,
            _TAG_METADATA_VERSION: self.metadata_version
        }

        ret = {}
        for k, v in raw_data.items():
            if isinstance(v, string_types):
                v = str(v)
            ret[k] = v
        return ret

    def to_string(self):
        """
        Returns a string that is suitable for the depend file inside our
        legacy egg.
        """
        template = _METADATA_TEMPLATES.get(self.metadata_version, None)
        data = self._to_dict()

        # This is just to ensure the exact same string as the produced by the
        # legacy buildsystem
        if len(self.packages) == 0:
            data[_TAG_PACKAGES] = "[]"
        else:
            data[_TAG_PACKAGES] = "[\n{0}\n]". \
                format("\n".join("  '{0}',".format(p)
                       for p in self.packages))
        return template.format(**data)


class Dependencies(object):
    """ Object storing the various dependencies for an egg.

    Each attribute is a tuple of Requirement instances.
    """
    def __init__(self, runtime=None, build=None):
        self.runtime = runtime or ()
        self.build = runtime or ()


_TAG_RE = re.compile("""
    (?P<interpreter>(cp|pp|cpython|py))
    (?P<version>([\d_]+))
""", flags=re.VERBOSE)


def _python_tag_to_python(python_tag):
    # This converts only python version we currently intent to support in
    # metadata version 1.x.
    if python_tag is None:
        return None

    generic_exc = InvalidMetadataField('python_tag', python_tag)

    m = _TAG_RE.match(python_tag)
    if m is None:
        raise generic_exc
    else:
        d = m.groupdict()
        version = d["version"]
        if len(version) == 1:
            if version == "2":
                return "2.7"
            else:
                raise generic_exc
        elif len(version) == 2:
            return "{0}.{1}".format(version[0], version[1])
        else:
            raise generic_exc


def _metadata_version_to_tuple(metadata_version):
    """ Convert a metadata version string to a tuple for comparison."""
    return tuple(int(s) for s in metadata_version.split("."))


def _guess_abi_tag(platform, python_tag):
    assert python_tag is not None, "BUG, this function expects a python_tag"

    # For legacy (aka legacy spec version info < 1.3), we know that pyver
    # can only be one of "2.X" with X in (5, 6, 7).
    #
    # In those cases, the mapping (platform pyver) -> ABI is unambiguous,
    # as we only ever used one ABI for a given python version/platform.
    pyver = _python_tag_to_python(python_tag)
    return "cp{0}{1}m".format(pyver[0], pyver[2])


def _guess_platform_tag(platform):
    if platform is None:
        return None

    return platform.pep425_tag


def _normalized_info_from_string(spec_depend_string, epd_platform=None,
                                 sha256=None):
    """ Return a 'normalized' dictionary from the given spec/depend string.

    Note: the name value is NOT lower-cased, so that the egg filename may
    rebuilt from the data.
    """
    raw_data = parse_rawspec(spec_depend_string)

    data = {}
    for k in (_TAG_METADATA_VERSION,
              _TAG_NAME, _TAG_VERSION, _TAG_BUILD,
              _TAG_ARCH, _TAG_OSDIST, _TAG_PLATFORM,
              _TAG_PYTHON, _TAG_PACKAGES):
        data[k] = raw_data[k]

    epd_platform = epd_platform or _epd_platform_from_raw_spec(data)
    for k in (_TAG_ARCH, _TAG_PLATFORM, _TAG_OSDIST):
        data.pop(k)

    metadata_version = MetadataVersion.from_string(data[_TAG_METADATA_VERSION])

    python_tag = EGG_PYTHON_TAG_BLACK_LIST.get(sha256)
    if python_tag:
        data[_TAG_PYTHON_PEP425_TAG] = python_tag
    else:
        if metadata_version < M("1.2"):
            data[_TAG_PYTHON_PEP425_TAG] = _guess_python_tag(
                raw_data[_TAG_PYTHON]
            )
        else:
            data[_TAG_PYTHON_PEP425_TAG] = raw_data[_TAG_PYTHON_PEP425_TAG]

    if metadata_version < M("1.3"):
        python_tag = data[_TAG_PYTHON_PEP425_TAG]

        if python_tag is None:
            # No python tag, so should only be a "pure binary" egg, i.e.
            # an egg containing no python code and no python C extensions.
            abi = None
        elif epd_platform is None:
            # No platform, so should only be a "pure python" egg, i.e.
            # an egg containing no C extension.
            abi = None
        else:
            abi = _guess_abi_tag(epd_platform, python_tag)
        data[_TAG_ABI_PEP425_TAG] = abi
    else:
        data[_TAG_ABI_PEP425_TAG] = raw_data[_TAG_ABI_PEP425_TAG]

    if metadata_version < M("1.3"):
        if epd_platform is None:
            platform_tag = None
        else:
            platform_tag = _guess_platform_tag(epd_platform)
        data[_TAG_PLATFORM_PEP425_TAG] = platform_tag
    else:
        data[_TAG_PLATFORM_PEP425_TAG] = raw_data[_TAG_PLATFORM_PEP425_TAG]

    return data, epd_platform


[docs]class EggMetadata(object): """ Enthought egg metadata for format 1.x. """ HIGHEST_SUPPORTED_METADATA_VERSION = _METADATA_DEFAULT_VERSION """ Highest supported metadata version (as a MetadataVersion object). If the parsed metadata is higher, it will not be possible to write back the metadata. If the parsed metadata version is not compatible (different major version), then parsing will raise an UnsupportedMetadata exception as well. """ @classmethod
[docs] def from_egg(cls, path_or_file, strict=True): """ Create a EggMetadata instance from an existing Enthought egg. Parameters ---------- path: str or file-like object. If a string, understood as the path to the egg. Otherwise, understood as a zipfile-like object. strict: bool If True, will fail if metadata cannot be decoded correctly (e.g. unicode errors in EGG-INFO/PKG-INFO). If false, will ignore those errors, at the risk of data loss. """ sha256 = None if isinstance(path_or_file, string_types): if ( may_be_in_platform_blacklist(path_or_file) or may_be_in_pkg_info_blacklist(path_or_file) or may_be_in_python_tag_blacklist(path_or_file) ): sha256 = compute_sha256(path_or_file) else: with _keep_position(path_or_file.fp): sha256 = compute_sha256(path_or_file.fp) return cls._from_egg(path_or_file, sha256, strict)
@classmethod def _from_egg(cls, path_or_file, sha256, strict=True): def _read_summary(fp): summary_arcname = "EGG-INFO/spec/summary" try: summary = fp.read(summary_arcname) except KeyError: # the summary file may not exist for eggs built with # endist/repack summary = b"" return summary.decode("utf8") spec_depend = LegacySpecDepend._from_egg(path_or_file, sha256) if isinstance(path_or_file, string_types): with zipfile2.ZipFile(path_or_file) as fp: summary = _read_summary(fp) pkg_info_data = _read_pkg_info(fp) else: summary = _read_summary(path_or_file) pkg_info_data = _read_pkg_info(path_or_file) if pkg_info_data is None: pkg_info = None else: pkg_info = PackageInfo._from_egg(path_or_file, sha256, strict) return cls._from_spec_depend(spec_depend, pkg_info, summary) @classmethod def _from_spec_depend(cls, spec_depend, pkg_info, summary, metadata_version=None): raw_name = spec_depend.name version = EnpkgVersion.from_upstream_and_build(spec_depend.version, spec_depend.build) python_tag = spec_depend.python_tag abi_tag = spec_depend.abi_tag if spec_depend._epd_legacy_platform is None: platform = None else: platform_string = str(spec_depend._epd_legacy_platform) platform = EPDPlatform.from_epd_string(platform_string) dependencies = Dependencies( tuple(dep for dep in spec_depend.packages) ) metadata_version = metadata_version or spec_depend.metadata_version return cls(raw_name, version, platform, python_tag, abi_tag, dependencies, pkg_info, summary, metadata_version) @classmethod
[docs] def from_egg_metadata(cls, egg_metadata, **kw): """ Utility ctor to create a new EggMetadata instance from an existing one, potentially updating some metadata. Any keyword argument (except `egg_metadata`) is understood as an argument to EggMetadata.__init__. Parameters ---------- egg_metadata: EggMetadata """ passed_kw = {"raw_name": egg_metadata._raw_name} for k in ( "version", "platform", "python", "abi_tag", "pkg_info", "summary", "metadata_version" ): passed_kw[k] = getattr(egg_metadata, k) passed_kw["dependencies"] = Dependencies( egg_metadata.runtime_dependencies ) passed_kw.update(**kw) return cls(**passed_kw)
def __init__(self, raw_name, version, platform, python, abi_tag, dependencies, pkg_info, summary, metadata_version=None): """ EggMetadata instances encompass Enthought egg metadata. Note: the constructor is considered private, please use one of the from_* class methods. Parameters ---------- raw_name: str The 'raw' name, i.e. the name value in spec/depend. version: EnpkgVersion The full version platform: EPDPlatform An EPDPlatform instance, or None for cross-platform eggs python: Python The python implementation abi_tag: str The ABI tag, e.g. 'cp27m'. May be None. dependencies: Dependencies A Dependencies instance. pkg_info: PackageInfo or None Instance modeling the PKG-INFO content of the egg. summary: str The summary. Models the string in EGG-INFO/spec/summary. May be empty. """ self._raw_name = raw_name self.version = version """ The version, as an EnpkgVersion instance.""" self.platform = platform """ The platform, as a Platform instance.""" if isinstance(python, string_types): python = PythonImplementation.from_string(python) self.python = python """ The python implementation.""" self.abi_tag = abi_tag """ The ABI tag, following the PEP425 format, except that no ABI is sorted as None.""" self.runtime_dependencies = tuple(dependencies.runtime) """ List of runtime dependencies (as strings).""" self.metadata_version = metadata_version or _METADATA_DEFAULT_VERSION """ The version format of the underlying metadata.""" self.pkg_info = pkg_info """ A PackageInfo instance modeling the underlying PKG-INFO. May be None for eggs without an PKG-INFO file.""" self.summary = summary """ The summary string.""" @property def abi_tag_string(self): if self.abi_tag is None: return 'none' else: return self.abi_tag @property def build(self): """ The build number.""" return self.version.build @property def egg_basename(self): """ The egg "base name", i.e. the name part of the egg filename.""" return self._raw_name @property def egg_name(self): """ The egg filename.""" return self._spec_depend.egg_name @property def is_strictly_supported(self): """ Returns True if the given metadata_version is fully supported. A metadata_version is fully supported iff: - metadata_version.major == EggMetadata.HIGHEST_SUPPORTED_METADATA_VERSION.major - and metadata_version.minor <= EggMetadata.HIGHEST_SUPPORTED_METADATA_VERSION.minor """ max_supported = EggMetadata.HIGHEST_SUPPORTED_METADATA_VERSION return ( _are_compatible(self.metadata_version, max_supported) and self.metadata_version.minor <= max_supported.minor ) @property def kind(self): return "egg" @property def name(self): """ The package name.""" return self._raw_name.lower().replace("-", "_") @property def platform_tag(self): """ Platform tag following PEP425, except that no platform is represented as None and not 'any'.""" if self.platform is None: return None else: return self.platform.pep425_tag @property def platform_tag_string(self): if self.platform_tag is None: return 'any' else: return self.platform_tag @property def python_tag(self): if self.python is None: return None else: return self.python.pep425_tag @property def python_tag_string(self): if self.python_tag is None: # an extension of PEP 425, to signify the egg will work on any # python version (mostly non-python eggs) return 'none' else: return self.python_tag @property def spec_depend_string(self): return self._spec_depend.to_string() @property def upstream_version(self): return str(self.version.upstream) @property def _python(self): if self.python is None: return None else: return "{0}.{1}".format(self.python.major, self.python.minor) @property def _spec_depend(self): if not self.is_strictly_supported: msg = "Cannot write back metadata with unsupported version {0!r}" raise UnsupportedMetadata( self.metadata_version, msg.format(str(self.metadata_version)) ) if self.platform is None: _epd_legacy_platform = None else: _epd_legacy_platform = LegacyEPDPlatform(self.platform) args = { "name": self._raw_name, "version": self.upstream_version, "build": self.build, "python": self._python, "python_tag": self.python_tag, "abi_tag": self.abi_tag, "platform_tag": self.platform_tag, "packages": [p for p in self.runtime_dependencies], "_epd_legacy_platform": _epd_legacy_platform, "_metadata_version": self.metadata_version, } return LegacySpecDepend(**args)