import hashlib # Sentinal value for nondeterministic dependencies DISCORDIA = 'discordia' OMNISCIENCE = 'omniscience' REVISION = 'revision' def get_hash(s): if s is None: return None return hashlib.md5(s).digest() def get_seq_hash(l): return get_hash(u','.join(unicode(i) for i in sorted(l))) class Dependencies(set): def add(self, d): assert d in (DISCORDIA, OMNISCIENCE, REVISION) or len(d) == 3, d set.add(self, d) def addRawDep(self, ename, pname, hsh): self.add((ename, pname, hsh)) def makeUncacheable(self): self.add(DISCORDIA) def makeRestricted(self): self.add(OMNISCIENCE) def isRestricted(self): return OMNISCIENCE in self def addDep(self, propval): assert propval is not None self.addRawDep(propval.element.ename, propval.propname, get_hash(propval.value)) # TODO(xavid): rename and make sure this makes sense def addExistsDep(self, ename): self.addRawDep(ename, '__exists', None) def addNoPropvalDep(self, ename, pname): self.addRawDep(ename, pname, None) def addPropvalDep(self, e, pname): if hasattr(e, 'element'): # Reference e = e.element pv = e.get_propval(pname) if pv is None: self.addNoPropvalDep(e.ename, pname) else: self.addDep(pv) def addChildrenDep(self, e): self.addRawDep(e.ename, '__children', get_seq_hash(e.get_children())) def addParentDep(self, e): self.addRawDep(e.ename, '__parent', get_hash(e.get_parent_ename())) def addElementPropvalsDep(self, e): self.addRawDep(e.ename, '__propvals', get_seq_hash(e.list_props())) # A dependency that alwas gets re-evaled if tested, but is still cachable # for if the subversion revision hasn't changed. # TODO(xavid): It'd be cool to expand this to also pass if the element # hasn't changed, with this defined by what would affect # the yaml file in bazsvn. Would require some model-specific # version/logic. def addFragileDep(self, e): self.addRawDep(e.ename, '__fragile', None) # This is a truly fragile dep, for things we want to cache by revision # but don't want to bother validating if the revision's changed. def addRevisionDep(self): self.add(REVISION)