Source code for okonomiyaki.platforms.epd_platform
from __future__ import absolute_import
import re
import warnings
import six
from attr import attributes, attr
from attr.validators import instance_of
from ..errors import OkonomiyakiError
from ._arch import Arch, ArchitectureKind
from ._platform import OSKind, FamilyKind, NameKind, Platform
# the string used in EGG-INFO/spec/depend. Only used during normalization
# operations.
_X86_64_LEGACY_SPEC = "amd64"
# Those lists are redundant with legacy spec. We check the consistency in our
# unit-tests
_ARCHBITS_TO_ARCH = {
"32": ArchitectureKind.x86.name,
"64": _X86_64_LEGACY_SPEC,
ArchitectureKind.x86: ArchitectureKind.x86.name,
ArchitectureKind.x86_64: _X86_64_LEGACY_SPEC,
}
X86 = Arch(ArchitectureKind.x86)
X86_64 = Arch(ArchitectureKind.x86_64)
PLATFORM_NAMES = (
"osx",
"rh3",
"rh5",
"rh6",
"rh7",
"sol",
"win",
)
EPD_PLATFORM_SHORT_NAMES = (
"osx-32",
"osx-64",
"rh3-32",
"rh3-64",
"rh5-32",
"rh5-64",
"rh6-64",
"rh7-64",
"sol-32",
"sol-64",
"win-32",
"win-64",
)
VALID_PLATFORMS_FILTER = PLATFORM_NAMES + ("all", "rh",)
_EPD_PLATFORM_STRING_RE = re.compile("""
^
(?P<os>[^-_]+)
[_-]
(?P<arch>[^-]+)
$
""", flags=re.VERBOSE)
_LINUX_TAG_R = re.compile("^linux_(?P<arch>\S+)$")
_MACOSX_TAG_R = re.compile("^macosx_([^_]+)_([^_]+)_(?P<arch>\S+)$")
_WINDOWS_TAG_R = re.compile("^win_*(?P<arch>\S+)$")
_ANY_PLATFORM_STRING = u'any'
def platform_validator():
def wrapper(inst, attr, value):
instance_of(Platform)(inst, attr, value)
if not _is_supported(value):
raise OkonomiyakiError(
"Platform {0} not supported".format(value)
)
return wrapper
[docs]@six.python_2_unicode_compatible
@attributes(frozen=True)
class EPDPlatform(object):
"""
An sane Canopy/EPD platform representation.
Example::
epd_platform = EPDPlatform.from_epd_string("rh5-32")
assert epd.name == "rh5"
assert epd.arch_bits == "32"
assert epd.arch == "x86"
"""
platform = attr(validator=platform_validator())
"""
Main name of the platform (e.g. 'rh5')
"""
@staticmethod
def pep425_tag_string(platform):
if platform is None:
return _ANY_PLATFORM_STRING
else:
return platform.pep425_tag
[docs] @classmethod
def from_running_python(cls):
"""
Attempt to create an EPDPlatform instance by guessing the running
python. May raise an OkonomiyakiError exception
"""
return cls(Platform.from_running_python())
[docs] @classmethod
def from_running_system(cls, arch_name=None):
"""
Attempt to create an EPDPlatform instance by guessing the running
platform. May raise an OkonomiyakiError exception
Parameters
----------
arch_name: str, None
If given, must be a valid architecture string (e.g. 'x86'). If
None, will be guessed from the running platform.
"""
if arch_name is not None:
arch = Arch.from_name(arch_name)
else:
arch = Arch.from_running_system()
return _guess_epd_platform(arch)
[docs] @classmethod
def from_string(cls, s):
"""
Create a new instance from an epd platform string (e.g. 'win-32')
Note: new, more explicit architecture names are also supported, e.g.
'win-x86' or 'win-x86_64' are supported.
"""
m = _EPD_PLATFORM_STRING_RE.match(s)
if m is None:
raise OkonomiyakiError("Invalid epd string: {0}".format(s))
else:
d = m.groupdict()
platform_name = d["os"]
arch_bits = d["arch"]
if arch_bits not in _ARCHBITS_TO_ARCH:
arch = machine = Arch.from_name(arch_bits)
else:
arch_name = _ARCHBITS_TO_ARCH[arch_bits]
arch = machine = Arch.from_name(arch_name)
os, name, family, release = _epd_name_to_quadruplet(platform_name)
platform = Platform(os, name, family, release, arch, machine)
return cls(platform)
[docs] @classmethod
def from_epd_string(cls, s):
"""
Create a new instance from an epd platform string (e.g. 'win-32')
DEPRECATED: use from_string instead.
"""
warnings.warn(
"Deprecated: use EPDPlatform.from_string instead",
DeprecationWarning
)
return cls.from_string(s)
@classmethod
def _from_spec_depend_data(cls, platform, osdist, arch_name):
msg = ("Unrecognized platform/osdist combination: {0!r}/{1!r}"
.format(platform, osdist))
arch = Arch.from_name(arch_name)
if platform == "darwin":
epd_name = "osx"
elif platform == "win32":
epd_name = "win"
elif platform.startswith("linux"):
if osdist == "RedHat_3":
epd_name = "rh3"
elif osdist in (None, "RedHat_5"):
epd_name = "rh5"
elif osdist == "RedHat_6":
epd_name = "rh6"
elif osdist == "RedHat_7":
epd_name = "rh7"
else:
raise ValueError(msg)
else:
raise ValueError(msg)
return cls.from_epd_string(u"{0}-{1}".format(epd_name, arch._arch_bits))
@classmethod
def _from_platform_tag(cls, platform_tag):
"""
Attempt to create an EPDPlatform instance from a PEP 425 platform tag.
"""
if platform_tag is None or platform_tag == _ANY_PLATFORM_STRING:
raise ValueError(
"Invalid platform_tag for platform: '{}'".format(platform_tag)
)
else:
if platform_tag.startswith("linux"):
m = _LINUX_TAG_R.match(platform_tag)
assert m, platform_tag
arch_string = m.group("arch")
epd_string = u"rh5_" + str(Arch.from_name(arch_string))
elif platform_tag.startswith("macosx"):
m = _MACOSX_TAG_R.match(platform_tag)
assert m, platform_tag
arch_string = m.group("arch")
epd_string = u"osx_" + str(Arch.from_name(arch_string))
elif platform_tag.startswith("win"):
m = _WINDOWS_TAG_R.match(platform_tag)
assert m, platform_tag
arch_string = m.group("arch")
if arch_string == "32":
epd_string = u"win_i386"
else:
epd_string = u"win_" + str(Arch.from_name(arch_string))
else:
raise NotImplementedError(
"Unsupported platform '{0}'".format(platform_tag)
)
return cls.from_epd_string(epd_string)
@property
def arch(self):
return self.platform.arch
@property
def arch_bits(self):
"""
The number of bits (as a string) of this epd platform.
"""
return self.arch._arch_bits
@property
def pep425_tag(self):
msg = "Cannot guess platform tag for platform {0!r}"
if self.platform.os_kind == OSKind.darwin:
if self.platform.arch == X86:
return u"macosx_10_6_i386"
elif self.platform.arch == X86_64:
return u"macosx_10_6_x86_64"
else:
raise OkonomiyakiError(msg.format(self.platform))
elif self.platform.os_kind == OSKind.linux:
if self.platform.arch == X86:
return u"linux_i686"
elif self.platform.arch == X86_64:
return u"linux_x86_64"
else:
raise OkonomiyakiError(msg.format(self.platform))
elif self.platform.os_kind == OSKind.windows:
if self.platform.arch == X86:
return u"win32"
elif self.platform.arch == X86_64:
return u"win_amd64"
else:
raise OkonomiyakiError(msg.format(self.platform))
else:
raise OkonomiyakiError(msg.format(self.platform))
@property
def platform_name(self):
os_kind = self.platform.os_kind
if os_kind == OSKind.windows:
return u"win"
elif os_kind == OSKind.darwin:
return u"osx"
elif os_kind == OSKind.linux:
family_kind = self.platform.family_kind
release = self.platform.release
if family_kind == FamilyKind.rhel:
parts = release.split(".")
if parts[0] == "3":
base = u"rh3"
elif parts[0] == "5":
base = u"rh5"
elif parts[0] == "6":
base = u"rh6"
elif parts[0] == "7":
base = u"rh7"
else:
msg = ("Unsupported rhel release: {0!r}".format(release))
raise OkonomiyakiError(msg)
return base
else:
msg = "Unsupported distribution: {0!r}".format(family_kind)
raise OkonomiyakiError(msg)
elif os_kind == OSKind.solaris:
return u"sol"
else:
msg = "Unsupported OS: {0!r}".format(self.platform.name)
raise OkonomiyakiError(msg)
@property
def short(self):
return u"{0}-{1}".format(self.platform_name, self.arch_bits)
def __str__(self):
return u"{0.platform_name}_{0.arch}".format(self)
def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
return self.platform == other.platform
def __ne__(self, other):
if not isinstance(other, self.__class__):
return True
return not (self.platform == other.platform)
def __hash__(self):
return hash(self.platform)
def applies(platform_string, to='current'):
""" Returns True if the given platform string applies to the platform
specified by 'to'."""
def _parse_component(component):
component = component.strip()
parts = component.split("-")
if len(parts) == 1:
if parts[0] in VALID_PLATFORMS_FILTER:
return parts[0], None
elif parts[0] in _ARCHBITS_TO_ARCH:
return "all", parts[0]
else:
raise ValueError(
"Invalid filter string: '{}'".format(component)
)
elif len(parts) == 2:
if (
parts[0] not in VALID_PLATFORMS_FILTER or
parts[1] not in _ARCHBITS_TO_ARCH
):
raise ValueError(
"Invalid filter string: '{}'".format(component)
)
return parts[0], parts[1]
else:
raise ValueError(
"Invalid filter string: '{}'".format(component)
)
def _are_compatible(short_left, short_right):
return short_left == short_right or \
short_left == "rh" and short_right.startswith("rh") \
or short_right == "rh" and short_left.startswith("rh") \
or short_left == "all"
if isinstance(to, str):
if to == 'current':
full = EPDPlatform.from_running_system()
to_platform = full.platform_name
to_arch_bits = full.arch_bits
elif '-' in to:
full = EPDPlatform.from_epd_string(to)
to_platform = full.platform_name
to_arch_bits = full.arch_bits
else:
if not (to in PLATFORM_NAMES or to == 'rh'):
raise ValueError("Invalid 'to' argument: {0!r}".format(to))
to_platform = to
to_arch_bits = None
else:
to_platform = to.platform_name
to_arch_bits = to.arch_bits
conditions = []
platform_string = platform_string.strip()
if platform_string.startswith("!"):
invert = True
platform_string = platform_string[1:]
else:
invert = False
platform_strings = [s for s in platform_string.split(",")]
for platform_string in platform_strings:
short, bits = _parse_component(platform_string)
if _are_compatible(short, to_platform):
if bits is None:
conditions.append(True)
else:
conditions.append(bits == to_arch_bits or to_arch_bits is None)
else:
conditions.append(False)
if invert:
return not any(conditions)
else:
return any(conditions)
def _epd_name_to_quadruplet(name):
if name == "rh7":
return (OSKind.linux, NameKind.rhel, FamilyKind.rhel, "7.1")
if name == "rh6":
return (OSKind.linux, NameKind.rhel, FamilyKind.rhel, "6.5")
elif name == "rh5":
return (OSKind.linux, NameKind.rhel, FamilyKind.rhel, "5.8")
elif name == "rh3":
return (OSKind.linux, NameKind.rhel, FamilyKind.rhel, "3.8")
elif name == "osx":
return (OSKind.darwin, NameKind.mac_os_x, FamilyKind.mac_os_x, "10.6")
elif name == "sol":
return (OSKind.solaris, NameKind.solaris, FamilyKind.solaris, "")
elif name == "win":
return (OSKind.windows, NameKind.windows, FamilyKind.windows, "")
else:
msg = "Invalid epd platform string name: {0!r}".format(name)
raise OkonomiyakiError(msg)
def _guess_epd_platform(arch=None):
if arch is None:
arch = Arch.from_running_python()
platform = Platform.from_running_system(str(arch))
return EPDPlatform(platform)
def _is_supported(platform):
arch_and_machine_are_intel = (
platform.arch in (X86, X86_64)
and platform.machine in (X86, X86_64)
)
if platform.os_kind == OSKind.windows:
return arch_and_machine_are_intel
if platform.os_kind == OSKind.darwin:
return arch_and_machine_are_intel
if platform.os_kind == OSKind.solaris:
return arch_and_machine_are_intel
if platform.os_kind == OSKind.linux:
if platform.family_kind != FamilyKind.rhel:
return False
parts = platform.release.split(".")
return parts[0] in ("3", "5", "6", "7") \
and arch_and_machine_are_intel
return False