Exemplo n.º 1
0
class Package(PackageBaseResourceWrapper):
    """A package.

    Note:
        Do not instantiate this class directly, instead use the function
        `iter_packages` or `PackageFamily.iter_packages`.
    """
    keys = schema_keys(package_schema)

    def __init__(self, resource):
        _check_class(resource, PackageResource)
        super(Package, self).__init__(resource)

    @cached_property
    def qualified_name(self):
        """Get the qualified name of the package.

        Returns:
            str: Name of the package with version, eg "maya-2016.1".
        """
        o = VersionedObject.construct(self.name, self.version)
        return str(o)

    @cached_property
    def parent(self):
        """Get the parent package family.

        Returns:
            `PackageFamily`.
        """
        repo = self.resource._repository
        family = repo.get_parent_package_family(self.resource)
        return PackageFamily(family) if family else None

    @cached_property
    def num_variants(self):
        return len(self.data.get("variants", []))

    def iter_variants(self):
        """Iterate over the variants within this package, in index order.

        Returns:
            `Variant` iterator.
        """
        repo = self.resource._repository
        for variant in repo.iter_variants(self.resource):
            yield Variant(variant)

    def get_variant(self, index=None):
        """Get the variant with the associated index.

        Returns:
            `Variant` object, or None if no variant with the given index exists.
        """
        for variant in self.iter_variants():
            if variant.index == index:
                return variant
Exemplo n.º 2
0
class PackageFamily(PackageRepositoryResourceWrapper):
    """A package family.

    Note:
        Do not instantiate this class directly, instead use the function
        `iter_package_families`.
    """
    keys = schema_keys(package_family_schema)

    def __init__(self, resource):
        _check_class(resource, PackageFamilyResource)
        super(PackageFamily, self).__init__(resource)

    def iter_packages(self):
        """Iterate over the packages within this family, in no particular order.

        Returns:
            `Package` iterator.
        """
        for package in self.repository.iter_packages(self.resource):
            yield Package(package)
Exemplo n.º 3
0
class Variant(PackageBaseResourceWrapper):
    """A package variant.

    Note:
        Do not instantiate this class directly, instead use the function
        `Package.iter_variants`.
    """
    keys = schema_keys(variant_schema)
    keys.update(["index", "root", "subpath"])

    def __init__(self, resource):
        _check_class(resource, VariantResource)
        super(Variant, self).__init__(resource)

    @cached_property
    def qualified_package_name(self):
        o = VersionedObject.construct(self.name, self.version)
        return str(o)

    @cached_property
    def qualified_name(self):
        """Get the qualified name of the variant.

        Returns:
            str: Name of the variant with version and index, eg "maya-2016.1[1]".
        """
        idxstr = '' if self.index is None else str(self.index)
        return "%s[%s]" % (self.qualified_package_name, idxstr)

    @cached_property
    def parent(self):
        """Get the parent package.

        Returns:
            `Package`.
        """
        repo = self.resource._repository
        package = repo.get_parent_package(self.resource)
        return Package(package)

    def get_requires(self, build_requires=False, private_build_requires=False):
        """Get the requirements of the variant.

        Args:
            build_requires (bool): If True, include build requirements.
            private_build_requires (bool): If True, include private build
                requirements.

        Returns:
            List of `Requirement` objects.
        """
        requires = self.requires or []
        if build_requires:
            requires = requires + (self.build_requires or [])
        if private_build_requires:
            requires = requires + (self.private_build_requires or [])
        return requires

    def install(self, path, dry_run=False, overrides=None):
        """Install this variant into another package repository.

        If the package already exists, this variant will be correctly merged
        into the package. If the variant already exists in this package, the
        existing variant is returned.

        Args:
            path (str): Path to destination package repository.
            dry_run (bool): If True, do not actually install the variant. In this
                mode, a `Variant` instance is only returned if the equivalent
                variant already exists in this repository; otherwise, None is
                returned.
            overrides (dict): Use this to change or add attributes to the
                installed variant.

        Returns:
            `Variant` object - the (existing or newly created) variant in the
            specified repository. If `dry_run` is True, None may be returned.
        """
        repo = package_repository_manager.get_repository(path)
        resource = repo.install_variant(self.resource,
                                        dry_run=dry_run,
                                        overrides=overrides)
        if resource is None:
            return None
        elif resource is self.resource:
            return self
        else:
            return Variant(resource)
