__init__.py revision 2639
1516N/A#!/usr/bin/python
49N/A#
49N/A# CDDL HEADER START
49N/A#
49N/A# The contents of this file are subject to the terms of the
49N/A# Common Development and Distribution License (the "License").
49N/A# You may not use this file except in compliance with the License.
49N/A#
49N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
49N/A# or http://www.opensolaris.org/os/licensing.
49N/A# See the License for the specific language governing permissions
49N/A# and limitations under the License.
49N/A#
49N/A# When distributing Covered Code, include this CDDL HEADER in each
49N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
49N/A# If applicable, add the following below this CDDL HEADER, with the
49N/A# fields enclosed by brackets "[]" replaced with your own identifying
49N/A# information: Portions Copyright [yyyy] [name of copyright owner]
49N/A#
49N/A# CDDL HEADER END
49N/A#
49N/A
49N/A#
3339N/A# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
49N/A#
49N/A
49N/A"""
49N/Apackage containing packaging action (file type) modules
49N/A
49N/AThis package contains modules describing packaging actions, or file types. The
49N/Aactions are dynamically discovered, so that new modules can be placed in this
49N/Apackage directory and they'll just be picked up. The current package contents
49N/Acan be seen in the section "PACKAGE CONTENTS", below.
49N/A
2843N/AThis package has one data member: "types". This is a dictionary which maps the
2843N/Aaction names to the classes that represent them.
2843N/A
2843N/AThis package also has one function: "fromstr", which creates an action instance
2843N/Abased on a str() representation of an action.
2843N/A"""
51N/A
51N/Aimport inspect
51N/Aimport os
49N/A
49N/A# All modules in this package (all python files except __init__.py with their
49N/A# extensions stripped off).
49N/A__all__ = [
49N/A f[:-3]
49N/A for f in os.listdir(__path__[0])
49N/A if f.endswith(".py") and f != "__init__.py"
49N/A]
49N/A
49N/A# A dictionary of all the types in this package, mapping to the classes that
49N/A# define them.
49N/Atypes = {}
49N/Afor modname in __all__:
49N/A module = __import__("%s.%s" % (__name__, modname),
49N/A globals(), locals(), [modname])
49N/A
2843N/A nvlist = inspect.getmembers(module, inspect.isclass)
2843N/A
2843N/A # Pull the class objects out of nvlist, keeping only those that are
2843N/A # actually defined in this package.
2843N/A classes = [
2843N/A c[1]
49N/A for c in nvlist
3158N/A if '.'.join(c[1].__module__.split('.')[:-1]) == __name__
49N/A ]
49N/A for cls in classes:
49N/A if hasattr(cls, "name"):
49N/A types[cls.name] = cls
49N/A
49N/A# Clean up after ourselves
49N/Adel f, modname, module, nvlist, classes, c, cls
49N/A
49N/A
49N/Aclass ActionError(Exception):
49N/A """Base exception class for Action errors."""
49N/A
2277N/A def __unicode__(self):
2277N/A # To workaround python issues 6108 and 2517, this provides a
2843N/A # a standard wrapper for this class' exceptions so that they
2843N/A # have a chance of being stringified correctly.
49N/A return str(self)
49N/A
3339N/A def __str__(self):
51N/A raise NotImplementedError()
1846N/A
591N/Aclass UnknownActionError(ActionError):
873N/A def __init__(self, *args):
873N/A ActionError.__init__(self)
873N/A self.actionstr = args[0]
873N/A self.type = args[1]
591N/A
3255N/A def __str__(self):
3255N/A if hasattr(self, "fmri") and self.fmri is not None:
3255N/A return _("unknown action type '%(type)s' in package "
3255N/A "'%(fmri)s' in action '%(action)s'") % {
3255N/A "type": self.type, "fmri": self.fmri,
3255N/A "action": self.actionstr }
3255N/A return _("unknown action type '%(type)s' in action "
3255N/A "'%(action)s'") % { "type": self.type,
3255N/A "action": self.actionstr }
591N/A
591N/Aclass MalformedActionError(ActionError):
1755N/A def __init__(self, *args):
591N/A ActionError.__init__(self)
591N/A self.actionstr = args[0]
591N/A self.position = args[1]
591N/A self.errorstr = args[2]
591N/A
3158N/A def __str__(self):
3158N/A marker = " " * (4 + self.position) + "^"
3158N/A if hasattr(self, "fmri") and self.fmri is not None:
3158N/A return _("Malformed action in package '%(fmri)s' at "
3158N/A "position: %(pos)d: %(error)s:\n %(action)s\n"
3158N/A "%(marker)s") % { "fmri": self.fmri,
3158N/A "pos": self.position, "action": self.actionstr,
591N/A "marker": marker, "error": self.errorstr }
591N/A return _("Malformed action at position: %(pos)d: %(error)s:\n "
591N/A "%(action)s\n%(marker)s") % { "pos": self.position,
1755N/A "action": self.actionstr, "marker": marker,
591N/A "error": self.errorstr }
591N/A
591N/A
591N/Aclass ActionDataError(ActionError):
591N/A """Used to indicate that a file-related error occuring during action
591N/A initialization."""
591N/A
3158N/A def __init__(self, *args, **kwargs):
3158N/A ActionError.__init__(self)
3158N/A self.error = args[0]
3158N/A self.path = kwargs.get("path", None)
3158N/A
3158N/A def __str__(self):
3158N/A return str(self.error)
3158N/A
3158N/A
873N/Aclass InvalidActionError(ActionError):
873N/A """Used to indicate that attributes provided were invalid, or required
873N/A attributes were missing for an action."""
873N/A
873N/A def __init__(self, *args):
873N/A ActionError.__init__(self)
1544N/A self.actionstr = args[0]
1755N/A self.errorstr = args[1]
873N/A
1544N/A def __str__(self):
873N/A if hasattr(self, "fmri") and self.fmri is not None:
873N/A return _("invalid action in package %(fmri)s: "
873N/A "%(action)s: %(error)s") % { "fmri": self.fmri,
873N/A "action": self.actionstr, "error": self.errorstr }
873N/A return _("invalid action, '%(action)s': %(error)s") % {
873N/A "action": self.actionstr, "error": self.errorstr }
873N/A
873N/A
873N/Aclass MissingKeyAttributeError(InvalidActionError):
873N/A """Used to indicate that an action's key attribute is missing."""
1755N/A
873N/A def __init__(self, *args):
873N/A InvalidActionError.__init__(self, str(args[0]),
873N/A _("no value specified for key attribute '%s'") % args[1])
873N/A
873N/A
3158N/Aclass KeyAttributeMultiValueError(InvalidActionError):
3158N/A """Used to indicate that an action's key attribute was specified
3158N/A multiple times for an action that expects it only once."""
3158N/A
3158N/A def __init__(self, *args):
873N/A InvalidActionError.__init__(self, str(args[0]),
591N/A _("%s attribute may only be specified once") % args[1])
2639N/A
2639N/A
2639N/Aclass InvalidPathAttributeError(InvalidActionError):
2639N/A """Used to indicate that an action's path attribute value was either
2639N/A empty, '/', or not a string."""
3158N/A
3158N/A def __init__(self, *args):
2639N/A InvalidActionError.__init__(self, str(args[0]),
2639N/A _("Empty or invalid path attribute"))
2639N/A
2639N/A
2639N/Aclass InvalidActionAttributesError(ActionError):
2639N/A """Used to indicate that one or more action attributes were invalid."""
2639N/A
2639N/A def __init__(self, act, errors, fmri=None):
3158N/A """'act' is an Action (object or string).
3158N/A
2639N/A 'errors' is a list of tuples of the form (name, error) where
2639N/A 'name' is the action attribute name, and 'error' is a string
2639N/A indicating what attribute is invalid and why.
2639N/A
2639N/A 'fmri' is an optional package FMRI (object or string)
2639N/A indicating what package contained the actions with invalid
2639N/A attributes."""
2639N/A
2639N/A ActionError.__init__(self)
2639N/A self.action = act
2639N/A self.errors = errors
1755N/A self.fmri = fmri
2476N/A
1755N/A def __str__(self):
1755N/A act_errors = "\n ".join(err for name, err in self.errors)
1755N/A if self.fmri:
1755N/A return _("The action '%(action)s' in package "
1755N/A "'%(fmri)s' has invalid attribute(s):\n"
1755N/A " %(act_errors)s") % { "action": self.action,
1755N/A "fmri": self.fmri, "act_errors": act_errors }
1755N/A return _("The action '%(action)s' has invalid attribute(s):\n"
1755N/A " %(act_errors)s") % { "action": self.action,
1755N/A "act_errors": act_errors }
1755N/A
1755N/A
1755N/A# This must be imported *after* all of the exception classes are defined as
1755N/A# _actions module init needs the exception objects.
1755N/Afrom _actions import fromstr
1755N/A
1755N/Adef attrsfromstr(string):
1755N/A """Create an attribute dict given a string w/ key=value pairs.
1755N/A
1755N/A Raises MalformedActionError if the attributes have syntactic problems.
3158N/A """
3158N/A return fromstr("unknown %s" % string).attrs
3158N/A
3158N/Adef internalizelist(atype, args, ahash=None, basedirs=None):
3158N/A """Create an action instance based on a sequence of "key=value" strings.
3158N/A This function also translates external representations of actions with
3158N/A payloads (like file and license which can use NOHASH or file paths to
1755N/A point to the payload) to an internal representation which sets the
1755N/A data field of the action returned.
2639N/A
2639N/A The "atype" parameter is the type of action to be built.
3339N/A
591N/A The "args" parameter is the sequence of "key=value" strings.
1292N/A
1292N/A The "ahash" parameter is used to set the hash value for the action.
1292N/A
1292N/A The "basedirs" parameter is the list of directories to look in to find
1292N/A any payload for the action.
3158N/A
57N/A Raises MalformedActionError if the attribute strings are malformed.
1500N/A """
279N/A
1500N/A if atype not in types:
1500N/A raise UnknownActionError(("%s %s" % (atype,
1500N/A " ".join(args))).strip(), atype)
1500N/A
1500N/A data = None
1500N/A
1500N/A if atype in ("file", "license"):
1500N/A data = args.pop(0)
1500N/A
1500N/A attrs = {}
1500N/A
1500N/A try:
1500N/A for a, v in [kv.split("=", 1) for kv in args]:
279N/A if v == '' or a == '':
591N/A kvi = args.index(kv) + 1
279N/A p1 = " ".join(args[:kvi])
305N/A p2 = " ".join(args[kvi:])
1500N/A raise MalformedActionError(
3158N/A "%s %s %s" % (atype, p1, p2), len(p1) + 1,
1500N/A "attribute '%s'" % kv)
873N/A
1500N/A # This is by far the common case-- an attribute with
1500N/A # a single value.
1500N/A if a not in attrs:
1500N/A attrs[a] = v
2456N/A else:
305N/A av = attrs[a]
305N/A if isinstance(av, list):
305N/A attrs[a].append(v)
3339N/A else:
3339N/A attrs[a] = [ av, v ]
3339N/A except ValueError:
3339N/A # We're only here if the for: statement above throws a
305N/A # MalformedActionError. That can happen if split yields a
591N/A # single element, which is possible if e.g. an attribute lacks
591N/A # an =.
591N/A kvi = args.index(kv) + 1
591N/A p1 = " ".join(args[:kvi])
3158N/A p2 = " ".join(args[kvi:])
3158N/A raise MalformedActionError("%s %s %s" % (atype, p1, p2),
3158N/A len(p1) + 2, "attribute '%s'" % kv)
57N/A
305N/A # keys called 'data' cause problems due to the named parameter being
305N/A # passed to the action constructor below. Check for these. Note that
305N/A # _fromstr also checks for this.
305N/A if "data" in attrs:
305N/A astr = atype + " " + " ".join(args)
305N/A raise InvalidActionError(astr,
305N/A "%s action cannot have a 'data' attribute" % atype)
305N/A
305N/A action = types[atype](**attrs)
305N/A if ahash:
591N/A action.hash = ahash
305N/A
591N/A local_path, used_basedir = set_action_data(data, action,
591N/A basedirs=basedirs)
591N/A return action, local_path
591N/A
591N/Adef internalizestr(string, basedirs=None, load_data=True):
591N/A """Create an action instance based on a sequence of strings.
3158N/A This function also translates external representations of actions with
3158N/A payloads (like file and license which can use NOHASH or file paths to
51N/A point to the payload) to an internal representation which sets the
1659N/A data field of the action returned.
1659N/A
1659N/A In general, each string should be in the form of "key=value". The
1659N/A exception is a payload for certain actions which should be the first
1659N/A item in the sequence.
1659N/A
3158N/A Raises MalformedActionError if the attribute strings are malformed.
3158N/A """
1659N/A
3339N/A action = fromstr(string)
1500N/A
1500N/A if action.name not in ("file", "license") or not load_data:
1500N/A return action, None, None
2456N/A
2456N/A local_path, used_basedir = set_action_data(action.hash, action,
1500N/A basedirs=basedirs)
1500N/A return action, local_path, used_basedir
1500N/A
1500N/Adef set_action_data(payload, action, basedirs=None, bundles=None):
1500N/A """Sets the data field of an action using the information in the
1500N/A payload and returns the actual path used to set the data and the
1500N/A source used to find the data (this may be a path or a bundle
1500N/A object).
1500N/A
1500N/A The "payload" parameter is the representation of the data to assign to
1500N/A the action's data field. It can either be NOHASH or a path to the file.
1500N/A
1500N/A The "action" parameter is the action to modify.
1500N/A
1500N/A The "basedirs" parameter contains the directories to examine to find the
1500N/A payload in.
1500N/A
1500N/A The "bundles" parameter contains a list of bundle objects to find the
2460N/A payload in.
1908N/A
51N/A "basedirs" and/or "bundles" must be specified.
2460N/A """
2456N/A
1908N/A if not payload:
1500N/A return None, None
2456N/A
1500N/A if payload == "NOHASH":
2456N/A filepath = os.path.sep + action.attrs["path"]
2456N/A else:
2456N/A filepath = payload
1500N/A
1500N/A if not basedirs:
1500N/A basedirs = []
1500N/A if not bundles:
1500N/A bundles = []
1500N/A
2456N/A # Attempt to find directory or bundle containing source of file data.
2456N/A data = None
2456N/A used_src = None
2456N/A path = filepath.lstrip(os.path.sep)
2456N/A for bd in basedirs:
2456N/A # look for file in specified dir
2456N/A npath = os.path.join(bd, path)
2456N/A if os.path.isfile(npath):
1500N/A used_src = bd
1500N/A data = npath
1908N/A break
1908N/A else:
1500N/A for bundle in bundles:
2800N/A act = bundle.get_action(path)
2800N/A if act:
2800N/A data = act.data
2800N/A used_src = bundle
1500N/A action.attrs["pkg.size"] = \
1500N/A act.attrs["pkg.size"]
1500N/A break
2456N/A
2456N/A if not data and basedirs:
2456N/A raise ActionDataError(_("Action payload '%(name)s' "
2456N/A "was not found in any of the provided locations:"
2456N/A "\n%(basedirs)s") % { "name": filepath,
2456N/A "basedirs": "\n".join(basedirs) }, path=filepath)
2456N/A elif not data and bundles:
2456N/A raise ActionDataError(_("Action payload '%(name)s' was "
2456N/A "not found in any of the provided sources:"
2456N/A "\n%(sources)s") % { "name": filepath,
2456N/A "sources": "\n".join(b.filename for b in bundles) },
2456N/A path=filepath)
2456N/A elif not data:
2456N/A # Only if no explicit sources were provided should a
2456N/A # fallback to filepath be performed.
2456N/A data = filepath
2456N/A
2456N/A # This relies on data having a value set by the code above so that an
2456N/A # ActionDataError will be raised if the file doesn't exist or is not
2456N/A # accessible.
2456N/A action.set_data(data)
2456N/A return data, used_src
2456N/A