manifest.py revision 1044
409N/A#!/usr/bin/python2.4
39N/A#
39N/A# CDDL HEADER START
39N/A#
39N/A# The contents of this file are subject to the terms of the
39N/A# Common Development and Distribution License (the "License").
39N/A# You may not use this file except in compliance with the License.
39N/A#
39N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
39N/A# or http://www.opensolaris.org/os/licensing.
39N/A# See the License for the specific language governing permissions
39N/A# and limitations under the License.
39N/A#
39N/A# When distributing Covered Code, include this CDDL HEADER in each
39N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
39N/A# If applicable, add the following below this CDDL HEADER, with the
39N/A# fields enclosed by brackets "[]" replaced with your own identifying
39N/A# information: Portions Copyright [yyyy] [name of copyright owner]
39N/A#
39N/A# CDDL HEADER END
39N/A#
926N/A
926N/A#
926N/A# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
39N/A# Use is subject to license terms.
926N/A#
39N/A
39N/Aimport os
342N/Aimport errno
838N/Afrom itertools import groupby, chain, repeat
956N/Afrom pkg.misc import EmptyI, expanddirs
39N/A
51N/Aimport pkg.actions as actions
296N/Afrom pkg.actions.attribute import AttributeAction
39N/A
39N/Aclass Manifest(object):
39N/A """A Manifest is the representation of the actions composing a specific
39N/A package version on both the client and the repository. Both purposes
39N/A utilize the same storage format.
39N/A
205N/A The serialized structure of a manifest is an unordered list of actions.
39N/A
205N/A The special action, "set", represents a package attribute.
39N/A
39N/A The reserved attribute, "fmri", represents the package and version
39N/A described by this manifest. It is available as a string via the
39N/A attributes dictionary, and as an FMRI object from the fmri member.
39N/A
39N/A The list of manifest-wide reserved attributes is
39N/A
39N/A base_directory Default base directory, for non-user images.
39N/A fmri Package FMRI.
39N/A isa Package is intended for a list of ISAs.
39N/A platform Package is intended for a list of platforms.
39N/A relocatable Suitable for User Image.
39N/A
39N/A All non-prefixed attributes are reserved to the framework. Third
39N/A parties may prefix their attributes with a reversed domain name, domain
39N/A name, or stock symbol. An example might be
39N/A
39N/A com.example,supported
39N/A
39N/A as an indicator that a specific package version is supported by the
39N/A vendor, example.com.
39N/A
48N/A manifest.null is provided as the null manifest. Differences against the
48N/A null manifest result in the complete set of attributes and actions of
48N/A the non-null manifest, meaning that all operations can be viewed as
48N/A tranitions between the manifest being installed and the manifest already
48N/A present in the image (which may be the null manifest).
59N/A """
48N/A
39N/A def __init__(self):
104N/A self.img = None
39N/A self.fmri = None
39N/A
39N/A self.actions = []
567N/A self.actions_bytype = {}
838N/A self.variants = {} # variants seen in package
838N/A self.facets = {} # facets seen in package
838N/A self.attributes = {} # package-wide attributes
39N/A
39N/A def __str__(self):
39N/A r = ""
956N/A if "fmri" not in self.attributes and self.fmri != None:
315N/A r += "set name=fmri value=%s\n" % self.fmri
39N/A
227N/A for act in sorted(self.actions):
315N/A r += "%s\n" % act
315N/A return r
39N/A
315N/A def tostr_unsorted(self):
315N/A r = ""
956N/A if "fmri" not in self.attributes and self.fmri != None:
315N/A r += "set name=fmri value=%s\n" % self.fmri
429N/A
315N/A for act in self.actions:
315N/A r += "%s\n" % act
39N/A return r
429N/A
39N/A
926N/A def difference(self, origin, origin_exclude=EmptyI,
926N/A self_exclude=EmptyI):
203N/A """Return three lists of action pairs representing origin and
203N/A destination actions. The first list contains the pairs
203N/A representing additions, the second list contains the pairs
203N/A representing updates, and the third list contains the pairs
203N/A represnting removals. All three lists are in the order in which
203N/A they should be executed."""
72N/A # XXX Do we need to find some way to assert that the keys are
72N/A # all unique?
59N/A
72N/A sdict = dict(
72N/A ((a.name, a.attrs.get(a.key_attr, id(a))), a)
838N/A for a in self.gen_actions(self_exclude)
72N/A )
72N/A odict = dict(
72N/A ((a.name, a.attrs.get(a.key_attr, id(a))), a)
838N/A for a in origin.gen_actions(origin_exclude)
72N/A )
59N/A
72N/A sset = set(sdict.keys())
72N/A oset = set(odict.keys())
59N/A
72N/A added = [(None, sdict[i]) for i in sset - oset]
72N/A removed = [(odict[i], None) for i in oset - sset]
307N/A # XXX for now, we force license actions to always be
307N/A # different to insure that existing license files for
307N/A # new versions are always installed
72N/A changed = [
72N/A (odict[i], sdict[i])
72N/A for i in oset & sset
237N/A if odict[i].different(sdict[i]) or i[0] == "license"
72N/A ]
59N/A
72N/A # XXX Do changed actions need to be sorted at all? This is
72N/A # likely to be the largest list, so we might save significant
72N/A # time by not sorting. Should we sort above? Insert into a
72N/A # sorted list?
59N/A
72N/A # singlesort = lambda x: x[0] or x[1]
72N/A addsort = lambda x: x[1]
72N/A remsort = lambda x: x[0]
202N/A removed.sort(key = remsort, reverse = True)
72N/A added.sort(key = addsort)
72N/A changed.sort(key = addsort)
59N/A
202N/A return (added, changed, removed)
59N/A
838N/A @staticmethod
838N/A def comm(*compare_m):
838N/A """Like the unix utility comm, except that this function
838N/A takes an arbitrary number of manifests and compares them,
838N/A returning a tuple consisting of each manifest's actions
926N/A that are not the same for all manifests, followed by a
838N/A list of actions that are the same in each manifest."""
838N/A
838N/A # construct list of dictionaries of actions in each
838N/A # manifest, indexed by unique keys
926N/A m_dicts = [
838N/A dict(
926N/A ((a.name, a.attrs.get(a.key_attr, id(a))), a)
926N/A for a in m.actions)
926N/A for m in compare_m
838N/A ]
838N/A # construct list of key sets in each dict
838N/A #
838N/A m_sets = [
838N/A set(m.keys())
838N/A for m in m_dicts
838N/A ]
838N/A
838N/A common_keys = reduce(lambda a, b: a & b, m_sets)
926N/A
838N/A # determine which common_keys have common actions
838N/A for k in common_keys.copy():
838N/A for i in range(len(m_dicts) - 1):
838N/A if m_dicts[i][k].different(
838N/A m_dicts[i + 1][k]):
838N/A common_keys.remove(k)
838N/A break
845N/A return tuple(
845N/A [
926N/A [m_dicts[i][k] for k in m_sets[i] - common_keys]
838N/A for i in range(len(m_dicts))
845N/A ]
845N/A +
845N/A [
838N/A [ m_dicts[0][k] for k in common_keys ]
845N/A ]
845N/A )
838N/A
838N/A
926N/A def combined_difference(self, origin, ov=EmptyI, sv=EmptyI):
203N/A """Where difference() returns three lists, combined_difference()
315N/A returns a single list of the concatenation of the three."""
838N/A return list(chain(*self.difference(origin, ov, sv)))
203N/A
838N/A def humanized_differences(self, other, ov=EmptyI, sv=EmptyI):
48N/A """Output expects that self is newer than other. Use of sets
48N/A requires that we convert the action objects into some marshalled
48N/A form, otherwise set member identities are derived from the
48N/A object pointers, rather than the contents."""
48N/A
838N/A l = self.difference(other, ov, sv)
203N/A out = ""
48N/A
203N/A for src, dest in chain(*l):
72N/A if not src:
181N/A out += "+ %s\n" % str(dest)
72N/A elif not dest:
181N/A out += "- %s\n" + str(src)
46N/A else:
181N/A out += "%s -> %s\n" % (src, dest)
237N/A return out
46N/A
838N/A def gen_actions(self, excludes=EmptyI):
838N/A """Generate actions in manifest through ordered callable list"""
838N/A for a in self.actions:
838N/A for c in excludes:
838N/A if not c(a):
838N/A break
838N/A else:
838N/A yield a
342N/A
926N/A def gen_actions_by_type(self, atype, excludes=EmptyI):
838N/A """Generate actions in the manifest of type "type"
838N/A through ordered callable list"""
926N/A for a in self.actions_bytype.get(atype, []):
838N/A for c in excludes:
838N/A if not c(a):
838N/A break
838N/A else:
838N/A yield a
926N/A
926N/A def gen_key_attribute_value_by_type(self, atype, excludes=EmptyI):
615N/A """Generate the value of the key atrribute for each action
615N/A of type "type" in the manifest."""
615N/A
615N/A return (
615N/A a.attrs.get(a.key_attr)
926N/A for a in self.gen_actions_by_type(atype, excludes)
615N/A )
615N/A
838N/A def duplicates(self, excludes=EmptyI):
111N/A """Find actions in the manifest which are duplicates (i.e.,
111N/A represent the same object) but which are not identical (i.e.,
111N/A have all the same attributes)."""
111N/A
111N/A def fun(a):
111N/A """Return a key on which actions can be sorted."""
111N/A return a.name, a.attrs.get(a.key_attr, id(a))
111N/A
113N/A alldups = []
926N/A acts = [a for a in self.gen_actions(excludes)]
838N/A
926N/A for k, g in groupby(sorted(acts, key=fun), fun):
113N/A glist = list(g)
113N/A dups = set()
113N/A for i in range(len(glist) - 1):
113N/A if glist[i].different(glist[i + 1]):
113N/A dups.add(glist[i])
113N/A dups.add(glist[i + 1])
113N/A if dups:
113N/A alldups.append((k, dups))
113N/A return alldups
111N/A
104N/A def set_fmri(self, img, fmri):
104N/A self.img = img
39N/A self.fmri = fmri
39N/A
926N/A def set_content(self, content, excludes=EmptyI):
926N/A """content is the text representation of the manifest"""
877N/A self.actions = []
877N/A self.actions_bytype = {}
926N/A self.variants = {}
926N/A self.facets = {}
926N/A self.attributes = {}
926N/A
72N/A # So we could build up here the type/key_attr dictionaries like
72N/A # sdict and odict in difference() above, and have that be our
72N/A # main datastore, rather than the simple list we have now. If
72N/A # we do that here, we can even assert that the "same" action
72N/A # can't be in a manifest twice. (The problem of having the same
72N/A # action more than once in packages that can be installed
72N/A # together has to be solved somewhere else, though.)
926N/A for l in content.splitlines():
146N/A l = l.lstrip()
146N/A if not l or l[0] == "#":
50N/A continue
51N/A
51N/A try:
51N/A action = actions.fromstr(l)
591N/A except actions.ActionError, e:
591N/A # Add the FMRI to the exception and re-raise
591N/A e.fmri = self.fmri
591N/A raise
51N/A
926N/A if action.name == "set" and \
926N/A action.attrs["name"] == "authority":
926N/A # Translate old action to new.
926N/A action.attrs["name"] = "publisher"
926N/A
123N/A if action.attrs.has_key("path"):
123N/A np = action.attrs["path"].lstrip(os.path.sep)
123N/A action.attrs["path"] = np
123N/A
941N/A if not action.include_this(excludes):
877N/A continue
877N/A
429N/A self.actions.append(action)
39N/A
567N/A if action.name not in self.actions_bytype:
567N/A self.actions_bytype[action.name] = [ action ]
567N/A else:
567N/A self.actions_bytype[action.name].append(action)
838N/A # add any set actions to attributes
838N/A if action.name == "set":
956N/A self.fill_attributes(action)
838N/A # append any variants and facets to manifest dict
838N/A v_list, f_list = action.get_varcet_keys()
838N/A
838N/A if v_list or f_list:
838N/A for v, d in zip(v_list, repeat(self.variants)) \
838N/A + zip(f_list, repeat(self.facets)):
838N/A if v not in d:
838N/A d[v] = set([action.attrs[v]])
838N/A else:
838N/A d[v].add(action.attrs[v])
39N/A return
39N/A
956N/A def fill_attributes(self, action):
956N/A """Fill attribute array w/ set action contents."""
956N/A try:
956N/A keyvalue = action.attrs["name"]
956N/A if keyvalue not in self.attributes:
956N/A self.attributes[keyvalue] = \
956N/A action.attrs["value"]
956N/A except KeyError: # ignore broken set actions
956N/A pass
956N/A
956N/A
941N/A @staticmethod
1007N/A def search_dict(file_path, excludes, return_line=False,
1007N/A log=None):
1007N/A """Produces the search dictionary for a specific manifest.
1007N/A A dictionary is constructed which maps a tuple of token,
1007N/A action type, key, and the value that matched the token to
1007N/A the byte offset into the manifest file. file_path is the
1007N/A path to the manifest file. excludes is the variants which
1007N/A should be allowed in this image. return_line is a debugging
1007N/A flag which makes the function map the information to the
1007N/A string of the line, rather than the byte offset to allow
1007N/A easier debugging."""
1007N/A
1007N/A if log is None:
1007N/A log = lambda x: None
1007N/A
941N/A file_handle = file(file_path)
941N/A cur_pos = 0
941N/A line = file_handle.readline()
144N/A action_dict = {}
941N/A def __handle_list(lst, cp):
941N/A for action_name, subtype, tok, full_value in lst:
941N/A if action_name == "set":
941N/A if full_value is None:
941N/A full_value = tok
941N/A else:
941N/A if full_value is None:
941N/A full_value = subtype
941N/A if full_value is None:
941N/A full_value = action_name
941N/A if isinstance(tok, list):
941N/A __handle_list([
941N/A (action_name, subtype, t,
941N/A full_value)
941N/A for t in tok
941N/A ], cp)
429N/A else:
941N/A if (tok, action_name, subtype,
941N/A full_value) in action_dict:
941N/A action_dict[(tok, action_name,
941N/A subtype, full_value)
941N/A ].append(cp)
429N/A else:
941N/A action_dict[(tok, action_name,
941N/A subtype, full_value)] = [cp]
941N/A while line:
941N/A l = line.strip()
941N/A if l and l[0] != "#":
1007N/A try:
1007N/A action = actions.fromstr(l)
1007N/A except actions.MalformedActionError, e:
1007N/A log((_("%(fp)s:\n%(e)s") %
1007N/A { "fp": file_path, "e": e }))
1007N/A else:
1007N/A if action.include_this(excludes):
1007N/A if action.attrs.has_key("path"):
1007N/A np = action.attrs["path"].lstrip(os.path.sep)
1007N/A action.attrs["path"] = \
1007N/A np
1007N/A try:
1007N/A inds = action.generate_indices()
1007N/A except KeyError, k:
1007N/A log(_("%(fp)s contains "
1007N/A "an action which is"
1007N/A " missing the "
1007N/A "expected attribute"
1007N/A ": %(at)s.\nThe "
1007N/A "action is:"
1007N/A "%(act)s") %
1007N/A {
1007N/A "fp": file_path,
1007N/A "at": k.args[0],
1007N/A "act":l
1007N/A })
1007N/A else:
1007N/A arg = cur_pos
1007N/A if return_line:
1007N/A arg = l
1007N/A __handle_list(inds, arg)
941N/A cur_pos = file_handle.tell()
941N/A line = file_handle.readline()
941N/A file_handle.close()
144N/A return action_dict
144N/A
429N/A def store(self, mfst_path):
429N/A """Store the manifest contents to disk."""
144N/A
315N/A try:
315N/A mfile = file(mfst_path, "w")
315N/A except IOError:
342N/A try:
342N/A os.makedirs(os.path.dirname(mfst_path))
342N/A except OSError, e:
342N/A if e.errno != errno.EEXIST:
342N/A raise
315N/A mfile = file(mfst_path, "w")
315N/A
315N/A #
315N/A # We specifically avoid sorting manifests before writing
315N/A # them to disk-- there's really no point in doing so, since
315N/A # we'll sort actions globally during packaging operations.
315N/A #
315N/A mfile.write(self.tostr_unsorted())
315N/A mfile.close()
144N/A
838N/A def get_variants(self, name):
838N/A if name not in self.attributes:
838N/A return None
926N/A variants = self.attributes[name]
926N/A if not isinstance(variants, str):
926N/A return variants
926N/A return [variants]
838N/A
205N/A def get(self, key, default):
257N/A try:
257N/A return self[key]
257N/A except KeyError:
205N/A return default
205N/A
1044N/A def get_size(self, excludes=EmptyI):
1044N/A """Returns an integer representing the total size, in bytes, of
1044N/A the Manifest's data payload.
1044N/A
1044N/A 'excludes' is a list of variants which should be allowed when
1044N/A calculating the total.
1044N/A """
1044N/A
1044N/A size = 0
1044N/A for a in self.gen_actions(excludes):
1044N/A size += int(a.attrs.get("pkg.size", "0"))
1044N/A
1044N/A return size
1044N/A
205N/A def __getitem__(self, key):
838N/A """Return the value for the package attribute 'key'."""
838N/A return self.attributes[key]
205N/A
296N/A def __setitem__(self, key, value):
296N/A """Set the value for the package attribute 'key' to 'value'."""
838N/A self.attributes[key] = value
296N/A for a in self.actions:
296N/A if a.name == "set" and a.attrs["name"] == key:
296N/A a.attrs["value"] = value
296N/A return
296N/A
296N/A new_attr = AttributeAction(None, name=key, value=value)
296N/A self.actions.append(new_attr)
296N/A
205N/A def __contains__(self, key):
838N/A return key in self.attributes
205N/A
48N/Anull = Manifest()
956N/A
956N/Aclass CachedManifest(Manifest):
956N/A """This class handles a cache of manifests for the client;
956N/A it partitions the manifest into multiple files (one per
956N/A action type) and also builds an on-disk cache of the
956N/A directories explictly and implicitly referenced by the
956N/A manifest, tagging each one w/ the appropriate variants/facets."""
956N/A
956N/A def __file_path(self, file):
956N/A return os.path.join(self.__pkgdir,
956N/A self.fmri.get_dir_path(), file)
956N/A
1037N/A def __init__(self, fmri, pkgdir, preferred_pub, excludes=EmptyI, contents=None):
956N/A """Raises KeyError exception if cached manifest
956N/A is not present and contents are None; delays
956N/A reading of manifest until required if cache file
956N/A is present"""
956N/A
956N/A Manifest.__init__(self)
1037N/A self.__pkgdir = pkgdir
1037N/A self.__pub = preferred_pub
1037N/A self.loaded = False
956N/A self.set_fmri(None, fmri)
956N/A self.excludes = excludes
956N/A
956N/A mpath = self.__file_path("manifest")
956N/A
956N/A # Do we have a cached copy?
956N/A if not os.path.exists(mpath):
956N/A if not contents:
956N/A raise KeyError, fmri
956N/A # we have no cached copy; save one
956N/A # don't specify excludes so on-disk copy has
956N/A # all variants
956N/A self.set_content(contents)
956N/A self.__finiload()
967N/A if self.__storeback():
967N/A self.__unload()
967N/A elif excludes:
956N/A self.set_content(contents, excludes)
956N/A return
956N/A
956N/A # we have a cached copy of the manifest
956N/A mdpath = self.__file_path("manifest.dircache")
956N/A
956N/A # have we computed the dircache?
956N/A if not os.path.exists(mdpath): # we're adding cache
956N/A self.excludes = EmptyI # to existing manifest
956N/A self.__load()
967N/A if self.__storeback():
967N/A self.__unload()
967N/A elif excludes:
956N/A self.excludes = excludes
956N/A self.__load()
956N/A
956N/A def __load(self):
956N/A """Load all manifest contents from on-disk copy of manifest"""
956N/A f = file(self.__file_path("manifest"))
956N/A data = f.read()
956N/A f.close()
956N/A self.set_content(data, self.excludes)
956N/A self.__finiload()
956N/A
967N/A def __unload(self):
967N/A """Unload manifest; used to reduce peak memory comsumption
967N/A when downloading new manifests"""
967N/A self.actions = []
967N/A self.actions_bytype = {}
967N/A self.variants = {}
967N/A self.facets = {}
967N/A self.attributes = {}
967N/A self.loaded = False
967N/A
956N/A def __finiload(self):
956N/A """Finish loading.... this part of initialization is common
956N/A to multiple code paths"""
956N/A self.loaded = True
956N/A # this needs to change; we should not modify on-disk manifest
956N/A if "publisher" not in self.attributes:
956N/A if not self.fmri.has_publisher():
956N/A pub = self.__pub
956N/A else:
956N/A pub = self.fmri.get_publisher()
956N/A Manifest.__setitem__(self, "publisher", pub)
956N/A
956N/A def __storeback(self):
956N/A """ store the current action set; also create per-type
967N/A caches. Return True if data was saved, False if not"""
956N/A assert self.loaded
956N/A try:
956N/A self.store(self.__file_path("manifest"))
956N/A self.__storebytype()
967N/A return True
956N/A
956N/A except EnvironmentError, e:
956N/A # this allows us to try to cache new manifests
956N/A # when non-root w/o failures
956N/A if e.errno not in (errno.EROFS, errno.EACCES):
956N/A raise
967N/A return False
956N/A
956N/A def __storebytype(self):
956N/A """ create manifest.<typename> files to accelerate partial
956N/A parsing of manifests. Separate from __storeback code to
956N/A allow upgrade to reuse existing on disk manifests"""
956N/A
956N/A assert self.loaded
956N/A
956N/A # create per-action type cache; use rename to avoid
956N/A # corrupt files if ^C'd in the middle
956N/A # XXX consider use of per-process tmp file names
956N/A for n in self.actions_bytype.keys():
956N/A f = file(self.__file_path("manifest.%s.tmp" % n),
956N/A "w")
956N/A for a in self.actions_bytype[n]:
956N/A f.write("%s\n" % a)
956N/A f.close()
956N/A os.rename(self.__file_path("manifest.%s.tmp" % n),
956N/A self.__file_path("manifest.%s" % n))
956N/A # create dircache
956N/A f = file(self.__file_path("manifest.dircache.tmp"), "w")
956N/A dirs = self.__actions_to_dirs()
956N/A
956N/A for s in self.__gen_dirs_to_str(dirs):
956N/A f.write(s)
956N/A
956N/A f.close()
956N/A os.rename(self.__file_path("manifest.dircache.tmp"),
956N/A self.__file_path("manifest.dircache"))
956N/A
956N/A def __gen_dirs_to_str(self, dirs):
956N/A """ from a dictionary of paths, generate contents of dircache
956N/A file"""
956N/A for d in dirs:
956N/A for v in dirs[d]:
956N/A yield "dir path=%s %s\n" % \
956N/A (d, " ".join("%s=%s" % t \
956N/A for t in v.iteritems()))
956N/A
956N/A def __actions_to_dirs(self):
956N/A """ create dictionary of all directories referenced
956N/A by actions explicitly or implicitly from self.actions...
956N/A include variants as values; collapse variants where possible"""
956N/A assert self.loaded
956N/A
956N/A dirs = {}
956N/A # build a dictionary containing all directories tagged w/
956N/A # variants
956N/A for a in self.actions:
956N/A v, f = a.get_varcet_keys()
956N/A variants = dict((name, a.attrs[name]) for name in v + f)
956N/A for d in expanddirs(a.directory_references()):
956N/A if d not in dirs:
956N/A dirs[d] = [variants]
956N/A elif variants not in dirs[d]:
956N/A dirs[d].append(variants)
956N/A
956N/A # remove any tags if any entries are always installed (NULL)
956N/A for d in dirs:
956N/A if {} in dirs[d]:
956N/A dirs[d] = [{}]
956N/A continue
956N/A # could collapse dirs where all variants are present
956N/A return dirs
956N/A
956N/A def get_directories(self, excludes):
956N/A """ return a list of directories implicitly or
956N/A explicitly referenced by this object"""
956N/A
956N/A mpath = self.__file_path("manifest.dircache")
956N/A
956N/A if not os.path.exists(mpath):
956N/A # no cached copy
956N/A if not self.loaded:
956N/A # need to load from disk
956N/A self.__load()
956N/A # generate actions that contain directories
956N/A alist = [
964N/A actions.fromstr(s.strip())
956N/A for s in self.__gen_dirs_to_str(
964N/A self.__actions_to_dirs())
956N/A ]
956N/A else:
956N/A # we have cached copy on disk; use it
956N/A f = file(mpath)
956N/A alist = [actions.fromstr(s.strip()) for s in f]
956N/A f.close()
956N/A s = set([
956N/A a.attrs["path"]
956N/A for a in alist
956N/A if a.include_this(excludes)
956N/A ])
956N/A return list(s)
956N/A
956N/A def gen_actions_by_type(self, atype, excludes=EmptyI):
956N/A """ generate actions of the specified type;
956N/A use already in-memory stuff if already loaded,
956N/A otherwise use per-action types files"""
956N/A
956N/A if self.loaded: #if already loaded, use in-memory cached version
956N/A # invoke subclass method to generate action by action
956N/A for a in Manifest.gen_actions_by_type(self, atype,
956N/A excludes):
956N/A yield a
956N/A return
956N/A
956N/A mpath = self.__file_path("manifest.dircache")
956N/A
956N/A if not os.path.exists(mpath):
956N/A # no cached copy :-(
956N/A if not self.loaded:
956N/A # get manifest from disk
956N/A self.__load()
956N/A # invoke subclass method to generate action by action
956N/A for a in Manifest.gen_actions_by_type(self, atype,
956N/A excludes):
956N/A yield a
956N/A else:
956N/A # we have a cached copy - use it
956N/A mpath = self.__file_path("manifest.%s" % atype)
956N/A
956N/A if not os.path.exists(mpath):
956N/A return # no such action in this manifest
956N/A
956N/A f = file(mpath)
956N/A for l in f:
956N/A a = actions.fromstr(l.strip())
956N/A if a.include_this(excludes):
956N/A yield a
956N/A f.close()
956N/A
956N/A def __load_attributes(self):
956N/A """Load attributes dictionary from cached set actions;
956N/A this speeds up pkg info a lot"""
956N/A
956N/A mpath = self.__file_path("manifest.set")
956N/A if not os.path.exists(mpath):
956N/A return False
956N/A f = file(mpath)
956N/A for l in f:
956N/A a = actions.fromstr(l.strip())
956N/A if a.include_this(self.excludes):
956N/A self.fill_attributes(a)
956N/A f.close()
956N/A return True
956N/A
956N/A def __getitem__(self, key):
956N/A if not self.loaded and not self.__load_attributes():
956N/A self.__load()
956N/A return Manifest.__getitem__(self, key)
956N/A
956N/A def __setitem__(self, key, value):
956N/A """No assignments to cached manifests allowed."""
956N/A assert "CachedManifests are not dicts"
956N/A
956N/A def __contains__(self, key):
956N/A if not self.loaded and not self.__load_attributes():
956N/A self.__load()
956N/A return Manifest.__contains__(self, key)
956N/A
956N/A def get(self, key, default):
956N/A try:
956N/A return self[key]
956N/A except KeyError:
956N/A return default
956N/A
956N/A def get_variants(self, name):
956N/A if not self.loaded and not self.__load_attributes():
956N/A self.__load()
956N/A return Manifest.get_variants(self, name)
956N/A
956N/A @staticmethod
956N/A def search_dict(file_path, excludes, return_line=False):
956N/A if not self.loaded:
956N/A self.__load()
956N/A return Manifest.search_dict(file_path, excludes,
956N/A return_line=return_line)
956N/A
956N/A def gen_actions(self, excludes=EmptyI):
956N/A if not self.loaded:
956N/A self.__load()
956N/A return Manifest.gen_actions(self, excludes=excludes)
956N/A
956N/A def duplicates(self, excludes=EmptyI):
956N/A if not self.loaded:
956N/A self.__load()
956N/A return Manifest.duplicates(self, excludes=excludes)
956N/A
956N/A def difference(self, origin, origin_exclude=EmptyI, self_exclude=EmptyI):
956N/A if not self.loaded:
956N/A self.__load()
956N/A return Manifest.difference(self, origin,
956N/A origin_exclude=origin_exclude,
956N/A self_exclude=self_exclude)
956N/A
956N/A
964N/Aclass EmptyCachedManifest(Manifest):
964N/A """Special class for pkgplan's need for a empty manifest;
964N/A the regular null manifest doesn't support get_directories
964N/A and making the cached manifest code handle this case is
964N/A too ugly..."""
964N/A def __init__(self):
964N/A Manifest.__init__(self)
964N/A
964N/A def get_directories(self, excludes):
964N/A return []
964N/A
964N/ANullCachedManifest = EmptyCachedManifest()