--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/modules/_varcet.c Wed Feb 29 11:37:15 2012 -0800
@@ -0,0 +1,189 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <Python.h>
+
+/*ARGSUSED*/
+static PyObject *
+_allow_facet(PyObject *self, PyObject *args)
+{
+ PyObject *action = NULL;
+ PyObject *facets = NULL;
+ PyObject *keylist = NULL;
+
+ PyObject *act_attrs = NULL;
+ PyObject *attr = NULL;
+ PyObject *value = NULL;
+
+ PyObject *res = NULL;
+ PyObject *ret = Py_True;
+ Py_ssize_t fpos = 0;
+ Py_ssize_t klen = 0;
+
+ if (!PyArg_UnpackTuple(args, "_allow_facet", 2, 2, &facets, &action))
+ return (NULL);
+
+ if ((act_attrs = PyObject_GetAttrString(action, "attrs")) == NULL)
+ return (NULL);
+
+ if ((keylist = PyObject_GetAttrString(facets,
+ "_Facets__keylist")) == NULL) {
+ Py_DECREF(act_attrs);
+ return (NULL);
+ }
+ klen = PyList_GET_SIZE(keylist);
+
+ if ((res = PyObject_GetAttrString(facets, "_Facets__res")) == NULL) {
+ Py_DECREF(act_attrs);
+ Py_DECREF(keylist);
+ return (NULL);
+ }
+
+#define CLEANUP_FREFS \
+ Py_DECREF(act_attrs);\
+ Py_DECREF(keylist);\
+ Py_DECREF(res);
+
+ while (PyDict_Next(act_attrs, &fpos, &attr, &value)) {
+ char *as = PyString_AS_STRING(attr);
+ if (strncmp(as, "facet.", 6) != 0)
+ continue;
+
+ PyObject *facet = PyDict_GetItem(facets, attr);
+ if (facet == Py_True) {
+ CLEANUP_FREFS;
+ Py_INCREF(facet);
+ return (facet);
+ }
+
+ if (facet == NULL) {
+ Py_ssize_t idx = 0;
+
+ /*
+ * Facet is unknown; see if it matches one of the
+ * wildcard patterns set.
+ */
+ for (idx = 0; idx < klen; idx++) {
+ PyObject *key = PyList_GET_ITEM(keylist, idx);
+ PyObject *re = PyDict_GetItem(res, key);
+ PyObject *match = PyObject_CallMethod(re,
+ "match", "O", attr);
+ if (match != Py_None) {
+ PyObject *fval = PyDict_GetItem(
+ facets, key);
+
+ Py_DECREF(match);
+ CLEANUP_FREFS;
+ if (fval == NULL)
+ return (NULL);
+ Py_INCREF(fval);
+ return (fval);
+ }
+ Py_DECREF(match);
+ }
+
+ /*
+ * If facet is unknown to the system and no facet
+ * patterns matched it, be inclusive and allow the
+ * action.
+ */
+ CLEANUP_FREFS;
+ Py_RETURN_TRUE;
+ }
+
+ /* Facets are currently OR'd. */
+ ret = Py_False;
+ }
+
+ CLEANUP_FREFS;
+ Py_INCREF(ret);
+ return (ret);
+}
+
+/*ARGSUSED*/
+static PyObject *
+_allow_variant(PyObject *self, PyObject *args)
+{
+ PyObject *action = NULL;
+ PyObject *vars = NULL;
+ PyObject *act_attrs = NULL;
+ PyObject *attr = NULL;
+ PyObject *value = NULL;
+ Py_ssize_t pos = 0;
+
+ if (!PyArg_UnpackTuple(args, "_allow_variant", 2, 2, &vars, &action))
+ return (NULL);
+
+ if ((act_attrs = PyObject_GetAttrString(action, "attrs")) == NULL)
+ return (NULL);
+
+ while (PyDict_Next(act_attrs, &pos, &attr, &value)) {
+ char *as = PyString_AS_STRING(attr);
+ if (strncmp(as, "variant.", 8) == 0) {
+ PyObject *sysv = PyDict_GetItem(vars, attr);
+ char *av = PyString_AsString(value);
+ char *sysav = NULL;
+
+ if (sysv == NULL) {
+ /*
+ * If system variant value doesn't exist, then
+ * allow the action if it is a debug variant
+ * that is "false".
+ */
+ if ((strncmp(as, "variant.debug.", 14) == 0) &&
+ (strncmp(av, "false", 5) != 0)) {
+ Py_DECREF(act_attrs);
+ Py_RETURN_FALSE;
+ }
+ continue;
+ }
+
+ sysav = PyString_AsString(sysv);
+ if (strcmp(av, sysav) != 0) {
+ /*
+ * If system variant value doesn't match action
+ * variant value, don't allow this action.
+ */
+ Py_DECREF(act_attrs);
+ Py_RETURN_FALSE;
+ }
+ }
+ }
+
+ Py_DECREF(act_attrs);
+ Py_RETURN_TRUE;
+}
+
+static PyMethodDef methods[] = {
+ { "_allow_facet", (PyCFunction)_allow_facet, METH_VARARGS },
+ { "_allow_variant", (PyCFunction)_allow_variant, METH_VARARGS },
+ { NULL, NULL, 0, NULL }
+};
+
+PyMODINIT_FUNC
+init_varcet(void)
+{
+ Py_InitModule("_varcet", methods);
+}
--- a/src/modules/actions/__init__.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/__init__.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
#
"""
@@ -113,13 +113,14 @@
marker = " " * (4 + self.position) + "^"
if hasattr(self, "fmri") and self.fmri is not None:
return _("Malformed action in package '%(fmri)s' at "
- "position: %(pos)d:\n %(action)s\n"
+ "position: %(pos)d: %(error)s:\n %(action)s\n"
"%(marker)s") % { "fmri": self.fmri,
"pos": self.position, "action": self.actionstr,
- "marker": marker }
- return _("Malformed action at position: %(pos)d:\n "
+ "marker": marker, "error": self.errorstr }
+ return _("Malformed action at position: %(pos)d: %(error)s:\n "
"%(action)s\n%(marker)s") % { "pos": self.position,
- "action": self.actionstr, "marker": marker }
+ "action": self.actionstr, "marker": marker,
+ "error": self.errorstr }
class ActionDataError(ActionError):
@@ -153,6 +154,32 @@
"action": self.actionstr, "error": self.errorstr }
+class MissingKeyAttributeError(InvalidActionError):
+ """Used to indicate that an action's key attribute is missing."""
+
+ def __init__(self, *args):
+ InvalidActionError.__init__(self, str(args[0]),
+ _("no value specified for key attribute '%s'") % args[1])
+
+
+class KeyAttributeMultiValueError(InvalidActionError):
+ """Used to indicate that an action's key attribute was specified
+ multiple times for an action that expects it only once."""
+
+ def __init__(self, *args):
+ InvalidActionError.__init__(self, str(args[0]),
+ _("%s attribute may only be specified once") % args[1])
+
+
+class InvalidPathAttributeError(InvalidActionError):
+ """Used to indicate that an action's path attribute value was either
+ empty, '/', or not a string."""
+
+ def __init__(self, *args):
+ InvalidActionError.__init__(self, str(args[0]),
+ _("Empty or invalid path attribute"))
+
+
class InvalidActionAttributesError(ActionError):
"""Used to indicate that one or more action attributes were invalid."""
@@ -184,33 +211,16 @@
"act_errors": act_errors }
-from _actions import _fromstr
+# This must be imported *after* all of the exception classes are defined as
+# _actions module init needs the exception objects.
+from _actions import fromstr
def attrsfromstr(string):
"""Create an attribute dict given a string w/ key=value pairs.
Raises MalformedActionError if the attributes have syntactic problems.
"""
- return _fromstr("bogus %s" % string)[2]
-
-def fromstr(string, data=None):
- """Create an action instance based on a str() representation of an
- action.
-
- Raises UnknownActionError if the action type is unknown.
- Raises MalformedActionError if the action has other syntactic problems.
- """
-
- atype, ahash, attr_dict = _fromstr(string)
-
- if atype not in types:
- raise UnknownActionError(string, atype)
-
- action = types[atype](data=data, **attr_dict)
- if ahash:
- action.hash = ahash
-
- return action
+ return fromstr("unknown %s" % string).attrs
def internalizelist(atype, args, ahash=None, basedirs=None):
"""Create an action instance based on a sequence of "key=value" strings.
@@ -281,7 +291,7 @@
raise InvalidActionError(astr,
"%s action cannot have a 'data' attribute" % atype)
- action = types[atype](data=None, **attrs)
+ action = types[atype](**attrs)
if ahash:
action.hash = ahash
--- a/src/modules/actions/_actions.c Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/_actions.c Wed Feb 29 11:37:15 2012 -0800
@@ -20,7 +20,7 @@
*/
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <Python.h>
@@ -29,40 +29,47 @@
static PyObject *MalformedActionError;
static PyObject *InvalidActionError;
+static PyObject *UnknownActionError;
+static PyObject *aclass_attribute;
+static PyObject *aclass_depend;
+static PyObject *aclass_directory;
+static PyObject *aclass_driver;
+static PyObject *aclass_file;
+static PyObject *aclass_group;
+static PyObject *aclass_hardlink;
+static PyObject *aclass_legacy;
+static PyObject *aclass_license;
+static PyObject *aclass_link;
+static PyObject *aclass_signature;
+static PyObject *aclass_unknown;
+static PyObject *aclass_user;
-static char *notident = "hash attribute not identical to positional hash";
+static const char *notident = "hash attribute not identical to positional hash";
-static int
+static inline int
add_to_attrs(PyObject *attrs, PyObject *key, PyObject *attr)
{
- int contains, ret;
+ int ret;
+ PyObject *list;
+ PyObject *av = PyDict_GetItem(attrs, key);
- contains = PyDict_Contains(attrs, key);
- if (contains == 0) {
+ if (av == NULL)
return (PyDict_SetItem(attrs, key, attr));
- } else if (contains == 1) {
- PyObject *av = PyDict_GetItem(attrs, key);
- Py_INCREF(av);
- if (PyList_Check(av)) {
- ret = PyList_Append(av, attr);
- Py_DECREF(av);
- return (ret);
- } else {
- PyObject *list;
- if ((list = PyList_New(2)) == NULL)
- return (-1);
- PyList_SET_ITEM(list, 0, av);
- Py_INCREF(attr);
- PyList_SET_ITEM(list, 1, attr);
- ret = PyDict_SetItem(attrs, key, list);
- Py_DECREF(list);
- return (ret);
- }
- } else if (contains == -1)
+
+ if (PyList_CheckExact(av))
+ return (PyList_Append(av, attr));
+
+ if ((list = PyList_New(2)) == NULL)
return (-1);
- /* Shouldn't ever get here */
- return (0);
+ /* PyList_SET_ITEM steals references. */
+ Py_INCREF(av);
+ PyList_SET_ITEM(list, 0, av);
+ Py_INCREF(attr);
+ PyList_SET_ITEM(list, 1, attr);
+ ret = PyDict_SetItem(attrs, key, list);
+ Py_DECREF(list);
+ return (ret);
}
static void
@@ -89,28 +96,30 @@
/*ARGSUSED*/
static PyObject *
-_fromstr(PyObject *self, PyObject *args)
+fromstr(PyObject *self, PyObject *args, PyObject *kwdict)
{
char *s = NULL;
char *str = NULL;
char *hashstr = NULL;
char *keystr = NULL;
int *slashmap = NULL;
- int strl;
+ int strl, typestrl;
int i, ks, vs, keysize;
int smlen, smpos;
char quote;
- PyObject *type = NULL;
+ PyObject *act_args = NULL;
+ PyObject *act_class = NULL;
+ PyObject *act_data = NULL;
+ PyObject *action = NULL;
PyObject *hash = NULL;
PyObject *attrs = NULL;
- PyObject *ret = NULL;
PyObject *key = NULL;
PyObject *attr = NULL;
enum {
- KEY, /* key */
- UQVAL, /* unquoted value */
- QVAL, /* quoted value */
- WS /* whitespace */
+ KEY, /* key */
+ UQVAL, /* unquoted value */
+ QVAL, /* quoted value */
+ WS /* whitespace */
} state;
/*
@@ -119,25 +128,34 @@
* malformed() or invalid(). Failure to order this properly will cause
* corruption of the exception messages.
*/
-#define malformed(msg) set_malformederr(str, i, (msg))
-#define invalid(msg) set_invaliderr(str, (msg))
-#define CLEANUP_REFS \
+#define malformed(msg) set_malformederr(str, i, (msg))
+#define invalid(msg) set_invaliderr(str, (msg))
+#define CLEANUP_REFS \
PyMem_Free(str);\
Py_XDECREF(key);\
- Py_XDECREF(type);\
Py_XDECREF(attr);\
Py_XDECREF(attrs);\
Py_XDECREF(hash);\
free(hashstr);
/*
+ * Positional arguments must be included in the keyword argument list in
+ * the order you want them to be assigned. (A subtle point missing from
+ * the Python documentation.)
+ */
+ static char *kwlist[] = { "string", "data", NULL };
+
+ /* Assume data=None by default. */
+ act_data = Py_None;
+
+ /*
* The action string is currently assumed to be a stream of bytes that
* are valid UTF-8. This method works regardless of whether the string
* object provided is a Unicode object, string object, or a character
* buffer.
*/
- if (PyArg_ParseTuple(args, "et#", "utf-8", &str, &strl) == 0) {
- PyErr_SetString(PyExc_ValueError, "could not parse argument");
+ if (PyArg_ParseTupleAndKeywords(args, kwdict, "et#|O:fromstr", kwlist,
+ "utf-8", &str, &strl, &act_data) == 0) {
return (NULL);
}
@@ -150,18 +168,70 @@
return (NULL);
}
- if ((type = PyString_FromStringAndSize(str, s - str)) == NULL) {
+ /*
+ * The comparisons here are ordered by frequency in which actions are
+ * most likely to be encountered in usage by the client grouped by
+ * length. Yes, a cheap hack to squeeze a tiny bit of additional
+ * performance out.
+ */
+ typestrl = s - str;
+ if (typestrl == 4) {
+ if (strncmp(str, "file", 4) == 0)
+ act_class = aclass_file;
+ else if (strncmp(str, "link", 4) == 0)
+ act_class = aclass_link;
+ else if (strncmp(str, "user", 4) == 0)
+ act_class = aclass_user;
+ } else if (typestrl == 6) {
+ if (strncmp(str, "depend", 6) == 0)
+ act_class = aclass_depend;
+ else if (strncmp(str, "driver", 6) == 0)
+ act_class = aclass_driver;
+ else if (strncmp(str, "legacy", 6) == 0)
+ act_class = aclass_legacy;
+ } else if (typestrl == 3) {
+ if (strncmp(str, "set", 3) == 0)
+ act_class = aclass_attribute;
+ else if (strncmp(str, "dir", 3) == 0)
+ act_class = aclass_directory;
+ } else if (typestrl == 8) {
+ if (strncmp(str, "hardlink", 8) == 0)
+ act_class = aclass_hardlink;
+ } else if (typestrl == 7) {
+ if (strncmp(str, "license", 7) == 0)
+ act_class = aclass_license;
+ else if (strncmp(str, "unknown", 7) == 0)
+ act_class = aclass_unknown;
+ } else if (typestrl == 9) {
+ if (strncmp(str, "signature", 9) == 0)
+ act_class = aclass_signature;
+ } else if (typestrl == 5) {
+ if (strncmp(str, "group", 5) == 0)
+ act_class = aclass_group;
+ }
+
+ if (act_class == NULL) {
+ if ((act_args = Py_BuildValue("s#s#", str, strl,
+ str, typestrl)) != NULL) {
+ PyErr_SetObject(UnknownActionError, act_args);
+ Py_DECREF(act_args);
+ PyMem_Free(str);
+ return (NULL);
+ }
+
+ /*
+ * Unable to build argument list for exception; so raise
+ * general type exception instead.
+ */
+ PyErr_SetString(PyExc_TypeError, "unknown action type");
PyMem_Free(str);
return (NULL);
}
- PyString_InternInPlace(&type);
-
- ks = vs = s - str;
+ ks = vs = typestrl;
state = WS;
if ((attrs = PyDict_New()) == NULL) {
PyMem_Free(str);
- Py_DECREF(type);
return (NULL);
}
for (i = s - str; str[i]; i++) {
@@ -174,10 +244,9 @@
malformed("whitespace in key");
CLEANUP_REFS;
return (NULL);
- }
- else {
+ } else {
if ((hash = PyString_FromStringAndSize(
- keystr, keysize)) == NULL) {
+ keystr, keysize)) == NULL) {
CLEANUP_REFS;
return (NULL);
}
@@ -186,13 +255,13 @@
}
} else if (str[i] == '=') {
if ((key = PyString_FromStringAndSize(
- keystr, keysize)) == NULL) {
+ keystr, keysize)) == NULL) {
CLEANUP_REFS;
return (NULL);
}
if (keysize == 4 && strncmp(keystr, "data",
- keysize) == 0) {
+ keysize) == 0) {
invalid("invalid key: 'data'");
CLEANUP_REFS;
return (NULL);
@@ -208,8 +277,7 @@
malformed("impossible: missing key");
CLEANUP_REFS;
return (NULL);
- }
- else if (++i == strl) {
+ } else if (++i == strl) {
malformed("missing value");
CLEANUP_REFS;
return (NULL);
@@ -222,8 +290,7 @@
malformed("missing value");
CLEANUP_REFS;
return (NULL);
- }
- else {
+ } else {
state = UQVAL;
vs = i;
}
@@ -243,7 +310,7 @@
*/
if (slashmap == NULL) {
smlen = 16;
- slashmap = calloc(smlen, sizeof(int));
+ slashmap = calloc(smlen, sizeof (int));
if (slashmap == NULL) {
PyMem_Free(str);
return (PyErr_NoMemory());
@@ -258,7 +325,7 @@
} else if (smpos == smlen - 1) {
smlen *= 2;
slashmap = realloc(slashmap,
- smlen * sizeof(int));
+ smlen * sizeof (int));
if (slashmap == NULL) {
PyMem_Free(str);
return (PyErr_NoMemory());
@@ -304,7 +371,7 @@
slashmap = NULL;
if ((attr = PyString_FromStringAndSize(
- sattr, attrlen - o)) == NULL) {
+ sattr, attrlen - o)) == NULL) {
free(sattr);
CLEANUP_REFS;
return (NULL);
@@ -319,7 +386,7 @@
}
}
- if (!strncmp(keystr, "hash=", 5)) {
+ if (strncmp(keystr, "hash=", 5) == 0) {
char *as = PyString_AsString(attr);
if (hashstr && strcmp(as, hashstr)) {
invalid(notident);
@@ -330,7 +397,8 @@
attr = NULL;
} else {
PyString_InternInPlace(&attr);
- if (add_to_attrs(attrs, key, attr) == -1) {
+ if (add_to_attrs(attrs, key,
+ attr) == -1) {
CLEANUP_REFS;
return (NULL);
}
@@ -340,8 +408,9 @@
if (str[i] == ' ' || str[i] == '\t') {
state = WS;
Py_XDECREF(attr);
- attr = PyString_FromStringAndSize(&str[vs], i - vs);
- if (!strncmp(keystr, "hash=", 5)) {
+ attr = PyString_FromStringAndSize(&str[vs],
+ i - vs);
+ if (strncmp(keystr, "hash=", 5) == 0) {
char *as = PyString_AsString(attr);
if (hashstr && strcmp(as, hashstr)) {
invalid(notident);
@@ -352,7 +421,8 @@
attr = NULL;
} else {
PyString_InternInPlace(&attr);
- if (add_to_attrs(attrs, key, attr) == -1) {
+ if (add_to_attrs(attrs, key,
+ attr) == -1) {
CLEANUP_REFS;
return (NULL);
}
@@ -371,24 +441,14 @@
}
}
- if (state == QVAL) {
- if (slashmap != NULL)
- free(slashmap);
-
- malformed("unfinished quoted value");
- CLEANUP_REFS;
- return (NULL);
- }
- if (state == KEY) {
- malformed("missing value");
- CLEANUP_REFS;
- return (NULL);
- }
-
+ /*
+ * UQVAL is the most frequently encountered end-state, so check that
+ * first to avoid unnecessary state comparisons.
+ */
if (state == UQVAL) {
Py_XDECREF(attr);
attr = PyString_FromStringAndSize(&str[vs], i - vs);
- if (!strncmp(keystr, "hash=", 5)) {
+ if (strncmp(keystr, "hash=", 5) == 0) {
char *as = PyString_AsString(attr);
if (hashstr && strcmp(as, hashstr)) {
invalid(notident);
@@ -404,33 +464,77 @@
return (NULL);
}
}
+ } else if (state == QVAL) {
+ if (slashmap != NULL)
+ free(slashmap);
+
+ malformed("unfinished quoted value");
+ CLEANUP_REFS;
+ return (NULL);
+ } else if (state == KEY) {
+ malformed("missing value");
+ CLEANUP_REFS;
+ return (NULL);
}
PyMem_Free(str);
- if (hash == NULL)
- hash = Py_None;
-
- ret = Py_BuildValue("OOO", type, hash, attrs);
Py_XDECREF(key);
Py_XDECREF(attr);
- Py_DECREF(type);
+
+ /*
+ * Action parsing is done; now build the list of arguments to construct
+ * the object for it.
+ */
+ if ((act_args = Py_BuildValue("(O)", act_data)) == NULL) {
+ if (hash != NULL && hash != Py_None)
+ Py_DECREF(hash);
+ Py_DECREF(attrs);
+ return (NULL);
+ }
+
+ /*
+ * Using the cached action class assigned earlier based on the type,
+ * call the action constructor, set the hash attribute, and then return
+ * the new action object.
+ */
+ action = PyObject_Call(act_class, act_args, attrs);
+ Py_DECREF(act_args);
Py_DECREF(attrs);
- if (hash != Py_None)
+ if (action == NULL) {
+ if (hash != NULL && hash != Py_None)
+ Py_DECREF(hash);
+ return (NULL);
+ }
+
+ if (hash != NULL && hash != Py_None) {
+ if (PyObject_SetAttrString(action, "hash", hash) == -1) {
+ Py_DECREF(hash);
+ Py_DECREF(action);
+ return (NULL);
+ }
Py_DECREF(hash);
- return (ret);
+ }
+
+ return (action);
}
static PyMethodDef methods[] = {
- { "_fromstr", _fromstr, METH_VARARGS },
- { NULL, NULL }
+ { "fromstr", (PyCFunction)fromstr, METH_VARARGS | METH_KEYWORDS },
+ { NULL, NULL, 0, NULL }
};
PyMODINIT_FUNC
init_actions(void)
{
- PyObject *sys, *pkg_actions;
- PyObject *sys_modules;
+ PyObject *action_types = NULL;
+ PyObject *pkg_actions = NULL;
+ PyObject *sys = NULL;
+ PyObject *sys_modules = NULL;
+ /*
+ * Note that module initialization functions are void and may not return
+ * a value. However, they should set an exception if appropriate.
+ */
if (Py_InitModule("_actions", methods) == NULL)
return;
@@ -449,14 +553,68 @@
return;
if ((pkg_actions = PyDict_GetItemString(sys_modules, "pkg.actions"))
- == NULL) {
+ == NULL) {
/* No exception is set */
PyErr_SetString(PyExc_KeyError, "pkg.actions");
+ Py_DECREF(sys_modules);
+ return;
+ }
+ Py_DECREF(sys_modules);
+
+ /*
+ * Each reference is DECREF'd after retrieval as Python 2.x doesn't
+ * provide a module shutdown/cleanup hook. Since these references are
+ * guaranteed to stay around until the module is unloaded, DECREF'ing
+ * them now ensures that garbage cleanup will work as expected during
+ * process exit. This applies to the action type caching below as well.
+ */
+ MalformedActionError = \
+ PyObject_GetAttrString(pkg_actions, "MalformedActionError");
+ Py_DECREF(MalformedActionError);
+ InvalidActionError = \
+ PyObject_GetAttrString(pkg_actions, "InvalidActionError");
+ Py_DECREF(InvalidActionError);
+ UnknownActionError = \
+ PyObject_GetAttrString(pkg_actions, "UnknownActionError");
+ Py_DECREF(UnknownActionError);
+
+ /*
+ * Retrieve the list of action types and then store a reference to each
+ * class for use during action construction. (This allows avoiding the
+ * overhead of retrieving a new reference for each action constructed.)
+ */
+ if ((action_types = PyObject_GetAttrString(pkg_actions,
+ "types")) == NULL) {
+ PyErr_SetString(PyExc_KeyError, "pkg.actions.types missing!");
return;
}
- MalformedActionError = \
- PyObject_GetAttrString(pkg_actions, "MalformedActionError");
- InvalidActionError = \
- PyObject_GetAttrString(pkg_actions, "InvalidActionError");
+ /*
+ * cache_class borrows the references to the action type objects; this
+ * is safe as they should remain valid as long as the module is loaded.
+ * (PyDict_GetItem* doesn't return a new reference.)
+ */
+#define cache_class(cache_var, name) \
+ if ((cache_var = PyDict_GetItemString(action_types, name)) == NULL) { \
+ PyErr_SetString(PyExc_KeyError, \
+ "Action type class missing: " name); \
+ Py_DECREF(action_types); \
+ return; \
+ }
+
+ cache_class(aclass_attribute, "set");
+ cache_class(aclass_depend, "depend");
+ cache_class(aclass_directory, "dir");
+ cache_class(aclass_driver, "driver");
+ cache_class(aclass_file, "file");
+ cache_class(aclass_group, "group");
+ cache_class(aclass_hardlink, "hardlink");
+ cache_class(aclass_legacy, "legacy");
+ cache_class(aclass_license, "license");
+ cache_class(aclass_link, "link");
+ cache_class(aclass_signature, "signature");
+ cache_class(aclass_unknown, "unknown");
+ cache_class(aclass_user, "user");
+
+ Py_DECREF(action_types);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/modules/actions/_common.c Wed Feb 29 11:37:15 2012 -0800
@@ -0,0 +1,297 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * These functions, although common to all actions, could not be placed in
+ * _actions.c due to module import dependencies.
+ */
+#include <Python.h>
+
+static PyObject *nohash;
+
+static void
+set_invalid_action_error(const char *name, PyObject *action,
+ PyObject *key_aname)
+{
+ PyObject *exc = NULL;
+ PyObject *val = NULL;
+ PyObject *pkg_actions = NULL;
+
+ if ((pkg_actions = PyImport_ImportModule("pkg.actions")) == NULL) {
+ /* No exception is set */
+ PyErr_SetString(PyExc_KeyError, "pkg.actions");
+ return;
+ }
+
+ /*
+ * Obtain a reference to the action exception type so that SetObject can
+ * build the appropriate exception object using the created list of
+ * arguments.
+ */
+ if ((exc = PyObject_GetAttrString(pkg_actions, name)) == NULL) {
+ Py_DECREF(pkg_actions);
+ return;
+ }
+ Py_DECREF(pkg_actions);
+
+ if ((val = Py_BuildValue("OO", action, key_aname)) != NULL) {
+ PyErr_SetObject(exc, val);
+ Py_DECREF(val);
+ }
+ Py_DECREF(exc);
+}
+
+/*
+ * These routines are expected to return NULL in an exception case per CPython
+ * calling conventions. Whenver NULL is returned, an exception should already
+ * be set, either by the function that was just called and failed, or by the
+ * routine. If the routine is successful, it is expected to return a PyObject
+ * of some kind even if the return value is ignored by consumers. The expected
+ * return value is usually None.
+ */
+
+/*ARGSUSED*/
+static inline PyObject *
+_generic_init_common(PyObject *action, PyObject *data, PyObject *attrs)
+{
+ PyObject *key_aname = NULL;
+ PyObject *key_attr = NULL;
+ PyObject *path_attr = NULL;
+ char *path = NULL;
+ char invalid_path = 0;
+
+ /*
+ * Before doing anything else to the action, action attributes must be
+ * set as set_data() relies on it.
+ */
+ if (attrs != NULL) {
+ if (PyObject_SetAttrString(action, "attrs", attrs) == -1)
+ return (NULL);
+ } else {
+ /* Caller didn't specify any keyword arguments. */
+ if ((attrs = PyDict_New()) == NULL)
+ return (NULL);
+ if (PyObject_SetAttrString(action, "attrs", attrs) == -1) {
+ Py_DECREF(attrs);
+ return (NULL);
+ }
+ Py_DECREF(attrs);
+ }
+
+ if (data == NULL || data == Py_None) {
+ /* No need to call set_data(); this is much faster. */
+ if (PyObject_SetAttrString(action, "data", Py_None) == -1)
+ return (NULL);
+ } else {
+ PyObject *res = PyObject_CallMethod(action, "set_data", "(O)",
+ data);
+ if (res == NULL)
+ return (NULL);
+ Py_DECREF(res);
+ }
+
+ if ((key_aname = PyObject_GetAttrString(action, "key_attr")) == NULL)
+ return (NULL);
+
+ if (key_aname == Py_None) {
+ Py_DECREF(key_aname);
+ Py_RETURN_NONE;
+ }
+
+ if ((key_attr = PyDict_GetItem(attrs, key_aname)) == NULL) {
+ PyObject *aname = PyObject_GetAttrString(action, "name");
+ char *ns = PyString_AS_STRING(aname);
+
+ /*
+ * set actions allow an alternate value form, so
+ * AttributeAction.__init__ will fill this in later and raise an
+ * exception if appropriate.
+ *
+ * signature actions can't require their key attribute since the
+ * value of a signature may not yet be known.
+ */
+ if (strcmp(ns, "set") != 0 && strcmp(ns, "signature") != 0) {
+ set_invalid_action_error("MissingKeyAttributeError",
+ action, key_aname);
+ Py_DECREF(key_aname);
+ return (NULL);
+ }
+
+ Py_DECREF(key_aname);
+ Py_RETURN_NONE;
+ }
+
+ if (PyList_CheckExact(key_attr)) {
+ PyObject *aname = PyObject_GetAttrString(action, "name");
+ char *ns = PyString_AS_STRING(aname);
+ int multi_error = 0;
+
+ if (strcmp(ns, "depend") != 0) {
+ /*
+ * Unless this is a dependency action, multiple values
+ * are never allowed for key attribute.
+ */
+ multi_error = 1;
+ } else {
+ PyObject *dt = PyDict_GetItemString(attrs, "type");
+ /*
+ * If dependency type is 'require-any', multiple values
+ * are allowed for key attribute.
+ */
+ if (dt != NULL) {
+ char *ts = PyString_AsString(dt);
+ if (ts == NULL) {
+ Py_DECREF(key_aname);
+ Py_DECREF(aname);
+ return (NULL);
+ }
+ if (strcmp(ts, "require-any") != 0)
+ multi_error = 1;
+ } else {
+ multi_error = 1;
+ }
+ }
+
+ Py_DECREF(aname);
+ if (multi_error == 1) {
+ set_invalid_action_error("KeyAttributeMultiValueError",
+ action, key_aname);
+ Py_DECREF(key_aname);
+ return (NULL);
+ }
+ }
+
+ if ((path_attr = PyDict_GetItemString(attrs, "path")) == NULL) {
+ Py_DECREF(key_aname);
+ Py_RETURN_NONE;
+ }
+
+ if ((path = PyString_AsString(path_attr)) != NULL) {
+ if (path[0] == '/') {
+ PyObject *stripped = PyObject_CallMethod(
+ path_attr, "lstrip", "(s)", "/");
+ if (stripped == NULL) {
+ Py_DECREF(key_aname);
+ return (NULL);
+ }
+ if (PyDict_SetItemString(attrs, "path",
+ stripped) == -1) {
+ Py_DECREF(key_aname);
+ Py_DECREF(stripped);
+ return (NULL);
+ }
+ if (PyString_GET_SIZE(stripped) == 0)
+ invalid_path = 1;
+ Py_DECREF(stripped);
+ } else {
+ if (PyString_GET_SIZE(path_attr) == 0)
+ invalid_path = 1;
+ }
+ } else {
+ /* path attribute is not a string. */
+ invalid_path = 1;
+ }
+
+ if (invalid_path == 1) {
+ set_invalid_action_error("InvalidPathAttributeError",
+ action, key_aname);
+ Py_DECREF(key_aname);
+ return (NULL);
+ }
+
+ Py_DECREF(key_aname);
+ Py_RETURN_NONE;
+}
+
+/*ARGSUSED*/
+static PyObject *
+_generic_init(PyObject *self, PyObject *args, PyObject *attrs)
+{
+ PyObject *action = NULL;
+ PyObject *data = NULL;
+
+ /* data is optional, but must not be specified as a keyword argument! */
+ if (!PyArg_UnpackTuple(args, "_generic_init", 1, 2, &action, &data))
+ return (NULL);
+
+ return (_generic_init_common(action, data, attrs));
+}
+
+/*ARGSUSED*/
+static PyObject *
+_file_init(PyObject *self, PyObject *args, PyObject *attrs)
+{
+ PyObject *action = NULL;
+ PyObject *data = NULL;
+ PyObject *result = NULL;
+
+ if (!PyArg_UnpackTuple(args, "_file_init", 1, 2, &action, &data))
+ return (NULL);
+
+ if ((result = _generic_init_common(action, data, attrs)) != NULL)
+ Py_DECREF(result);
+ else
+ return (NULL);
+
+ if (PyObject_SetAttrString(action, "hash", nohash) == -1)
+ return (NULL);
+
+ if (PyObject_SetAttrString(action, "replace_required", Py_False) == -1)
+ return (NULL);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef methods[] = {
+ { "_file_init", (PyCFunction)_file_init, METH_VARARGS | METH_KEYWORDS },
+ { "_generic_init", (PyCFunction)_generic_init,
+ METH_VARARGS | METH_KEYWORDS },
+ { NULL, NULL, 0, NULL }
+};
+
+PyMODINIT_FUNC
+init_common(void)
+{
+ PyObject *pkg_actions = NULL;
+
+ /*
+ * Note that module initialization functions are void and may not return
+ * a value. However, they should set an exception if appropriate.
+ */
+ if (Py_InitModule("_common", methods) == NULL)
+ return;
+
+ if ((pkg_actions = PyImport_ImportModule("pkg.actions")) == NULL) {
+ /* No exception is set */
+ PyErr_SetString(PyExc_KeyError, "pkg.actions");
+ return;
+ }
+
+ if ((nohash = PyString_FromStringAndSize("NOHASH", 6)) == NULL) {
+ PyErr_SetString(PyExc_ValueError,
+ "Unable to create nohash string object.");
+ return;
+ }
+}
--- a/src/modules/actions/attribute.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/attribute.py Wed Feb 29 11:37:15 2012 -0800
@@ -41,22 +41,25 @@
name = "set"
key_attr = "name"
+ ordinality = generic._orderdict[name]
def __init__(self, data=None, **attrs):
generic.Action.__init__(self, data, **attrs)
- # For convenience, we allow people to express attributes as
- # "<name>=<value>", rather than "name=<name> value=<value>", but
- # we always convert to the latter.
try:
- if len(attrs) == 1:
- self.attrs["name"], self.attrs["value"] = \
- self.attrs.popitem()
+ self.attrs["name"]
+ self.attrs["value"]
except KeyError:
- # Let error check below deal with this.
- pass
-
- if "name" not in self.attrs or "value" not in self.attrs:
+ # For convenience, we allow people to express attributes as
+ # "<name>=<value>", rather than "name=<name> value=<value>", but
+ # we always convert to the latter.
+ try:
+ if len(attrs) == 1:
+ self.attrs["name"], self.attrs["value"] = \
+ self.attrs.popitem()
+ return
+ except KeyError:
+ pass
raise pkg.actions.InvalidActionError(str(self),
'Missing "name" or "value" attribute')
--- a/src/modules/actions/depend.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/depend.py Wed Feb 29 11:37:15 2012 -0800
@@ -96,18 +96,19 @@
name = "depend"
key_attr = "fmri"
+ ordinality = generic._orderdict[name]
def __init__(self, data=None, **attrs):
generic.Action.__init__(self, data, **attrs)
- if "type" not in self.attrs:
+ try:
+ if self.attrs["type"] not in known_types:
+ raise pkg.actions.InvalidActionError(str(self),
+ _("Unknown type (%s) in depend action") %
+ self.attrs["type"])
+ except KeyError:
raise pkg.actions.InvalidActionError(
str(self), _("Missing type attribute"))
- if self.attrs["type"] not in known_types:
- raise pkg.actions.InvalidActionError(str(self),
- _("Unknown type (%s) in depend action") %
- self.attrs["type"])
-
def __check_parent_installed(self, image, fmri):
if not image.linked.ischild():
--- a/src/modules/actions/directory.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/directory.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
#
"""module describing a directory packaging object
@@ -48,6 +48,7 @@
globally_identical = True
refcountable = True
namespace_group = "path"
+ ordinality = generic._orderdict[name]
def compare(self, other):
return cmp(self.attrs["path"], other.attrs["path"])
--- a/src/modules/actions/driver.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/driver.py Wed Feb 29 11:37:15 2012 -0800
@@ -45,6 +45,7 @@
name = "driver"
key_attr = "name"
globally_identical = True
+ ordinality = generic._orderdict[name]
usr_sbin = None
add_drv = None
--- a/src/modules/actions/file.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/file.py Wed Feb 29 11:37:15 2012 -0800
@@ -29,13 +29,15 @@
This module contains the FileAction class, which represents a file-type
packaging object."""
+import errno
+import generic
import os
-import errno
+import stat
import tempfile
-import stat
-import generic
+import types
import zlib
+import _common
import pkg.actions
import pkg.client.api_errors as api_errors
import pkg.misc as misc
@@ -59,13 +61,12 @@
unique_attrs = "path", "mode", "owner", "group", "preserve"
globally_identical = True
namespace_group = "path"
+ ordinality = generic._orderdict[name]
has_payload = True
- def __init__(self, data=None, **attrs):
- generic.Action.__init__(self, data, **attrs)
- self.hash = "NOHASH"
- self.replace_required = False
+ # __init__ is provided as a native function (see end of class
+ # declaration).
# this check is only needed on Windows
if portable.ostype == "windows":
@@ -509,25 +510,15 @@
# Attempt to remove the file.
self.remove_fsobj(pkgplan, path)
- def different(self, other):
+ def different(self, other, cmp_hash=True):
# Override the generic different() method to ignore the file
# hash for ELF files and compare the ELF hash instead.
# XXX This should be modularized and controlled by policy.
# One of these isn't an ELF file, so call the generic method
- if "elfhash" not in self.attrs or "elfhash" not in other.attrs:
- return generic.Action.different(self, other)
-
- sset = set(self.attrs.keys())
- oset = set(other.attrs.keys())
- if sset.symmetric_difference(oset):
- return True
-
- for a in self.attrs:
- if self.attrs[a] != other.attrs[a]:
- return True
-
- return False
+ if "elfhash" in self.attrs and "elfhash" in other.attrs:
+ cmp_hash = False
+ return generic.Action.different(self, other, cmp_hash=cmp_hash)
def generate_indices(self):
"""Generates the indices needed by the search dictionary. See
@@ -600,3 +591,5 @@
if errors:
raise pkg.actions.InvalidActionAttributesError(self,
errors, fmri=fmri)
+
+FileAction.__init__ = types.MethodType(_common._file_init, None, FileAction)
--- a/src/modules/actions/generic.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/generic.py Wed Feb 29 11:37:15 2012 -0800
@@ -37,14 +37,22 @@
os.SEEK_SET
except AttributeError:
os.SEEK_SET, os.SEEK_CUR, os.SEEK_END = range(3)
+import stat
+import types
+
+import _common
import pkg.actions
import pkg.client.api_errors as apx
import pkg.portable as portable
import pkg.variant as variant
-import stat
-# Used to define sort order between action types.
-_ORDER_DICT_LIST = [
+# Directories must precede all filesystem object actions; hardlinks must follow
+# all filesystem object actions (except links). Note that user and group
+# actions precede file actions (so that the system permits chown'ing them to
+# users and groups that may be delivered during the same operation); this
+# implies that /etc/group and /etc/passwd file ownership needs to be part of
+# initial contents of those files.
+_orderdict = dict((k, i) for i, k in enumerate((
"set",
"depend",
"group",
@@ -55,9 +63,10 @@
"link",
"driver",
"unknown",
+ "license",
"legacy",
"signature"
-]
+)))
# EmptyI for argument defaults; no import to avoid pkg.misc dependency.
EmptyI = tuple()
@@ -119,7 +128,7 @@
files.
"""
- __slots__ = ["attrs", "data", "ord"]
+ __slots__ = ["attrs", "data"]
# 'name' is the name of the action, as specified in a manifest.
name = "generic"
@@ -140,110 +149,23 @@
# the sole members of their group, this is set to a non-None value for
# subclasses by the NSG metaclass.
namespace_group = None
+ # 'ordinality' is a numeric value that is used during action comparison
+ # to determine action sorting.
+ ordinality = _orderdict["unknown"]
# 'unique_attrs' is a tuple listing the attributes which must be
# identical in order for an action to be safely delivered multiple times
# (for those that can be).
unique_attrs = ()
- # the following establishes the sort order between action types.
- # Directories must precede all
- # filesystem-modifying actions; hardlinks must follow all
- # filesystem-modifying actions. Note that usr/group actions
- # precede file actions; this implies that /etc/group and /etc/passwd
- # file ownership needs to be part of initial contents of those files
- orderdict = {}
- unknown = 0
-
# The version of signature used.
sig_version = 0
# Most types of actions do not have a payload.
has_payload = False
- # By default, leading slashes should be stripped from "path" attribute.
- _strip_path = True
-
__metaclass__ = NSG
- def loadorderdict(self):
- self.orderdict.update(dict((
- (pkg.actions.types[t], i)
- for i, t in enumerate(_ORDER_DICT_LIST)
- )))
- self.__class__.unknown = \
- self.orderdict[pkg.actions.types["unknown"]]
-
- def __init__(self, data=None, **attrs):
- """Action constructor.
-
- The optional 'data' argument may be either a string, a file-like
- object, or a callable. If it is a string, then it will be
- substituted with a callable that will return an open handle to
- the file represented by the string. Otherwise, if it is not
- already a callable, it is assumed to be a file-like object, and
- will be substituted with a callable that will return the object.
- If it is a callable, it will not be replaced at all.
-
- Any remaining named arguments will be treated as attributes.
- """
-
- if not self.orderdict:
- self.loadorderdict()
-
-
- # A try/except is used here instead of get() as it is
- # consistently 2% faster.
- try:
- self.ord = self.orderdict[type(self)]
- except KeyError:
- self.ord = self.unknown
-
- self.attrs = attrs
-
- # Since this is a hot path, avoid a function call unless
- # absolutely necessary.
- if data is None:
- self.data = None
- else:
- self.set_data(data)
-
- if not self.key_attr:
- # Nothing more to do.
- return
-
- # Test if method only string object will have is defined to
- # determine if key attribute has been specified multiple times.
- try:
- self.attrs[self.key_attr].decode
- except KeyError:
- if self.name == "set" or self.name == "signature":
- # Special case for set and signature actions
- # since they allow two forms of syntax.
- return
- raise pkg.actions.InvalidActionError(str(self),
- _("no value specified for key attribute '%s'") %
- self.key_attr)
- except AttributeError:
- if self.name != "depend" or \
- self.attrs.get("type") != "require-any":
- # Assume list since fromstr() will only ever
- # provide a string or list and decode method
- # wasn't found. This is much faster than
- # checking isinstance or 'type() ==' in
- # a hot path.
- raise pkg.actions.InvalidActionError(str(self),
- _("%s attribute may only be specified "
- "once") % self.key_attr)
-
- if self._strip_path:
- # Strip leading slash from path if requested.
- try:
- self.attrs["path"] = \
- self.attrs["path"].lstrip("/")
- except KeyError:
- return
- if not self.attrs["path"]:
- raise pkg.actions.InvalidActionError(
- str(self), _("Empty path attribute"))
+ # __init__ is provided as a native function (see end of class
+ # declaration).
def set_data(self, data):
"""This function sets the data field of the action.
@@ -330,18 +252,33 @@
computed. This may need to be done externally.
"""
+ sattrs = self.attrs.keys()
out = self.name
- if hasattr(self, "hash") and self.hash is not None:
- if "=" not in self.hash:
- out += " " + self.hash
- else:
- self.attrs["hash"] = self.hash
+ try:
+ h = self.hash
+ if h:
+ if "=" not in h:
+ out += " " + h
+ else:
+ sattrs.append("hash")
+ except AttributeError:
+ # No hash to stash.
+ pass
# Sort so that we get consistent action attribute ordering.
# We pay a performance penalty to do so, but it seems worth it.
- for k in sorted(self.attrs.keys()):
- v = self.attrs[k]
- if isinstance(v, list) or isinstance(v, set):
+ sattrs.sort()
+
+ for k in sattrs:
+ try:
+ v = self.attrs[k]
+ except KeyError:
+ # If we can't find the attribute, it must be the
+ # hash. 'h' will only be in scope if the block
+ # at the start succeeded.
+ v = h
+
+ if type(v) is list:
out += " " + " ".join([
"=".join((k, quote_attr_value(lmt)))
for lmt in v
@@ -357,10 +294,6 @@
else:
out += " " + k + "=" + v
- # If we have a hash attribute, it's because we added it above;
- # get rid of it now.
- self.attrs.pop("hash", None)
-
return out
def __repr__(self):
@@ -402,7 +335,7 @@
# We pay a performance penalty to do so, but it seems worth it.
for k in sorted(self.attrs.keys()):
v = self.attrs[k]
- if isinstance(v, list) or isinstance(v, set):
+ if type(v) is list:
out += " " + " ".join([
"%s=%s" % (k, q(lmt)) for lmt in sorted(v)
])
@@ -439,20 +372,16 @@
"""Compare actions for ordering. The ordinality of a
given action is computed and stored at action
initialization."""
- if not isinstance(other, Action):
- return NotImplemented
- res = cmp(self.ord, other.ord)
-
+ res = cmp(self.ordinality, other.ordinality)
if res == 0:
return self.compare(other) # often subclassed
-
return res
def compare(self, other):
return cmp(id(self), id(other))
- def different(self, other):
+ def different(self, other, cmp_hash=True):
"""Returns True if other represents a non-ignorable change from
self.
@@ -463,25 +392,35 @@
# We could ignore key_attr, or possibly assert that it's the
# same.
- sset = set(self.attrs.keys())
- oset = set(other.attrs.keys())
+ sattrs = self.attrs
+ oattrs = other.attrs
+ sset = set(sattrs.iterkeys())
+ oset = set(oattrs.iterkeys())
if sset.symmetric_difference(oset):
return True
- for a in self.attrs:
- x = self.attrs[a]
- y = other.attrs[a]
- if isinstance(x, list) and \
- isinstance(y, list):
- if sorted(x) != sorted(y):
+ for a, x in sattrs.iteritems():
+ y = oattrs[a]
+ if x != y:
+ if len(x) == len(y) and \
+ type(x) is list and type(y) is list:
+ if sorted(x) != sorted(y):
+ return True
+ else:
return True
- elif x != y:
- return True
- if hasattr(self, "hash"):
- assert(hasattr(other, "hash"))
- if self.hash != other.hash:
- return True
+ if cmp_hash:
+ shash = ohash = None
+ try:
+ shash = self.hash
+ ohash = other.hash
+ if shash != other.hash:
+ return True
+ except AttributeError:
+ if shash or ohash:
+ raise AssertionError("attempt to "
+ "compare a %s action to a %s "
+ "action") % (self.name, other.name)
return False
@@ -655,15 +594,12 @@
"""Return the names of any facet or variant tags in this
action."""
- variants = []
- facets = []
-
- for k in self.attrs.iterkeys():
- if k[:8] == "variant.":
- variants.append(k)
- if k[:6] == "facet.":
- facets.append(k)
- return variants, facets
+ # Hot path; grab reference to attrs and use list comprehensions
+ # to construct the results. This is faster than iterating over
+ # attrs once and appending to two lists separately.
+ attrs = self.attrs
+ return [k for k in attrs if k[:8] == "variant."], \
+ [k for k in attrs if k[:6] == "facet."]
def get_variant_template(self):
"""Return the VariantCombinationTemplate that the variant tags
@@ -953,11 +889,13 @@
def attrlist(self, name):
"""return list containing value of named attribute."""
- value = self.attrs.get(name, [])
- if isinstance(value, list):
- return value
- else:
- return [ value ]
+ try:
+ value = self.attrs[name]
+ except KeyError:
+ return []
+ if type(value) is not list:
+ return [value]
+ return value
def directory_references(self):
"""Returns references to paths in action."""
@@ -1096,9 +1034,8 @@
errors = []
for attr in self.attrs:
if ((attr.startswith("facet.") or
- attr.startswith("variant.") or
attr == "reboot-needed" or attr in single_attrs) and
- isinstance(self.attrs[attr], list)):
+ type(self.attrs[attr]) is list):
errors.append((attr, _("%s may only be "
"specified once") % attr))
elif attr in numeric_attrs:
@@ -1163,3 +1100,5 @@
"try again.") % locals()
raise apx.ActionExecutionError(self, details=err_txt,
fmri=fmri)
+
+Action.__init__ = types.MethodType(_common._generic_init, None, Action)
--- a/src/modules/actions/group.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/group.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
#
"""module describing a user packaging object
@@ -50,9 +50,7 @@
name = "group"
key_attr = "groupname"
globally_identical = True
-
- def __init__(self, data=None, **attrs):
- generic.Action.__init__(self, data, **attrs)
+ ordinality = generic._orderdict[name]
def extract(self, attrlist):
""" return a dictionary containing attrs in attr list
--- a/src/modules/actions/hardlink.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/hardlink.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
#
"""module describing a (hard) link packaging object
@@ -30,6 +30,7 @@
packaging object."""
import errno
+import generic
import link
import os
import stat
@@ -43,10 +44,7 @@
__slots__ = []
name = "hardlink"
-
- # Tells parent class that leading slash should not be stripped
- # from "path" attribute.
- _strip_path = False
+ ordinality = generic._orderdict[name]
def get_target_path(self):
""" return a path for target that is relative to image"""
--- a/src/modules/actions/legacy.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/legacy.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
#
"""module describing a legacy packaging object
@@ -50,6 +50,7 @@
"version", "basedir", "pkginst", "pstamp", "sunw_prodvers")
refcountable = True
globally_identical = True
+ ordinality = generic._orderdict[name]
def directory_references(self):
return [os.path.normpath(os.path.join("var/sadm/pkg",
--- a/src/modules/actions/license.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/license.py Wed Feb 29 11:37:15 2012 -0800
@@ -54,6 +54,7 @@
reverse_indices = ("license", )
refcountable = True
globally_identical = True
+ ordinality = generic._orderdict[name]
has_payload = True
--- a/src/modules/actions/link.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/link.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
#
"""module describing a (symbolic) link packaging object
@@ -51,6 +51,7 @@
globally_identical = True
refcountable = True
namespace_group = "path"
+ ordinality = generic._orderdict[name]
def install(self, pkgplan, orig):
"""Client-side method that installs a link."""
--- a/src/modules/actions/signature.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/signature.py Wed Feb 29 11:37:15 2012 -0800
@@ -45,6 +45,7 @@
name = "signature"
key_attr = "value"
+ ordinality = generic._orderdict[name]
def __init__(self, data, **attrs):
generic.Action.__init__(self, data, **attrs)
--- a/src/modules/actions/unknown.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/unknown.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
#
"""module describing a unknown packaging object
@@ -39,3 +39,4 @@
__slots__ = []
name = "unknown"
+ ordinality = generic._orderdict[name]
--- a/src/modules/actions/user.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/actions/user.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
#
"""module describing a user packaging object
@@ -48,6 +48,7 @@
name = "user"
key_attr = "username"
globally_identical = True
+ ordinality = generic._orderdict[name]
# if these values are different on disk than in action
# prefer on-disk version
--- a/src/modules/bundle/SolarisPackageDatastreamBundle.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/bundle/SolarisPackageDatastreamBundle.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
#
import os
@@ -166,7 +166,7 @@
act = hardlink.HardLinkAction(path=mapline.pathname,
target=mapline.target)
elif mapline.type == "i" and mapline.pathname == "copyright":
- act = license.LicenseAction(data=ci.extractfile(),
+ act = license.LicenseAction(ci.extractfile(),
license="%s.copyright" % self.pkgname)
act.hash = "install/copyright"
elif mapline.type == "i":
--- a/src/modules/bundle/SolarisPackageDirBundle.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/bundle/SolarisPackageDirBundle.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
#
import os
@@ -281,7 +281,7 @@
name = name.replace("_", "-")
legacy_attrs[name] = pkginfo[key]
- actions.append(LegacyAction(data=None, **legacy_attrs))
+ actions.append(LegacyAction(**legacy_attrs))
if "DESC" in pkginfo:
actions.append(AttributeAction(name="pkg.description",
--- a/src/modules/catalog.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/catalog.py Wed Feb 29 11:37:15 2012 -0800
@@ -516,8 +516,8 @@
for pub, stem, entry in self.__iter_entries(last=last,
ordered=ordered, pubs=pubs):
- f = fmri.PkgFmri("%s@%s" % (stem, entry["version"]),
- publisher=pub)
+ f = fmri.PkgFmri(name=stem, publisher=pub,
+ version=entry["version"])
if cb is None or cb(f, entry):
yield f, entry
@@ -538,8 +538,8 @@
ver_list = self.__data[pub].get(name, ())
for entry in ver_list:
sver = entry["version"]
- pfmri = fmri.PkgFmri("%s@%s" % (name,
- sver), publisher=pub)
+ pfmri = fmri.PkgFmri(name=name, publisher=pub,
+ version=sver)
versions[sver] = pfmri.version
entries.setdefault(sver, [])
@@ -576,8 +576,8 @@
if objects:
for pub, stem, entry in self.__iter_entries(last=last,
ordered=ordered, pubs=pubs):
- yield fmri.PkgFmri("%s@%s" % (stem,
- entry["version"]), publisher=pub)
+ yield fmri.PkgFmri(name=stem, publisher=pub,
+ version=entry["version"])
return
for pub, stem, entry in self.__iter_entries(last=last,
@@ -605,8 +605,8 @@
for entry in ver_list:
sver = entry["version"]
- pfmri = fmri.PkgFmri("%s@%s" % (name,
- sver), publisher=pub)
+ pfmri = fmri.PkgFmri(name=name, publisher=pub,
+ version=sver)
versions[sver] = pfmri.version
entries.setdefault(sver, [])
@@ -1060,8 +1060,8 @@
if key.startswith("catalog."):
mdata[key] = entry[key]
op_time = basic_ts_to_datetime(entry["op-time"])
- pfmri = fmri.PkgFmri("%s@%s" % (stem, entry["version"]),
- publisher=pub)
+ pfmri = fmri.PkgFmri(name=stem, publisher=pub,
+ version=entry["version"])
return (pfmri, entry["op-type"], op_time, mdata)
for pub in self.publishers():
@@ -1368,14 +1368,15 @@
for f, entry in self.__entries(cb=cb, info_needed=info_needed,
locales=locales, last_version=last_version,
ordered=ordered, pubs=pubs):
- if "actions" in entry:
+ try:
yield f, self.__gen_actions(f, entry["actions"],
excludes)
- elif self.__manifest_cb:
- yield f, self.__gen_lazy_actions(f, info_needed,
- locales, excludes)
- else:
- yield f, EmptyI
+ except KeyError:
+ if self.__manifest_cb:
+ yield f, self.__gen_lazy_actions(f,
+ info_needed, locales, excludes)
+ else:
+ yield f, EmptyI
def __append(self, src, cb=None, pfmri=None, pubs=EmptyI):
"""Private version; caller responsible for locking."""
@@ -1588,8 +1589,8 @@
if not isinstance(pfmri, fmri.PkgFmri):
# pfmri is assumed to be a FMRI tuple.
pub, stem, ver = pfmri
- pfmri = fmri.PkgFmri("%s@%s" % (stem,
- ver), publisher=pub)
+ pfmri = fmri.PkgFmri(name=stem,
+ publisher=pub, version=ver)
e.fmri = pfmri
errors.append(e)
continue
@@ -1800,7 +1801,8 @@
npat = fmri.MatchingPkgFmri(pat_stem,
brelease)
else:
- npat = fmri.PkgFmri(pat_stem, brelease)
+ npat = fmri.PkgFmri(pat_stem,
+ brelease)
if not pat_ver:
# Do nothing.
@@ -2554,19 +2556,20 @@
for r, entry in self.__entries(cb=cb, info_needed=info_needed,
locales=locales, last_version=last, ordered=ordered,
pubs=pubs, tuples=True):
- if "actions" in entry:
+ try:
yield (r, entry,
self.__gen_actions(r, entry["actions"],
excludes))
- elif self.__manifest_cb:
- pub, stem, ver = r
- f = fmri.PkgFmri("%s@%s" % (stem, ver),
- publisher=pub)
- yield (r, entry,
- self.__gen_lazy_actions(f, info_needed,
- locales, excludes))
- else:
- yield r, entry, EmptyI
+ except KeyError:
+ if self.__manifest_cb:
+ pub, stem, ver = r
+ f = fmri.PkgFmri(name=stem, publisher=pub,
+ version=ver)
+ yield (r, entry,
+ self.__gen_lazy_actions(f, info_needed,
+ locales, excludes))
+ else:
+ yield r, entry, EmptyI
@property
def exists(self):
@@ -2773,14 +2776,15 @@
if entry is None:
raise api_errors.UnknownCatalogEntry(pfmri.get_fmri())
- if "actions" in entry:
+ try:
return self.__gen_actions(pfmri, entry["actions"],
excludes)
- elif self.__manifest_cb:
- return self.__gen_lazy_actions(pfmri, info_needed,
- locales, excludes)
- else:
- return EmptyI
+ except KeyError:
+ if self.__manifest_cb:
+ return self.__gen_lazy_actions(pfmri,
+ info_needed, locales, excludes)
+ else:
+ return EmptyI
def get_entry_all_variants(self, pfmri):
"""A generator function that yields tuples of the format
@@ -2793,13 +2797,14 @@
if entry is None:
raise api_errors.UnknownCatalogEntry(pfmri.get_fmri())
- if "actions" in entry:
+ try:
actions = self.__gen_actions(pfmri, entry["actions"])
- elif self.__manifest_cb:
- actions = self.__gen_lazy_actions(pfmri,
- info_needed)
- else:
- return
+ except KeyError:
+ if self.__manifest_cb:
+ actions = self.__gen_lazy_actions(pfmri,
+ info_needed)
+ else:
+ return
for a in actions:
if a.name != "set":
@@ -3099,8 +3104,8 @@
# Return the requested package data.
if return_fmris:
- pfmri = fmri.PkgFmri("%s@%s" % (stem, ver),
- build_release=brelease, publisher=pub)
+ pfmri = fmri.PkgFmri(build_release=brelease,
+ name=stem, publisher=pub, version=ver)
yield (pfmri, states, attrs)
else:
yield (t, states, attrs)
@@ -3747,8 +3752,8 @@
base = self.get_part(self.__BASE_PART, must_exist=True)
if base is None:
if not pfmri:
- pfmri = fmri.PkgFmri("%s@%s" % (stem, ver),
- publisher=pub)
+ pfmri = fmri.PkgFmri(name=stem, publisher=pub,
+ version=ver)
raise api_errors.UnknownCatalogEntry(pfmri.get_fmri())
# get_entry returns the actual catalog entry, so updating it
@@ -3756,8 +3761,8 @@
entry = base.get_entry(pfmri=pfmri, pub=pub, stem=stem, ver=ver)
if entry is None:
if not pfmri:
- pfmri = fmri.PkgFmri("%s@%s" % (stem, ver),
- publisher=pub)
+ pfmri = fmri.PkgFmri(name=stem, publisher=pub,
+ version=ver)
raise api_errors.UnknownCatalogEntry(pfmri.get_fmri())
if metadata is None:
if "metadata" in entry:
@@ -3993,8 +3998,8 @@
else:
assert pattern != None
# XXX "5.11" here needs to be saner
- tuples[pattern] = \
- fmri.PkgFmri(pattern, "5.11").tuple()
+ tuples[pattern] = fmri.PkgFmri(fmri=pattern,
+ build_release="5.11").tuple()
def by_pattern(p):
cat_pub, cat_name = p.tuple()[:2]
--- a/src/modules/client/api.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/client/api.py Wed Feb 29 11:37:15 2012 -0800
@@ -3536,8 +3536,8 @@
ranked_stems.setdefault(stem, pub)
if return_fmris:
- pfmri = fmri.PkgFmri("%s@%s" % (stem, ver),
- build_release=brelease, publisher=pub)
+ pfmri = fmri.PkgFmri(build_release=brelease,
+ name=stem, publisher=pub, version=ver)
yield (pfmri, summ, pcats, states, attrs)
else:
yield (t, summ, pcats, states, attrs)
--- a/src/modules/client/image.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/client/image.py Wed Feb 29 11:37:15 2012 -0800
@@ -3125,8 +3125,8 @@
pub=pub, stem=stem, ver=ver)
nipart.add(metadata=entry, op_time=op_time,
pub=pub, stem=stem, ver=ver)
- final_fmris.append(pkg.fmri.PkgFmri(
- "%s@%s" % (stem, ver), publisher=pub))
+ final_fmris.append(pkg.fmri.PkgFmri(name=stem,
+ publisher=pub, version=ver))
# Save the new catalogs.
for cat in kcat, icat:
--- a/src/modules/client/imageplan.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/client/imageplan.py Wed Feb 29 11:37:15 2012 -0800
@@ -1102,19 +1102,22 @@
# Don't bother accounting for implicit directories if we're not
# looking for them.
- if implicit_dirs and atype != "dir":
- implicit_dirs = False
-
+ if implicit_dirs:
+ if atype != "dir":
+ implicit_dirs = False
+ else:
+ da = pkg.actions.directory.DirectoryAction
+
for pfmri in generator():
m = self.image.get_manifest(pfmri, ignore_excludes=True)
- dirs = set() # Keep track of explicit dirs
+ if implicit_dirs:
+ dirs = set() # Keep track of explicit dirs
for act in m.gen_actions_by_type(atype,
self.__new_excludes):
if implicit_dirs:
dirs.add(act.attrs["path"])
yield act, pfmri
if implicit_dirs:
- da = pkg.actions.directory.DirectoryAction
for d in m.get_directories(self.__new_excludes):
if d not in dirs:
yield da(path=d, implicit="true"), pfmri
@@ -1132,11 +1135,15 @@
# Don't bother accounting for implicit directories if we're not
# looking for them.
- if implicit_dirs and atype != "dir":
- implicit_dirs = False
+ if implicit_dirs:
+ if atype != "dir":
+ implicit_dirs = False
+ else:
+ da = pkg.actions.directory.DirectoryAction
for pfmri, m in generator():
- dirs = set() # Keep track of explicit dirs
+ if implicit_dirs:
+ dirs = set() # Keep track of explicit dirs
for act in m.gen_actions_by_type(atype,
excludes):
if implicit_dirs:
@@ -1144,7 +1151,6 @@
yield act, pfmri
if implicit_dirs:
- da = pkg.actions.directory.DirectoryAction
for d in m.get_directories(excludes):
if d not in dirs:
yield da(path=d,
@@ -1154,15 +1160,19 @@
""" return set of all directories in target image """
# always consider var and the image directory fixed in image...
if self.__directories == None:
- dirs = set([self.image.imgdir.rstrip("/"),
- "var",
- "var/sadm",
- "var/sadm/install"])
- dirs.update((
+ # It's faster to build a large set and make a small
+ # update to it than to do the reverse.
+ dirs = set((
os.path.normpath(d[0].attrs["path"])
for d in self.gen_new_installed_actions_bytype("dir",
implicit_dirs=True)
))
+ dirs.update([
+ self.image.imgdir.rstrip("/"),
+ "var",
+ "var/sadm",
+ "var/sadm/install"
+ ])
self.__directories = dirs
return self.__directories
@@ -1533,10 +1543,10 @@
d = {}
for klass in action_classes:
+ self.__progtrack.evaluate_progress()
for a, pfmri in \
gen_func(klass.name, implicit_dirs=True,
excludes=excludes):
- self.__progtrack.evaluate_progress()
d.setdefault(a.attrs[klass.key_attr],
[]).append((a, pfmri))
return d
@@ -1706,7 +1716,7 @@
oactions = old.get(key, [])
# If new actions are being installed, then we need to do
# the full conflict checking.
- if len(oactions) == 0:
+ if not oactions:
continue
unmatched_old_actions = set(range(0, len(oactions)))
@@ -1714,8 +1724,9 @@
# If the action isn't refcountable and there's more than
# one action, that's an error so we let
# __check_conflicts handle it.
- if not actions[0][0].refcountable and \
- actions[0][0].globally_identical and \
+ entry = actions[0][0]
+ if not entry.refcountable and \
+ entry.globally_identical and \
len(actions) > 1:
continue
@@ -1724,16 +1735,18 @@
next_key = False
for act, pfmri in actions:
matched = False
+ aname = act.name
+ aattrs = act.attrs
# Compare this action with each outgoing action.
for i, (oact, opfmri) in enumerate(oactions):
- if act.name != oact.name:
+ if aname != oact.name:
continue
# Check whether all attributes which
# need to be unique are identical for
# these two actions.
+ oattrs = oact.attrs
if all((
- act.attrs.get(a, None) ==
- oact.attrs.get(a, None)
+ aattrs.get(a) == oattrs.get(a)
for a in act.unique_attrs
)):
matched = True
@@ -1759,8 +1772,8 @@
if act.name != oact.name:
continue
if all((
- act.attrs.get(a, None) ==
- oact.attrs.get(a, None)
+ act.attrs.get(a) ==
+ oact.attrs.get(a)
for a in act.unique_attrs
)):
matched = True
@@ -1817,8 +1830,8 @@
# Multiple non-refcountable actions delivered to
# the same name is an error.
- if not actions[0][0].refcountable and \
- actions[0][0].globally_identical:
+ entry = actions[0][0]
+ if not entry.refcountable and entry.globally_identical:
if self.__process_conflicts(key,
self.__check_duplicate_actions,
actions, oactions,
@@ -1829,7 +1842,7 @@
# Multiple refcountable but globally unique
# actions delivered to the same name must be
# identical.
- elif actions[0][0].globally_identical:
+ elif entry.globally_identical:
if self.__process_conflicts(key,
self.__check_inconsistent_attrs,
actions, oactions,
@@ -3027,9 +3040,11 @@
self.update_actions.extend(l_refresh)
# sort actions to match needed processing order
- self.removal_actions.sort(key = lambda obj:obj[1], reverse=True)
- self.update_actions.sort(key = lambda obj:obj[2])
- self.install_actions.sort(key = lambda obj:obj[2])
+ remsort = operator.itemgetter(1)
+ addsort = operator.itemgetter(2)
+ self.removal_actions.sort(key=remsort, reverse=True)
+ self.update_actions.sort(key=addsort)
+ self.install_actions.sort(key=addsort)
# Pre-calculate size of data retrieval for preexecute().
npkgs = nfiles = nbytes = 0
--- a/src/modules/client/pkg_solver.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/client/pkg_solver.py Wed Feb 29 11:37:15 2012 -0800
@@ -36,6 +36,7 @@
import pkg.version as version
from collections import defaultdict
+from itertools import chain
from pkg.client.debugvalues import DebugValues
from pkg.misc import EmptyI, EmptyDict, N_
@@ -96,9 +97,9 @@
self.__req_pkg_names = set() # package names that must be
# present in solution by spec.
for f in self.__installed_fmris: # record only sticky pubs
- pub = f.get_publisher()
+ pub = f.publisher
if self.__pub_ranks[pub][1]:
- self.__publisher[f.pkg_name] = f.get_publisher()
+ self.__publisher[f.pkg_name] = pub
self.__id2fmri = {} # map ids -> fmris
self.__fmri2id = {} # and reverse
@@ -114,6 +115,7 @@
self.__variants = variants # variants supported by image
self.__cache = {}
+ self.__depcache = {}
self.__trimdone = False # indicate we're finished
# trimming
self.__fmri_state = {} # cache of obsolete, renamed
@@ -125,7 +127,6 @@
self.__variables = 0
self.__timings = []
self.__start_time = 0
- self.__dep_dict = {}
self.__inc_list = []
self.__dependents = None
self.__root_fmris = None # set of fmris installed in root image;
@@ -196,10 +197,10 @@
self.__variant_dict = None
self.__variants = None
self.__cache = None
+ self.__depcache = None
self.__trimdone = None
self.__fmri_state = None
self.__start_time = None
- self.__dep_dict = None
self.__dependents = None
self.__fmridict = {}
@@ -293,7 +294,7 @@
self.__mark_pub_trimmed(name)
else:
self.__publisher[name] = \
- proposed_dict[name][0].get_publisher()
+ proposed_dict[name][0].publisher
self.__removal_fmris |= set([
self.__installed_dict[name]
@@ -990,29 +991,120 @@
"""Returns tuple of set of fmris that are matched within
CONSTRAINT.NONE of specified version and set of remaining
fmris."""
- return self.__comb_common(fmri, dotrim,
- version.CONSTRAINT_NONE, obsolete_ok)
+
+ if dotrim or not obsolete_ok or not fmri.version or \
+ not fmri.version.timestr:
+ return self.__comb_common(fmri, dotrim,
+ version.CONSTRAINT_NONE, obsolete_ok)
+
+ # In the case that a precise version (down to timestamp) is
+ # provided, no trimming is being done, and obsoletes are ok, the
+ # set of matching FMRIs can be determined without using version
+ # comparison because the solver caches catalog FMRIs in
+ # ascending version order.
+
+ # determine if the data is cacheable or cached:
+ tp = (fmri, dotrim, version.CONSTRAINT_NONE, obsolete_ok)
+ try:
+ return self.__cache[tp]
+ except KeyError:
+ pass
+
+ mver = fmri.version
+ all_fmris = self.__get_catalog_fmris(fmri.pkg_name)
+ all_fmris.reverse()
+
+ # frozensets are used so callers don't inadvertently
+ # update these sets (which may be cached). Iteration is
+ # performed in descending version order with the
+ # assumption that systems are generally up-to-date so it
+ # should be faster to start at the end and look for the
+ # older version.
+ last_ver = None
+ for i, f in enumerate(all_fmris):
+ if mver == f.version:
+ last_ver = i
+ continue
+ elif last_ver is not None:
+ break
+
+ if last_ver is not None:
+ matching = frozenset(all_fmris[:last_ver + 1])
+ remaining = frozenset(all_fmris[last_ver + 1:])
+ else:
+ matching = frozenset()
+ remaining = frozenset(all_fmris)
+
+ # if we haven't finished trimming, don't cache this
+ if not self.__trimdone:
+ return matching, remaining
+
+ # cache the result
+ self.__cache[tp] = (matching, remaining)
+ return self.__cache[tp]
def __comb_common(self, fmri, dotrim, constraint, obsolete_ok):
"""Underlying impl. of other comb routines"""
+
tp = (fmri, dotrim, constraint, obsolete_ok) # cache index
# determine if the data is cacheable or cached:
if (not self.__trimdone and dotrim) or tp not in self.__cache:
-
# use frozensets so callers don't inadvertently update
# these sets (which may be cached).
- all_fmris = set(self.__get_catalog_fmris(fmri.pkg_name))
- matching = frozenset([
- f
- for f in all_fmris
- if f not in self.__trim_dict or not dotrim
- if not fmri.version or
- fmri.version == f.version or
- f.version.is_successor(fmri.version,
- constraint=constraint)
- if obsolete_ok or not self.__fmri_is_obsolete(f)
- ])
- remaining = frozenset(all_fmris - matching)
+ all_fmris = self.__get_catalog_fmris(fmri.pkg_name)
+ mver = fmri.version
+ if not mver:
+ matching = frozenset((
+ f
+ for f in all_fmris
+ if not dotrim or f not in self.__trim_dict
+ if obsolete_ok or not self.__fmri_is_obsolete(f)
+ ))
+ remaining = frozenset(set(all_fmris) - matching)
+ else:
+ all_fmris.reverse()
+
+ # Iteration is performed in descending version
+ # order with the assumption that systems are
+ # generally up-to-date so it should be faster to
+ # start at the end and look for the oldest
+ # version that matches.
+ first_ver = None
+ last_ver = None
+ for i, f in enumerate(all_fmris):
+ fver = f.version
+ if (fver.is_successor(mver,
+ constraint=constraint) or \
+ fver == mver):
+ if first_ver is None:
+ first_ver = i
+ last_ver = i
+ elif last_ver is not None:
+ break
+
+ if last_ver is not None:
+ # Oddly enough, it's a little bit faster
+ # to iterate through the slice of
+ # all_fmris again and store matches here
+ # instead of above. Perhaps variable
+ # scoping overhead is to blame?
+ matching = []
+ remaining = []
+ for f in all_fmris[first_ver:last_ver + 1]:
+ if ((not dotrim or
+ f not in self.__trim_dict) and
+ (obsolete_ok or not
+ self.__fmri_is_obsolete(f))):
+ matching.append(f)
+ else:
+ remaining.append(f)
+ matching = frozenset(matching)
+ remaining = frozenset(chain(remaining,
+ all_fmris[:first_ver],
+ all_fmris[last_ver + 1:]))
+ else:
+ matching = frozenset()
+ remaining = frozenset(all_fmris)
# if we haven't finished trimming, don't cache this
if not self.__trimdone:
@@ -1032,7 +1124,7 @@
else:
# we're going to return the older packages, so we need
# to make sure that any trimmed packages are removed
- # from the matching set and added to the nom-matching
+ # from the matching set and added to the non-matching
# ones.
trimmed_older = set([
f
@@ -1086,13 +1178,18 @@
"""Return list of all dependency actions for this fmri"""
try:
- return [
+ return self.__depcache[fmri]
+ except KeyError:
+ pass
+
+ try:
+ self.__depcache[fmri] = [
a
for a in self.__catalog.get_entry_actions(fmri,
[catalog.Catalog.DEPENDENCY], excludes=excludes)
if a.name == "depend"
]
-
+ return self.__depcache[fmri]
except api_errors.InvalidPackageErrors:
if trim_invalid:
# Trim package entries that have unparseable
@@ -1831,7 +1928,7 @@
else:
# order by pub_rank; choose highest possible tier for
# pkgs; guard against unconfigured publishers in known catalog
- pubs_found = list(set([f.get_publisher() for f in fmri_list]))
+ pubs_found = set((f.publisher for f in fmri_list))
ranked = sorted([
(self.__pub_ranks[p][0], p)
for p in pubs_found
@@ -1846,27 +1943,19 @@
else:
reason = N_("Package publisher is ranked lower in search order")
- # generate a dictionary, indexed by version, of acceptable fmris
- for f in fmri_list:
- if f.get_publisher() in acceptable_pubs:
- version_dict.setdefault(f.version, []).append(f)
-
# allow installed packages to co-exist to meet dependency reqs.
# in case new publisher not proper superset of original.
# avoid multiple publishers w/ the exact same fmri to prevent
# thrashing in the solver due to many equiv. solutions.
-
- inst_f = self.__installed_dict.get(pkg_name, None)
-
- if inst_f:
- version_dict[inst_f.version] = [inst_f]
-
- acceptable_list = []
- for l in version_dict.values():
- acceptable_list.extend(l)
-
- for f in set(fmri_list) - set(acceptable_list):
- self.__trim(f, reason)
+ inst_f = self.__installed_dict.get(pkg_name)
+ self.__trim((
+ f
+ for f in fmri_list
+ if (f.publisher not in acceptable_pubs and
+ (not inst_f or f != inst_f)) or
+ (inst_f and f.publisher != inst_f.publisher and
+ f.version == inst_f.version)
+ ), reason)
# routines to manage the trim dictionary
# trim dictionary contains the reasons an fmri was rejected for consideration
--- a/src/modules/facet.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/facet.py Wed Feb 29 11:37:15 2012 -0800
@@ -26,9 +26,11 @@
# basic facet support
+from pkg._varcet import _allow_facet
from pkg.misc import EmptyI
import fnmatch
import re
+import types
class Facets(dict):
# store information on facets; subclass dict
@@ -68,6 +70,9 @@
def __getitem__(self, item):
"""implement facet lookup algorithm here"""
+ # Note that _allow_facet bypasses __getitem__ for performance
+ # reasons; if __getitem__ changes, _allow_facet in _varcet.c
+ # must also be updated.
if not item.startswith("facet."):
raise KeyError, "key must start w/ facet."
@@ -84,6 +89,9 @@
self.__keylist.remove(item)
del self.__res[item]
+ # allow_action is provided as a native function (see end of class
+ # declaration).
+
def pop(self, item, *args, **kwargs):
assert len(args) == 0 or (len(args) == 1 and
"default" not in kwargs)
@@ -134,18 +142,4 @@
self.__res = []
dict.clear(self)
- def allow_action(self, action):
- """ determine if facets permit this action; if any facets allow
- it, return True; also return True if no facets are present"""
-
- ret = True
-
- for f in action.attrs.keys():
- if f[:6] != "facet.":
- continue
- if self[f]:
- return True
- else:
- ret = False
-
- return ret
+Facets.allow_action = types.MethodType(_allow_facet, None, Facets)
--- a/src/modules/flavor/base.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/flavor/base.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
#
import os
@@ -121,6 +121,11 @@
])
attrs.update(action.get_variant_template())
+ # Only lists are permitted for multi-value action attributes.
+ for k, v in attrs.items():
+ if isinstance(v, set):
+ attrs[k] = list(v)
+
depend.DependencyAction.__init__(self, **attrs)
def is_error(self):
--- a/src/modules/fmri.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/fmri.py Wed Feb 29 11:37:15 2012 -0800
@@ -114,26 +114,39 @@
__slots__ = ["version", "publisher", "pkg_name", "_hash", "__weakref__"]
- def __init__(self, fmri, build_release=None, publisher=None):
- fmri = fmri.rstrip()
+ def __init__(self, fmri=None, build_release=None, publisher=None,
+ name=None, version=None):
+ if fmri is not None:
+ fmri = fmri.rstrip()
- veridx, nameidx, pubidx = PkgFmri._gen_fmri_indexes(fmri)
+ veridx, nameidx, pubidx = \
+ PkgFmri._gen_fmri_indexes(fmri)
+
+ if pubidx != None:
+ # Always use publisher information provided in
+ # FMRI string. (It could be ""; pkg:///name is
+ # valid.)
+ publisher = fmri[pubidx:nameidx - 1]
- if veridx != None:
- try:
- self.version = Version(fmri[veridx + 1:],
- build_release)
- except VersionError, iv:
- raise IllegalFmri(fmri, IllegalFmri.BAD_VERSION,
- nested_exc=iv)
-
+ if veridx != None:
+ self.pkg_name = fmri[nameidx:veridx]
+ try:
+ self.version = Version(
+ fmri[veridx + 1:], build_release)
+ except VersionError, iv:
+ raise IllegalFmri(fmri,
+ IllegalFmri.BAD_VERSION,
+ nested_exc=iv)
+ else:
+ self.pkg_name = fmri[nameidx:]
+ self.version = None
else:
- self.version = veridx = None
-
- if pubidx != None:
- # Always use publisher information provided in FMRI
- # string. (It could be ""; pkg:///name is valid.)
- publisher = fmri[pubidx:nameidx - 1]
+ # pkg_name and version must be explicitly set.
+ self.pkg_name = name
+ if version:
+ self.version = Version(version, build_release)
+ else:
+ self.version = None
# Ensure publisher is always None if one was not specified.
if publisher:
@@ -141,11 +154,6 @@
else:
self.publisher = None
- if veridx != None:
- self.pkg_name = fmri[nameidx:veridx]
- else:
- self.pkg_name = fmri[nameidx:]
-
if not self.pkg_name:
raise IllegalFmri(fmri, IllegalFmri.SYNTAX_ERROR,
detail="Missing package name")
--- a/src/modules/lint/pkglint_action.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/lint/pkglint_action.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
#
from pkg.lint.engine import lint_fmri_successor
@@ -129,7 +129,12 @@
variants = action.get_variant_template()
variants.merge_unknown(pkg_vars)
- action.attrs.update(variants)
+ # Action attributes must be lists or strings.
+ for k, v in variants.iteritems():
+ if isinstance(v, set):
+ action.attrs[k] = list(v)
+ else:
+ action.attrs[k] = v
p = action.attrs[attr]
if p not in dic:
--- a/src/modules/manifest.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/manifest.py Wed Feb 29 11:37:15 2012 -0800
@@ -29,7 +29,8 @@
import hashlib
import os
import tempfile
-from itertools import groupby, chain, repeat
+from itertools import groupby, chain, repeat, izip
+from operator import itemgetter
import pkg.actions as actions
import pkg.client.api_errors as apx
@@ -83,13 +84,13 @@
def __init__(self, pfmri=None):
self.fmri = pfmri
+ self._cache = {}
+ self._facets = None # facets seen in package
+ self._variants = None # variants seen in package
self.actions = []
self.actions_bytype = {}
- self.variants = {} # variants seen in package
- self.facets = {} # facets seen in package
self.attributes = {} # package-wide attributes
self.signatures = EmptyDict
- self._cache = {}
self.excludes = EmptyI
def __str__(self):
@@ -136,10 +137,9 @@
def hashify(v):
"""handle key values that may be lists"""
- if isinstance(v, list):
- return frozenset(v)
- else:
+ if type(v) is not list:
return v
+ return frozenset(v)
sdict = dict(
((a.name, hashify(a.attrs.get(a.key_attr, id(a)))), a)
@@ -150,8 +150,8 @@
for a in origin.gen_actions(origin_exclude)
)
- sset = set(sdict.keys())
- oset = set(odict.keys())
+ sset = set(sdict.iterkeys())
+ oset = set(odict.iterkeys())
added = [(None, sdict[i]) for i in sset - oset]
removed = [(odict[i], None) for i in oset - sset]
@@ -167,11 +167,11 @@
# sorted list?
# singlesort = lambda x: x[0] or x[1]
- addsort = lambda x: x[1]
- remsort = lambda x: x[0]
- removed.sort(key = remsort, reverse = True)
- added.sort(key = addsort)
- changed.sort(key = addsort)
+ addsort = itemgetter(1)
+ remsort = itemgetter(0)
+ removed.sort(key=remsort, reverse=True)
+ added.sort(key=addsort)
+ changed.sort(key=addsort)
return ManifestDifference(added, changed, removed)
@@ -358,14 +358,14 @@
except KeyError:
# generate actions that contain directories
alist = self._cache["manifest.dircache"] = [
- actions.fromstr(s.strip())
+ actions.fromstr(s.rstrip())
for s in self._gen_dirs_to_str()
]
s = set([
a.attrs["path"]
for a in alist
- if a.include_this(excludes)
+ if not excludes or a.include_this(excludes)
])
return list(s)
@@ -386,7 +386,7 @@
except KeyError:
# generate actions that contain mediators
alist = self._cache["manifest.mediatorcache"] = [
- actions.fromstr(s.strip())
+ actions.fromstr(s.rstrip())
for s in self._gen_mediators_to_str()
]
@@ -394,7 +394,7 @@
for attrs in (
act.attrs
for act in alist
- if act.include_this(excludes)):
+ if not excludes or act.include_this(excludes)):
med_ver = attrs.get("mediator-version")
if med_ver:
try:
@@ -543,8 +543,8 @@
self.actions = []
self.actions_bytype = {}
- self.variants = {}
- self.facets = {}
+ self._variants = None
+ self._facets = None
self.attributes = {}
self._cache = {}
@@ -594,45 +594,39 @@
The "excludes" parameter is the variants to exclude from the
manifest."""
- # XXX handle legacy transition issues; not needed after
- # 2009.06 release & republication are complete.
- if "opensolaris.zone" in action.attrs and \
- "variant.opensolaris.zone" not in action.attrs:
- action.attrs["variant.opensolaris.zone"] = \
- action.attrs["opensolaris.zone"]
+ attrs = action.attrs
+ aname = action.name
- if action.name == "set" and action.attrs["name"] == "authority":
+ # XXX handle legacy transition issues; not needed once support
+ # for upgrading images from older releases (< build 151) has
+ # been removed.
+ if "opensolaris.zone" in attrs and \
+ "variant.opensolaris.zone" not in attrs:
+ attrs["variant.opensolaris.zone"] = \
+ attrs["opensolaris.zone"]
+
+ if aname == "set" and attrs["name"] == "authority":
# Translate old action to new.
- action.attrs["name"] = "publisher"
+ attrs["name"] = "publisher"
+
+ if excludes and not action.include_this(excludes):
+ return
- if action.attrs.has_key("path"):
- np = action.attrs["path"].lstrip(os.path.sep)
- action.attrs["path"] = np
-
- if not action.include_this(excludes):
- return
+ if self._variants:
+ # Reset facet/variant cache if needed (if one is set,
+ # then both are set, so only need to check for one).
+ self._facets = None
+ self._variants = None
self.actions.append(action)
- self.actions_bytype.setdefault(action.name, []).append(action)
+ try:
+ self.actions_bytype[aname].append(action)
+ except KeyError:
+ self.actions_bytype.setdefault(aname, []).append(action)
# add any set actions to attributes
- if action.name == "set":
+ if aname == "set":
self.fill_attributes(action)
- # append any variants and facets to manifest dict
- v_list, f_list = action.get_varcet_keys()
-
- if not (v_list or f_list):
- return
-
- try:
- for v, d in zip(v_list, repeat(self.variants)) \
- + zip(f_list, repeat(self.facets)):
- d.setdefault(v, set()).add(action.attrs[v])
- except TypeError:
- # Lists can't be set elements.
- raise actions.InvalidActionError(action,
- _("%(forv)s '%(v)s' specified multiple times") %
- {"forv": v.split(".", 1)[0], "v": v})
def fill_attributes(self, action):
"""Fill attribute array w/ set action contents."""
@@ -710,7 +704,8 @@
log((_("%(fp)s:\n%(e)s") %
{ "fp": file_path, "e": e }))
else:
- if action.include_this(excludes):
+ if not excludes or \
+ action.include_this(excludes):
if action.attrs.has_key("path"):
np = action.attrs["path"].lstrip(os.path.sep)
action.attrs["path"] = \
@@ -857,6 +852,44 @@
size += a.get_size()
return size
+ def __load_varcets(self):
+ """Private helper function to populate list of facets and
+ variants on-demand."""
+
+ self._facets = {}
+ self._variants = {}
+ for action in self.gen_actions():
+ # append any variants and facets to manifest dict
+ attrs = action.attrs
+ v_list, f_list = action.get_varcet_keys()
+
+ if not (v_list or f_list):
+ continue
+
+ try:
+ for v, d in chain(
+ izip(v_list, repeat(self._variants)),
+ izip(f_list, repeat(self._facets))):
+ try:
+ d[v].add(attrs[v])
+ except KeyError:
+ d[v] = set([attrs[v]])
+ except TypeError:
+ # Lists can't be set elements.
+ raise actions.InvalidActionError(action,
+ _("%(forv)s '%(v)s' specified multiple times") %
+ {"forv": v.split(".", 1)[0], "v": v})
+
+ def __get_facets(self):
+ if self._facets is None:
+ self.__load_varcets()
+ return self._facets
+
+ def __get_variants(self):
+ if self._variants is None:
+ self.__load_varcets()
+ return self._variants
+
def __getitem__(self, key):
"""Return the value for the package attribute 'key'."""
return self.attributes[key]
@@ -876,6 +909,9 @@
def __contains__(self, key):
return key in self.attributes
+ facets = property(lambda self: self.__get_facets())
+ variants = property(lambda self: self.__get_variants())
+
null = Manifest()
class FactoredManifest(Manifest):
@@ -964,8 +1000,8 @@
when downloading new manifests"""
self.actions = []
self.actions_bytype = {}
- self.variants = {}
- self.facets = {}
+ self._variants = None
+ self._facets = None
self.attributes = {}
self.loaded = False
@@ -1070,10 +1106,11 @@
self._cache[name] = [
a for a in
(
- actions.fromstr(s.strip())
+ actions.fromstr(s.rstrip())
for s in f
)
- if a.include_this(self.excludes)
+ if not self.excludes or
+ a.include_this(self.excludes)
]
except EnvironmentError, e:
raise apx._convert_error(e)
@@ -1122,12 +1159,12 @@
if not os.path.exists(mpath):
return # no such action in this manifest
- f = file(mpath)
- for l in f:
- a = actions.fromstr(l.strip())
- if a.include_this(excludes):
- yield a
- f.close()
+ with open(mpath, "rb") as f:
+ for l in f:
+ a = actions.fromstr(l.rstrip())
+ if not excludes or \
+ a.include_this(excludes):
+ yield a
def gen_mediators(self, excludes):
"""A generator function that yields set actions expressing the
@@ -1143,12 +1180,12 @@
mpath = self.__cache_path("manifest.set")
if not os.path.exists(mpath):
return False
- f = file(mpath)
- for l in f:
- a = actions.fromstr(l.strip())
- if a.include_this(self.excludes):
- self.fill_attributes(a)
- f.close()
+ with open(mpath, "rb") as f:
+ for l in f:
+ a = actions.fromstr(l.rstrip())
+ if not self.excludes or \
+ a.include_this(self.excludes):
+ self.fill_attributes(a)
return True
def __getitem__(self, key):
--- a/src/modules/p5p.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/p5p.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
#
import atexit
@@ -798,8 +798,8 @@
ignored, stem, ver = name.rsplit("/", 2)
stem = urllib.unquote(stem)
ver = urllib.unquote(ver)
- pfmri = pkg.fmri.PkgFmri("%s@%s" % (stem, ver),
- publisher=pub)
+ pfmri = pkg.fmri.PkgFmri(name=stem,
+ publisher=pub, version=ver)
fobj = self.get_file(name)
m = pkg.manifest.Manifest(pfmri=pfmri)
--- a/src/modules/publish/dependencies.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/publish/dependencies.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
#
import copy
@@ -267,15 +267,25 @@
"""Convert pkg.base.Dependency objects to
pkg.actions.dependency.Dependency objects."""
+ def norm_attrs(a):
+ """Normalize attribute values as lists instead of sets; only
+ lists are permitted for multi-value action attributes."""
+ for k, v in a.items():
+ if isinstance(v, set):
+ a[k] = list(v)
+
res = []
for d in deps:
tmp = []
for c in d.dep_vars.not_sat_set:
attrs = d.attrs.copy()
attrs.update(c)
+ norm_attrs(attrs)
tmp.append(actions.depend.DependencyAction(**attrs))
if not tmp:
- tmp.append(actions.depend.DependencyAction(**d.attrs))
+ attrs = d.attrs.copy()
+ norm_attrs(attrs)
+ tmp.append(actions.depend.DependencyAction(**attrs))
res.extend(tmp)
return res
--- a/src/modules/variant.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/variant.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,15 +21,17 @@
#
#
-# Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
#
# basic variant support
import copy
import itertools
+import types
+
from collections import namedtuple
-
+from pkg._varcet import _allow_variant
from pkg.misc import EmptyI
class Variants(dict):
@@ -50,6 +52,9 @@
dict.__delitem__(self, item)
self.__keyset.remove(item)
+ # allow_action is provided as a native function (see end of class
+ # declaration).
+
def pop(self, item, default=None):
self.__keyset.discard(item)
return dict.pop(self, item, default)
@@ -75,25 +80,8 @@
self.__keyset = set()
dict.clear(self)
- # Methods which are unique to variants
- def allow_action(self, action):
- """ determine if variants permit this action """
-
- for k, v in action.attrs.iteritems():
- if k[:8] != "variant.":
- continue
- sys_v = self.get(k, None)
+Variants.allow_action = types.MethodType(_allow_variant, None, Variants)
- # If the system value and the action variant value
- # agree, then the action is allowed. Otherwise, if the
- # system value doesn't exist, check to see if the
- # action's variant is a debug variant. If it is, allow
- # the action if the variant value is set to false.
- if sys_v != v and (sys_v is not None or
- (k[:14] == "variant.debug." and v != "false")):
- return False
-
- return True
# The two classes which follow are used during dependency calculation when
# actions have variants, or the packages they're contained in do. The
--- a/src/modules/version.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/modules/version.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
#
import calendar
@@ -29,6 +29,8 @@
import time
import weakref
+from itertools import izip
+
CONSTRAINT_NONE = 0
CONSTRAINT_AUTO = 50
@@ -76,12 +78,10 @@
return x
def __new__(cls, dotstring):
- ds = DotSequence.__dotseq_pool.get(dotstring, None)
- if ds is not None:
- return ds
-
- ds = list.__new__(cls)
- cls.__dotseq_pool[dotstring] = ds
+ ds = DotSequence.__dotseq_pool.get(dotstring)
+ if ds is None:
+ cls.__dotseq_pool[dotstring] = ds = \
+ list.__new__(cls)
return ds
def __init__(self, dotstring):
@@ -113,7 +113,7 @@
if len(self) > len(other):
return False
- for a, b in zip(self, other):
+ for a, b in izip(self, other):
if a != b:
return False
return True
@@ -219,7 +219,7 @@
if len(self) > len(other):
return False
- for a, b in zip(self, other):
+ for a, b in izip(self, other):
if a != b:
return False
return True
--- a/src/pkg/manifests/package:pkg.p5m Wed Feb 29 11:26:51 2012 -0800
+++ b/src/pkg/manifests/package:pkg.p5m Wed Feb 29 11:37:15 2012 -0800
@@ -18,7 +18,7 @@
#
# CDDL HEADER END
#
-# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
#
set name=pkg.fmri value=pkg:/package/pkg@$(PKGVERS)
@@ -36,9 +36,11 @@
dir path=$(PYDIRVP)/pkg
file path=$(PYDIRVP)/pkg-0.1-py2.6.egg-info
file path=$(PYDIRVP)/pkg/__init__.py
+file path=$(PYDIRVP)/pkg/_varcet.so
dir path=$(PYDIRVP)/pkg/actions
file path=$(PYDIRVP)/pkg/actions/__init__.py
file path=$(PYDIRVP)/pkg/actions/_actions.so
+file path=$(PYDIRVP)/pkg/actions/_common.so
file path=$(PYDIRVP)/pkg/actions/attribute.py
file path=$(PYDIRVP)/pkg/actions/depend.py
file path=$(PYDIRVP)/pkg/actions/directory.py
--- a/src/setup.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/setup.py Wed Feb 29 11:37:15 2012 -0800
@@ -19,7 +19,7 @@
#
# CDDL HEADER END
#
-# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
#
import errno
@@ -422,6 +422,12 @@
_actions_srcs = [
'modules/actions/_actions.c'
]
+_actcomm_srcs = [
+ 'modules/actions/_common.c'
+ ]
+_varcet_srcs = [
+ 'modules/_varcet.c'
+ ]
solver_srcs = [
'modules/solver/solver.c',
'modules/solver/py_solver.c'
@@ -516,6 +522,14 @@
["%s%s" % ("-I", k) for k in include_dirs] + \
['-I' + self.escape(get_python_inc())] + \
_actions_srcs
+ _actcommcmd = ['lint'] + lint_flags + \
+ ["%s%s" % ("-I", k) for k in include_dirs] + \
+ ['-I' + self.escape(get_python_inc())] + \
+ _actcomm_srcs
+ _varcetcmd = ['lint'] + lint_flags + \
+ ["%s%s" % ("-I", k) for k in include_dirs] + \
+ ['-I' + self.escape(get_python_inc())] + \
+ _varcet_srcs
pspawncmd = ['lint'] + lint_flags + ['-D_FILE_OFFSET_BITS=64'] + \
["%s%s" % ("-I", k) for k in include_dirs] + \
['-I' + self.escape(get_python_inc())] + \
@@ -531,6 +545,10 @@
os.system(" ".join(elfcmd))
print(" ".join(_actionscmd))
os.system(" ".join(_actionscmd))
+ print(" ".join(_actcommcmd))
+ os.system(" ".join(_actcommcmd))
+ print(" ".join(_varcetcmd))
+ os.system(" ".join(_varcetcmd))
print(" ".join(pspawncmd))
os.system(" ".join(pspawncmd))
print(" ".join(syscallatcmd))
@@ -1163,6 +1181,20 @@
extra_link_args = link_args
),
Extension(
+ 'actions._common',
+ _actcomm_srcs,
+ include_dirs = include_dirs,
+ extra_compile_args = compile_args,
+ extra_link_args = link_args
+ ),
+ Extension(
+ '_varcet',
+ _varcet_srcs,
+ include_dirs = include_dirs,
+ extra_compile_args = compile_args,
+ extra_link_args = link_args
+ ),
+ Extension(
'solver',
solver_srcs,
include_dirs = include_dirs + ["."],
--- a/src/tests/api/t_action.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/tests/api/t_action.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
#
import testutils
@@ -132,6 +132,17 @@
action.fromstr("signature 12345 algorithm=foo")
+ # For convenience, we allow set actions to be expressed as
+ # "<name>=<value>", rather than "name=<name> value=<value>", but
+ # we always convert to the latter. Verify that both forms are
+ # parsed as expected.
+ a = action.fromstr("set pkg.obsolete=true")
+ a2 = action.fromstr("set name=pkg.obsolete value=true")
+ self.assertEqual(str(a), str(a2))
+ self.assertAttributes(a, ["name", "value"])
+ self.assertAttributeValue(a, "name", "pkg.obsolete")
+ self.assertAttributeValue(a, "value", "true")
+
# Single quoted value
a = action.fromstr("file 12345 name='foo' value=bar path=/tmp/foo")
self.assertAttributes(a, ["name", "value", "path"])
@@ -347,9 +358,12 @@
# Unknown action type
self.assertRaises(action.UnknownActionError, action.fromstr,
"moop bar=baz")
+ self.assertRaises(action.UnknownActionError, action.fromstr,
+ "setbar=baz quux=quark")
- # Nothing but the action type
+ # Nothing but the action type or type is malformed.
self.assertMalformed("moop")
+ self.assertMalformed("setbar=baz")
# Bad quoting: missing close quote
self.assertMalformed("file 12345 path=/tmp/foo name=\"foo bar")
@@ -457,7 +471,7 @@
# Verify multiple values for file attributes are rejected.
for attr in ("pkg.size", "pkg.csize", "chash", "preserve",
"overlay", "elfhash", "original_name", "facet.doc",
- "variant.count", "owner", "group"):
+ "owner", "group"):
nact = "file path=/usr/bin/foo owner=root group=root " \
"mode=0555 %(attr)s=1 %(attr)s=2 %(attr)s=3" % {
"attr": attr }
--- a/src/tests/api/t_manifest.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/tests/api/t_manifest.py Wed Feb 29 11:37:15 2012 -0800
@@ -101,11 +101,6 @@
file fff555ff9 mode=0555 owner=sch group=staff path=/usr/bin/i386/sort isa=i386
"""
- self.m6_contents = """\
-dir owner=root path=usr/bin group=bin mode=0755 variant.opensolaris.zone=global variant.opensolaris.zone=nonglobal
-"""
-
-
def test_set_content1(self):
""" ASSERT: manifest string repr reflects its construction """
@@ -140,13 +135,6 @@
self.assertEqual(bstr, output)
self.assert_(isinstance(output, str))
- def test_variants(self):
- """Make sure we reject multiple instances of the same
- variant."""
-
- self.assertRaises(actions.InvalidActionError,
- self.m1.set_content, self.m6_contents)
-
def test_diffs1(self):
""" humanized_differences runs to completion """
--- a/src/tests/cli/t_pkgdep.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/tests/cli/t_pkgdep.py Wed Feb 29 11:37:15 2012 -0800
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
-# Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
import testutils
if __name__ == "__main__":
@@ -1414,18 +1414,19 @@
""" pkgdep should gracefully deal with a non-manifest """
m_path = None
+ atype = "This"
nonsense = "This is a nonsense manifest"
m_path = self.make_manifest(nonsense)
self.pkgdepend_generate("-d %s %s" %
(self.test_proto_dir, m_path), exit=1)
self.debug(self.errout)
- self.check_res("Malformed action at position: 9:\n" + nonsense +
- "\n ^\n", self.errout)
+ self.check_res("unknown action type '%s' in action '%s'" %
+ (atype, nonsense), self.errout)
self.pkgdepend_resolve("-o %s " % m_path, exit=1)
- self.check_res("Malformed action at position: 9:\n" + nonsense +
- "\n ^\n", self.errout)
+ self.check_res("unknown action type '%s' in action '%s'" %
+ (atype, nonsense), self.errout)
def __run_dyn_tok_test(self, run_paths, replaced_path, dep_args):
"""Using the provided run paths, produces a elf binary with
--- a/src/tests/perf/actionbench.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/tests/perf/actionbench.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
#
#
@@ -75,15 +75,15 @@
setup3 = """
class superc(object):
def __cmp__(a, b):
- return cmp(a.ord, b.ord)
+ return cmp(a.ordinality, b.ordinality)
class aa(superc):
def __init__(self):
- self.ord = 1
+ self.ordinality = 1
class bb(superc):
def __init__(self):
- self.ord = 2
+ self.ordinality = 2
a = aa()
b = bb()
--- a/src/tests/perf/fmribench.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/tests/perf/fmribench.py Wed Feb 29 11:37:15 2012 -0800
@@ -20,9 +20,9 @@
# CDDL HEADER END
#
+
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
#
#
@@ -94,11 +94,16 @@
"""hash(f1)"""
],
- [ "fmri create", 50000,
+ [ "fmri create (string)", 50000,
"""import pkg.fmri as fmri""",
"""f = fmri.PkgFmri("pkg://origin/[email protected],5.11-0.72:20070921T203926Z")"""
],
+ [ "fmri create (parts)", 50000,
+ """import pkg.fmri as fmri""",
+ """f = fmri.PkgFmri(build_release="5.11", publisher="origin", name="SUNWxwssu", version="0.5.11,5.11-0.72:20070921T203926Z")"""
+ ],
+
[ "fmri create (no tstamp)", 50000,
"""import pkg.fmri as fmri""",
"""f = fmri.PkgFmri("pkg://origin/[email protected],0.5.11-0.90")"""
--- a/src/util/publish/pkgdiff.py Wed Feb 29 11:26:51 2012 -0800
+++ b/src/util/publish/pkgdiff.py Wed Feb 29 11:37:15 2012 -0800
@@ -21,7 +21,7 @@
#
#
-# Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
#
import getopt
@@ -204,7 +204,7 @@
if res:
return res
else:
- res = cmp(a.ord, b.ord)
+ res = cmp(a.ordinality, b.ordinality)
if res:
return res
return cmp(str(a), str(b))