Exemplo n.º 4
0
class Package(PackageBaseResourceWrapper):
    """A package.

    Note:
        Do not instantiate this class directly, instead use the function
        `iter_packages` or `PackageFamily.iter_packages`.
    """
    keys = schema_keys(package_schema)

    def __init__(self, resource):
        _check_class(resource, PackageResource)
        super(Package, self).__init__(resource)

    # arbitrary keys
    def __getattr__(self, name):
        if name in self.data:
            return self.data[name]
        else:
            raise AttributeError("Package instance has no attribute '%s'" %
                                 name)

    def arbitrary_keys(self):
        """Get the arbitrary keys present in this package.

        These are any keys not in the standard list ('name', 'version' etc).

        Returns:
            set of str: Arbitrary keys.
        """
        return set(self.data.keys()) - set(self.keys)

    @cached_property
    def qualified_name(self):
        """Get the qualified name of the package.

        Returns:
            str: Name of the package with version, eg "maya-2016.1".
        """
        o = VersionedObject.construct(self.name, self.version)
        return str(o)

    @cached_property
    def parent(self):
        """Get the parent package family.

        Returns:
            `PackageFamily`.
        """
        repo = self.resource._repository
        family = repo.get_parent_package_family(self.resource)
        return PackageFamily(family) if family else None

    @cached_property
    def num_variants(self):
        return len(self.data.get("variants", []))

    def iter_variants(self):
        """Iterate over the variants within this package, in index order.

        Returns:
            `Variant` iterator.
        """
        repo = self.resource._repository
        for variant in repo.iter_variants(self.resource):
            yield Variant(variant)

    def get_variant(self, index=None):
        """Get the variant with the associated index.

        Returns:
            `Variant` object, or None if no variant with the given index exists.
        """
        for variant in self.iter_variants():
            if variant.index == index:
                return variant
Exemplo n.º 5
0
class VariantResourceHelper(VariantResource):
    """Helper class for implementing variants that inherit properties from their
    parent package.

    Since a variant overlaps so much with a package, here we use the forwarding
    metaclass to forward our parent package's attributes onto ourself (with some
    exceptions - eg 'variants', 'requires'). This is a common enough pattern
    that it's supplied here for other repository plugins to use.
    """
    class _Metas(AttributeForwardMeta, LazyAttributeMeta): pass
    __metaclass__ = _Metas

    # Note: lazy key validation doesn't happen in this class, it just fowards on
    # attributes from the package. But LazyAttributeMeta does still use this
    # schema to create other class attributes, such as `validate_data`.
    schema = variant_schema

    # forward Package attributes onto ourself
    keys = schema_keys(package_schema) - set(["variants"])

    def _uri(self):
        index = self.index
        idxstr = '' if index is None else str(index)
        return "%s[%s]" % (self.parent.uri, idxstr)

    def _subpath(self):
        if self.index is None:
            return None
        else:
            reqs = self.variant_requires
            dirs = [x.safe_str() for x in reqs]
            subpath = os.path.join(*dirs)
            return subpath

    def _root(self):
        if self.base is None:
            return None
        elif self.index is None:
            return self.base
        else:
            root = os.path.join(self.base, self.subpath)
            return root

    @cached_property
    def variant_requires(self):
        index = self.index
        if index is None:
            return []
        else:
            try:
                return self.parent.variants[index] or []
            except (IndexError, TypeError):
                raise ResourceError(
                    "Unexpected error - variant %s cannot be found in its "
                    "parent package %s" % (self.uri, self.parent.uri))

    @property
    def wrapped(self):  # forward Package attributes onto ourself
        return self.parent

    def _load(self):
        # doesn't have its own data, forwards on from parent instead
        return None
Exemplo n.º 6
0
    Optional('uuid'):                   basestring,
    Optional('config'):                 dict,
    Optional('tools'):                  [basestring],
    Optional('help'):                   help_schema,

    Optional('pre_commands'):           _commands_schema,
    Optional('commands'):               _commands_schema,
    Optional('post_commands'):          _commands_schema,

    Optional('custom'):                 dict,

    Optional(basestring):               object  # allows deprecated fields
})


package_schema_keys = schema_keys(package_schema)


class PackageMaker(AttrDictWrapper):
    """Utility class for creating packages."""
    def __init__(self, name, data=None):
        """Create a package maker.

        Args:
            name (str): Package name.
        """
        super(PackageMaker, self).__init__(data)
        self.name = name

    def get_package(self):
        """Create the analogous package.
Exemplo n.º 7
0
class Variant(PackageBaseResourceWrapper):
    """A package variant.

    Note:
        Do not instantiate this class directly, instead use the function
        `Package.iter_variants`.
    """
    keys = schema_keys(variant_schema)
    keys.update(["index", "root", "subpath"])

    # See comment in `Package`
    is_package = False
    is_variant = True

    def __init__(self, resource, context=None, parent=None):
        _check_class(resource, VariantResource)
        super(Variant, self).__init__(resource, context)
        self._parent = parent

    # arbitrary keys
    def __getattr__(self, name):
        try:
            return self.parent.__getattr__(name)
        except AttributeError:
            raise AttributeError("Variant instance has no attribute '%s'" %
                                 name)

    def arbitrary_keys(self):
        return self.parent.arbitrary_keys()

    @cached_property
    def qualified_package_name(self):
        o = VersionedObject.construct(self.name, self.version)
        return str(o)

    @cached_property
    def qualified_name(self):
        """Get the qualified name of the variant.

        Returns:
            str: Name of the variant with version and index, eg "maya-2016.1[1]".
        """
        idxstr = '' if self.index is None else str(self.index)
        return "%s[%s]" % (self.qualified_package_name, idxstr)

    @cached_property
    def parent(self):
        """Get the parent package.

        Returns:
            `Package`.
        """
        if self._parent is not None:
            return self._parent

        try:
            package = self.repository.get_parent_package(self.resource)
            self._parent = Package(package, context=self.context)
        except AttributeError as e:
            reraise(e, ValueError)

        return self._parent

    @property
    def variant_requires(self):
        """Get the subset of requirements specific to this variant.

        Returns:
            List of `Requirement` objects.
        """
        if self.index is None:
            return []
        else:
            return self.parent.variants[self.index] or []

    @property
    def requires(self):
        """Get variant requirements.

        This is a concatenation of the package requirements and those of this
        specific variant.

        Returns:
            List of `Requirement` objects.
        """
        return ((self.parent.requires or []) + self.variant_requires)

    def get_requires(self, build_requires=False, private_build_requires=False):
        """Get the requirements of the variant.

        Args:
            build_requires (bool): If True, include build requirements.
            private_build_requires (bool): If True, include private build
                requirements.

        Returns:
            List of `Requirement` objects.
        """
        requires = self.requires or []

        if build_requires:
            requires = requires + (self.build_requires or [])
        if private_build_requires:
            requires = requires + (self.private_build_requires or [])

        return requires

    def install(self, path, dry_run=False, overrides=None):
        """Install this variant into another package repository.

        If the package already exists, this variant will be correctly merged
        into the package. If the variant already exists in this package, the
        existing variant is returned.

        Args:
            path (str): Path to destination package repository.
            dry_run (bool): If True, do not actually install the variant. In this
                mode, a `Variant` instance is only returned if the equivalent
                variant already exists in this repository; otherwise, None is
                returned.
            overrides (dict): Use this to change or add attributes to the
                installed variant.

        Returns:
            `Variant` object - the (existing or newly created) variant in the
            specified repository. If `dry_run` is True, None may be returned.
        """
        repo = package_repository_manager.get_repository(path)
        resource = repo.install_variant(self.resource,
                                        dry_run=dry_run,
                                        overrides=overrides)
        if resource is None:
            return None
        elif resource is self.resource:
            return self
        else:
            return Variant(resource)

    @property
    def _non_shortlinked_subpath(self):
        return self.resource._subpath(ignore_shortlinks=True)
Exemplo n.º 8
0
class Package(PackageBaseResourceWrapper):
    """A package.

    Note:
        Do not instantiate this class directly, instead use the function
        `iter_packages` or `PackageFamily.iter_packages`.
    """
    keys = schema_keys(package_schema)

    # This is to allow for a simple check like 'this.is_package' in late-bound
    # funcs, where 'this' may be a package or variant.
    #
    is_package = True
    is_variant = False

    def __init__(self, resource, context=None):
        _check_class(resource, PackageResource)
        super(Package, self).__init__(resource, context)

    # arbitrary keys
    def __getattr__(self, name):
        if name in self.data:
            value = self.data[name]
            return self._wrap_forwarded(name, value)
        else:
            raise AttributeError("Package instance has no attribute '%s'" %
                                 name)

    def arbitrary_keys(self):
        """Get the arbitrary keys present in this package.

        These are any keys not in the standard list ('name', 'version' etc).

        Returns:
            set of str: Arbitrary keys.
        """
        return set(self.data.keys()) - set(self.keys)

    @cached_property
    def qualified_name(self):
        """Get the qualified name of the package.

        Returns:
            str: Name of the package with version, eg "maya-2016.1".
        """
        o = VersionedObject.construct(self.name, self.version)
        return str(o)

    def as_exact_requirement(self):
        """Get the package, as an exact requirement string.

        Returns:
            Equivalent requirement string, eg "maya==2016.1"
        """
        o = VersionedObject.construct(self.name, self.version)
        return o.as_exact_requirement()

    @cached_property
    def parent(self):
        """Get the parent package family.

        Returns:
            `PackageFamily`.
        """
        family = self.repository.get_parent_package_family(self.resource)
        return PackageFamily(family) if family else None

    @cached_property
    def num_variants(self):
        return len(self.data.get("variants", []))

    @property
    def is_relocatable(self):
        """True if the package and its payload is safe to copy.
        """
        if self.relocatable is not None:
            return self.relocatable

        if config.default_relocatable_per_repository:
            value = config.default_relocatable_per_repository.get(
                self.repository.location)
            if value is not None:
                return value

        if config.default_relocatable_per_package:
            value = config.default_relocatable_per_package.get(self.name)
            if value is not None:
                return value

        return config.default_relocatable

    @property
    def is_cachable(self):
        """True if the package and its payload is safe to cache locally.
        """
        if self.cachable is not None:
            return self.cachable

        if config.default_cachable_per_repository:
            # TODO: The location of filesystem repository is canonical path,
            #   so if the path in `default_cachable_per_repository` isn't
            #   canonical, this may return false value e.g. on Windows.
            value = config.default_cachable_per_repository.get(
                self.repository.location)
            if value is not None:
                return value

        if config.default_cachable_per_package:
            value = config.default_cachable_per_package.get(self.name)
            if value is not None:
                return value

        if config.default_cachable is not None:
            return config.default_cachable

        return self.is_relocatable

    def iter_variants(self):
        """Iterate over the variants within this package, in index order.

        Returns:
            `Variant` iterator.
        """
        for variant in self.repository.iter_variants(self.resource):
            yield Variant(variant, context=self.context, parent=self)

    def get_variant(self, index=None):
        """Get the variant with the associated index.

        Returns:
            `Variant` object, or None if no variant with the given index exists.
        """
        for variant in self.iter_variants():
            if variant.index == index:
                return variant
Exemplo n.º 9
0
class VariantResourceHelper(six.with_metaclass(_Metas, VariantResource)):
    """Helper class for implementing variants that inherit properties from their
    parent package.

    Since a variant overlaps so much with a package, here we use the forwarding
    metaclass to forward our parent package's attributes onto ourself (with some
    exceptions - eg 'variants', 'requires'). This is a common enough pattern
    that it's supplied here for other repository plugins to use.
    """

    # Note: lazy key validation doesn't happen in this class, it just fowards on
    # attributes from the package. But LazyAttributeMeta does still use this
    # schema to create other class attributes, such as `validate_data`.
    schema = variant_schema

    # forward Package attributes onto ourself
    keys = schema_keys(package_schema) - set(["variants"])

    def _uri(self):
        index = self.index
        idxstr = '' if index is None else str(index)
        return "%s[%s]" % (self.parent.uri, idxstr)

    def _subpath(self, ignore_shortlinks=False):
        if self.index is None:
            return None

        if self.parent.hashed_variants:
            vars_str = str(list(map(str, self.variant_requires)))
            h = sha1(vars_str.encode("utf8"))
            hashdir = h.hexdigest()

            if (not ignore_shortlinks) and \
                    config.use_variant_shortlinks and \
                    self.base is not None:

                # search for matching shortlink and use that
                path = os.path.join(self.base,
                                    config.variant_shortlinks_dirname)

                if os.path.exists(path):
                    actual_root = os.path.join(self.base, hashdir)
                    linkname = find_matching_symlink(path, actual_root)

                    if linkname:
                        return os.path.join(config.variant_shortlinks_dirname,
                                            linkname)

            return hashdir
        else:
            dirs = [x.safe_str() for x in self.variant_requires]
            subpath = os.path.join(*dirs)
            return subpath

    def _root(self, ignore_shortlinks=False):
        if self.base is None:
            return None
        elif self.index is None:
            return self.base
        else:
            subpath = self._subpath(ignore_shortlinks=ignore_shortlinks)
            root = os.path.join(self.base, subpath)
            return root

    @cached_property
    def variant_requires(self):
        index = self.index
        if index is None:
            return []
        else:
            try:
                return self.parent.variants[index] or []
            except (IndexError, TypeError):
                raise ResourceError(
                    "Unexpected error - variant %s cannot be found in its "
                    "parent package %s" % (self.uri, self.parent.uri))

    @property
    def wrapped(self):  # forward Package attributes onto ourself
        return self.parent

    def _load(self):
        # doesn't have its own data, forwards on from parent instead
        return None