def callAtom(self, atom, arguments, namedArgsMap): """ This method is used to reuse atoms without having to rebuild them. """ # Promote the atom, on the basis that atoms are generally reused. atom = promote(atom) # Log the atom to the JIT log. Don't do this if the atom's not # promoted; it'll be slow. jit_debug(atom.repr) try: return self.recvNamed(atom, arguments, namedArgsMap) except Refused as r: addTrail(r, self, atom, arguments) raise except UserException as ue: addTrail(ue, self, atom, arguments) raise except MemoryError: ue = userError(u"Memory corruption or exhausted heap") addTrail(ue, self, atom, arguments) raise ue except StackOverflow: check_stack_overflow() ue = userError(u"Stack overflow") addTrail(ue, self, atom, arguments) raise ue
def recv(self, atom, args): if atom is RUN_1: return IntObject(int(unwrapStr(args[0]).encode('utf-8'))) if atom is RUN_2: inp = unwrapStr(args[0]) radix = unwrapInt(args[1]) try: v = int(inp.encode("utf-8"), radix) except ValueError: raise userError(u"Invalid literal for base %d: %s" % ( radix, inp)) return IntObject(v) if atom is FROMBYTES_1: return IntObject(int(unwrapBytes(args[0]))) if atom is FROMBYTES_2: bs = unwrapBytes(args[0]) radix = unwrapInt(args[1]) try: v = int(bs, radix) except ValueError: raise userError(u"Invalid literal for base %d: %s" % ( radix, bytesToString(bs))) return IntObject(v) raise Refused(self, atom, args)
def setTarget(self, newTarget): if self.isSwitchable: self._target = newTarget.resolutionRef() if self is self._target: raise userError(u"Ref loop") else: raise userError(u"No longer switchable")
def recv(self, atom, args): if atom is RUN_1: pname = unwrapStr(args[0]) for extension in [".ty", ".mast"]: path = pname.encode("utf-8") + extension for base in self.paths: try: with open(os.path.join(base, path), "rb") as handle: source = handle.read() with self.recorder.context("Deserialization"): return loadMASTBytes(source) except IOError: continue raise userError(u"Could not locate " + pname) if atom is RUN_2: scope = unwrapMap(args[1]) d = {} for k, v in scope.items(): s = unwrapStr(k) if not s.startswith("&&"): raise userError(u"evalMonteFile scope map must be of the " "form '[\"&&name\" => binding]'") d[s[2:]] = scope[k] code = obtainModule(self.paths, unwrapStr(args[0]).encode("utf-8"), self.recorder) return evaluateRaise([code], d)[0] raise Refused(self, atom, args)
def auditDeepFrozen(audition): from typhon.nodes import FinalPattern, Obj from typhon.objects.user import Audition from typhon.objects.printers import toString if not isinstance(audition, Audition): raise userError(u"not invoked with an Audition") ast = audition.ast if not isinstance(ast, Obj): raise userError(u"audition not created with an object expr") n = ast._n if isinstance(n, FinalPattern): objName = ast._n._n else: objName = None ss = ast._script.getStaticScope() namesUsed = ss.read + ss.set errors = [] for name in namesUsed: if name == objName: continue guard = audition.getGuard(name) if not deepFrozenSupersetOf(guard): errors.append(u'"%s" in the lexical scope of %s does not have a ' u'guard implying DeepFrozen, but %s' % (name, audition.fqn, toString(guard))) if len(errors) > 0: raise userError(u'\n'.join(errors))
def _slice(self, start, stop): if start < 0: raise userError(u"slice/1: Negative start") if stop < 0: raise userError(u"slice/2: Negative stop") stop = min(stop, len(self.objs)) start = min(start, stop) return self.objs[start:stop]
def get(self, index): # Lookup by index. if index < 0: raise userError(u"get/1: Index %d cannot be negative" % index) try: return self.objs[index] except IndexError: raise userError(u"get/1: Index %d is out of bounds" % index)
def takeTurn(self): from typhon.objects.exceptions import sealException from typhon.objects.refs import Promise, resolution with self._pendingLock: resolver, target, atom, args, namedArgs = self._pending.pop(0) # Set up our Miranda FAIL. if namedArgs.extractStringKey(u"FAIL", None) is None: if resolver is not None: FAIL = resolver.makeSmasher() else: from typhon.objects.ejectors import theThrower FAIL = theThrower namedArgs = namedArgs.withStringKey(u"FAIL", FAIL) # If the target is a promise, then we should send to it instead of # calling. Try to resolve it as much as possible first, though. target = resolution(target) # self.log(u"Taking turn: %s<-%s(%s) (resolver: %s)" % # (target.toQuote(), atom.verb, # u", ".join([arg.toQuote() for arg in args]), # u"yes" if resolver is not None else u"no")) try: if isinstance(target, Promise): if resolver is None: target.sendOnly(atom, args, namedArgs) else: result = target.send(atom, args, namedArgs) # The resolver may have already been invoked, so we'll use # .resolveRace/1 instead of .resolve/1. ~ C. resolver.resolveRace(result) else: result = target.callAtom(atom, args, namedArgs) if resolver is not None: # Same logic as above. resolver.resolveRace(result) except UserException as ue: if resolver is not None: resolver.smash(sealException(ue)) else: self.log(u"Uncaught user exception while taking turn" u" (and no resolver): %s" % ue.formatError().decode("utf-8"), tags=["serious"]) except VatCheckpointed: self.log(u"Ran out of checkpoints while taking turn", tags=["serious"]) if resolver is not None: resolver.smash(sealException(userError(u"Vat ran out of checkpoints"))) except Ejecting: self.log(u"Ejector tried to escape vat turn boundary", tags=["serious"]) if resolver is not None: resolver.smash(sealException(userError(u"Ejector tried to escape from vat")))
def _slice(self, start, stop): if start < 0: raise userError(u"slice/2: Negative start") if stop < 0: raise userError(u"slice/2: Negative stop") keys = self.objectSet.keys()[start:stop] rv = monteSet() for k in keys: rv[k] = None return rv
def _slice(self, start, stop): if start < 0: raise userError(u"slice/1: Negative start") if stop < 0: raise userError(u"slice/1: Negative stop") items = self.objectMap.items()[start:stop] rv = monteMap() for k, v in items: rv[k] = v return rv
def recv(self, atom, args): from typhon.nodes import Noun, Method, Obj from typhon.objects.equality import optSame, EQUAL from typhon.objects.user import Audition if atom is _UNCALL_0: from typhon.objects.collections.maps import EMPTY_MAP return ConstList([subrangeGuardMaker, StrObject(u"get"), ConstList([self.superGuard]), EMPTY_MAP]) if atom is AUDIT_1: audition = args[0] if not isinstance(audition, Audition): raise userError(u"not invoked with an Audition") ast = audition.ast if not isinstance(ast, Obj): raise userError(u"audition not created with an object expr") methods = ast._script._methods for m in methods: if isinstance(m, Method) and m._verb == u"coerce": mguard = m._g if isinstance(mguard, Noun): rGSG = audition.getGuard(mguard.name) if isinstance(rGSG, FinalSlotGuard): rGSG0 = rGSG.valueGuard if isinstance(rGSG0, SameGuard): resultGuard = rGSG0.value if optSame(resultGuard, self.superGuard) is EQUAL or ( SUPERSETOF_1 in self.superGuard.respondingAtoms() and self.superGuard.call(u"supersetOf", [resultGuard]) is wrapBool(True) ): return wrapBool(True) raise userError( u"%s does not have a result guard implying " u"%s, but %s" % (audition.fqn, self.superGuard.toQuote(), resultGuard.toQuote()) ) raise userError( u"%s does not have a determinable " u"result guard, but <& %s> :%s" % (audition.fqn, mguard.name, rGSG.toQuote()) ) break return self if atom is PASSES_1: return wrapBool(args[0].auditedBy(self)) if atom is COERCE_2: specimen, ej = args[0], args[1] if specimen.auditedBy(self): return specimen c = specimen.call(u"_conformTo", [self]) if c.auditedBy(self): return c throw(ej, StrObject(u"%s does not conform to %s" % (specimen.toQuote(), self.toQuote()))) raise Refused(self, atom, args)
def samenessFringe(original, path, fringe, sofar=None): """ Walk an object graph, building up the fringe. Returns whether the graph is settled. """ # Resolve the object. o = resolution(original) # Handle primitive cases first. if o in (NullObject, TrueObject, FalseObject): return True if (isinstance(o, CharObject) or isinstance(o, DoubleObject) or isinstance(o, IntObject) or isinstance(o, BigInt) or isinstance(o, StrObject) or isinstance(o, BytesObject) or isinstance(o, TraversalKey)): return True if isinstance(o, ConstMap) and o.empty(): return True if sofar is None: sofar = {} elif o in sofar: return True if isinstance(o, ConstList): sofar[o] = None return listFringe(o, fringe, path, sofar) if selfless in o.auditorStamps(): if transparentStamp in o.auditorStamps(): sofar[o] = None return samenessFringe(o.call(u"_uncall", []), path, fringe, sofar) if semitransparentStamp in o.auditorStamps(): sofar[o] = None p = o.call(u"_uncall", []) if not isinstance(p, SealedPortrayal): userError( u'Semitransparent portrayal was not a SealedPortrayal!') return samenessFringe(p, path, fringe, sofar) if isResolved(o): return True # Welp, it's unsettled. if fringe is not None: fringe.append(FringeNode(o, path)) return False
def recv(self, atom, args): if atom is GETCONTENTS_0: return self.open(openGetContentsCB, flags=os.O_RDONLY, mode=0000) if atom is SETCONTENTS_1: data = unwrapBytes(args[0]) sibling = self.temporarySibling(".setContents") p, r = makePromise() vat = currentVat.get() uv_loop = vat.uv_loop fs = ruv.alloc_fs() path = sibling.asBytes() # Use CREAT | EXCL to cause a failure if the temporary file # already exists. flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL sc = SetContents(vat, data, r, sibling, self) ruv.stashFS(fs, (vat, sc)) ruv.fsOpen(uv_loop, fs, path, flags, 0777, openSetContentsCB) return p if atom is OPENFOUNT_0: return self.open(openFountCB, flags=os.O_RDONLY, mode=0000) if atom is OPENDRAIN_0: # Create the file if it doesn't yet exist, and truncate it if it # does. Trust the umask to be reasonable for now. flags = os.O_CREAT | os.O_WRONLY # XXX this behavior should be configurable via namedarg? flags |= os.O_TRUNC return self.open(openDrainCB, flags=flags, mode=0777) if atom is RENAME_1: fr = args[0] if not isinstance(fr, FileResource): raise userError(u"rename/1: Must be file resource") return self.rename(fr.asBytes()) if atom is SIBLING_1: name = unwrapStr(args[0]) if u'/' in name: raise userError(u"sibling/1: Illegal file name '%s'" % name) return self.sibling(name.encode("utf-8")) if atom is TEMPORARYSIBLING_0: return self.temporarySibling(".new") raise Refused(self, atom, args)
def samenessFringe(original, path, fringe, sofar=None): """ Walk an object graph, building up the fringe. Returns whether the graph is settled. """ # Resolve the object. o = resolution(original) # Handle primitive cases first. if o in (NullObject, TrueObject, FalseObject): return True if (isinstance(o, CharObject) or isinstance(o, DoubleObject) or isinstance(o, IntObject) or isinstance(o, BigInt) or isinstance(o, StrObject) or isinstance(o, BytesObject) or isinstance(o, TraversalKey)): return True if isinstance(o, ConstMap) and o.empty(): return True if sofar is None: sofar = {} elif o in sofar: return True if isinstance(o, ConstList): sofar[o] = None return listFringe(o, fringe, path, sofar) if selfless in o.auditorStamps(): if transparentStamp in o.auditorStamps(): sofar[o] = None return samenessFringe(o.call(u"_uncall", []), path, fringe, sofar) if semitransparentStamp in o.auditorStamps(): sofar[o] = None p = o.call(u"_uncall", []) if not isinstance(p, SealedPortrayal): userError(u'Semitransparent portrayal was not a SealedPortrayal!') return samenessFringe(p, path, fringe, sofar) if isResolved(o): return True # Welp, it's unsettled. if fringe is not None: fringe.append(FringeNode(o, path)) return False
def pop(self): from typhon.objects.collections.lists import ConstList if self.objectMap: key, value = self.objectMap.popitem() return ConstList([key, value]) else: raise userError(u"pop/0: Pop from empty map")
def put(self, index, value): top = len(self._l) if 0 <= index < top: self._l[index] = self.coerce(value) else: raise userError(u"put/2: Index %d out of bounds for list of length %d" % (index, top))
def withIndex(self, index, value): # Make a new ConstList. if not (0 < index < len(self._l)): raise userError(u"with/2: Index %d is out of bounds" % index) new = self._l[:] new[index] = value return new
def run(self, executable, argv, env, stdinFount=None, stdoutDrain=None, stderrDrain=None, stdin=False, stdout=False, stderr=False): # Sixth incarnation: Now with streamcaps! # Unwrap argv. l = [] for arg in argv: bs = unwrapBytes(arg) assert bs is not None, "proven impossible" l.append(bs) argv = l # Unwrap and prep environment. d = {} for (k, v) in env.items(): d[unwrapBytes(k)] = unwrapBytes(v) packedEnv = [k + '=' + v for (k, v) in d.items()] env = d vat = currentVat.get() # Set up the list of streams and attach streamcaps. stdinSink = nullSink stdoutSource = stderrSource = emptySource streams = [] if stdin: stream = ruv.rffi.cast(ruv.stream_tp, ruv.alloc_pipe(vat.uv_loop)) streams.append(stream) wrapped = ruv.wrapStream(stream, 1) stdinSink = StreamSink(wrapped, vat) else: streams.append(nullptr(ruv.stream_t)) if stdout: stream = ruv.rffi.cast(ruv.stream_tp, ruv.alloc_pipe(vat.uv_loop)) streams.append(stream) wrapped = ruv.wrapStream(stream, 1) stdoutSource = StreamSource(wrapped, vat) else: streams.append(nullptr(ruv.stream_t)) if stderr: stream = ruv.rffi.cast(ruv.stream_tp, ruv.alloc_pipe(vat.uv_loop)) streams.append(stream) wrapped = ruv.wrapStream(stream, 1) stderrSource = StreamSource(wrapped, vat) else: streams.append(nullptr(ruv.stream_t)) try: process = ruv.allocProcess() sub = SubProcess(vat, process, argv, env, stdin=stdinSink, stdout=stdoutSource, stderr=stderrSource) vat.enqueueEvent(SpawnProcessIOEvent( vat, sub, process, executable, argv, packedEnv, streams)) return sub except ruv.UVError as uve: raise userError(u"makeProcess: Couldn't spawn process: %s" % uve.repr().decode("utf-8"))
def put(self, index, value): top = self.strategy.size(self) if 0 <= index <= top: self.strategy.store(self, index, value) else: raise userError(u"put/2: Index %d out of bounds for list of length %d" % (index, top))
def withIndex(self, index, value): # Make a new ConstList. if index >= self.strategy.size(self) or index < 0: raise userError(u"with/2: Index %d is out of bounds" % index) new = self.strategy.fetch_all(self)[:] new[index] = value return new
def eq(self, other): if not isinstance(other, Proxy): return False if self.checkSlot() or other.checkSlot(): raise userError(u"equals comparison of resolved proxy is" u" impossible") return optSame(self.handler, other.handler) is EQUAL
def fromSecretBytes(self, secretKey): expectedSize = intmask(rsodium.cryptoBoxSecretkeybytes()) if len(secretKey) != expectedSize: message = u"Expected key length of %d bytes, not %d" % ( expectedSize, len(secretKey)) raise userError(message) return SecretKey(secretKey)
def resolveKey(key): from typhon.objects.refs import Promise, isResolved if isinstance(key, Promise): key = key.resolution() if not isResolved(key): raise userError(u"Unresolved promises cannot be used as map keys") return key
def insert(self, index, value): if 0 <= index < len(self._l): self._l.insert(index, self.coerce(value)) elif index == len(self._l): self._l.append(self.coerce(value)) else: raise userError(u"insert/2: Index %d is out of bounds" % index)
def makeProxy(handler, resolutionBox, resolved): if not isSettled(handler): raise userError(u"Proxy handler not settled") if unwrapBool(resolved): return FarRef(handler, resolutionBox) else: return RemotePromise(handler, resolutionBox)
def fromPublicBytes(self, publicKey): expectedSize = intmask(rsodium.cryptoBoxPublickeybytes()) if len(publicKey) != expectedSize: message = u"Expected key length of %d bytes, not %d" % ( expectedSize, len(publicKey)) raise userError(message) return PublicKey(publicKey)
def __init__(self, vat, handler, resolutionBox): if not handler.isSettled(): raise userError(u"Proxy handler not settled: " + handler.toString()) self.vat = vat self.handler = handler self.resolutionBox = resolutionBox
def _resolve(self, target, strict=True): if self._ref is None: if strict: raise userError(u"Already resolved") return False else: if self.guard is not None and not isinstance(target, UnconnectedRef): # Coerce. If there's a problem, then smash the promise. with Ejector() as ej: try: target = self.guard.call(u"coerce", [target, ej]) except Ejecting as e: if e.ejector is not ej: raise target = UnconnectedRef(self.vat, e.value) except UserException as ue: from typhon.objects.exceptions import sealException target = UnconnectedRef(self.vat, sealException(ue)) self._ref.setTarget(_toRef(target, self.vat)) self._ref.commit() self._buf.deliverAll(target) self._ref = None self._buf = None return True
def callAll(self, atom, args, namedArgs): if self.isSwitchable: raise userError(u"not synchronously callable (%s)" % atom.repr.decode("utf-8")) else: self.resolutionRef() return self._target.callAll(atom, args, namedArgs)
def recv(self, atom, args): if atom is ISSETTLED_1: return wrapBool(isSettled(args[0])) if atom is OPTSAME_2: first, second = args result = optSame(first, second) if result is NOTYET: return NullObject return wrapBool(result is EQUAL) if atom is SAMEEVER_2: first, second = args result = optSame(first, second) if result is NOTYET: raise userError(u"Not yet settled!") return wrapBool(result is EQUAL) if atom is SAMEYET_2: first, second = args result = optSame(first, second) return wrapBool(result is EQUAL) if atom is MAKETRAVERSALKEY_1: return TraversalKey(args[0]) raise Refused(self, atom, args)
def __init__(self, handler, resolutionBox): if not isSettled(handler): raise userError(u"Proxy handler not settled: " + handler.toString()) self.handler = handler self.resolutionBox = resolutionBox self.committed = False
def makeProcess(executable, args, environment): """ Create a subordinate process on the current node from the given executable, arguments, and environment. """ # Third incarnation: libuv-powered and requiring bytes. executable = unwrapBytes(executable) # This could be an LC, but doing it this way fixes the RPython annotation # for the list to be non-None. argv = [] for arg in unwrapList(args): s = unwrapBytes(arg) assert s is not None, "proven impossible by hand" argv.append(s) env = {} for (k, v) in unwrapMap(environment).items(): env[unwrapBytes(k)] = unwrapBytes(v) packedEnv = [k + '=' + v for (k, v) in env.items()] vat = currentVat.get() try: process = ruv.allocProcess() sub = SubProcess(vat, process, argv, env) ruv.spawn(vat.uv_loop, process, file=executable, args=argv, env=packedEnv) sub.retrievePID() return sub except ruv.UVError as uve: raise userError(u"makeProcess: Couldn't spawn process: %s" % uve.repr().decode("utf-8"))
def unseal(self, cipher, nonce): try: message = rsodium.boxUnseal(cipher, nonce, self.publicKey, self.secretKey) except rsodium.SodiumError: raise userError(u"unseal/2: Couldn't open this box") return message
def put(self, index, value): top = self.strategy.size(self) if 0 <= index < top: self.strategy.store(self, index, value) else: raise userError( u"put/2: Index %d out of bounds for list of length %d" % (index, top))
def slice(self, start): if start < 0: raise userError(u"slice/1: Negative start") keys = self.objectSet.keys()[start:] rv = monteSet() for k in keys: rv[k] = None return rv
def slice(self, start): if start < 0: raise userError(u"slice/1: Negative start") items = self.objectMap.items()[start:] rv = monteMap() for k, v in items: rv[k] = v return rv
def throw(ej, payload): if ej is None or ej is NullObject: raise UserException(payload) if isinstance(ej, Ejector): ej.fire(payload) else: ej.call(u"run", [payload]) raise userError(u"Ejector did not exit")
def recv(self, atom, args): # _makeIterator/0: Create an iterator for this collection's contents. if atom is _MAKEITERATOR_0: return self._makeIterator() if atom is _PRINTON_1: printer = args[0] self.printOn(printer) return NullObject # contains/1: Determine whether an element is in this collection. if atom is CONTAINS_1: return wrapBool(self.contains(args[0])) # size/0: Get the number of elements in the collection. if atom is SIZE_0: return IntObject(self.size()) # slice/1 and slice/2: Select a subrange of this collection. if atom is SLICE_1: start = unwrapInt(args[0]) try: return self.slice(start) except IndexError: raise userError(u"slice/1: Index out of bounds") # slice/1 and slice/2: Select a subrange of this collection. if atom is SLICE_2: start = unwrapInt(args[0]) stop = unwrapInt(args[1]) try: return self.slice(start, stop) except IndexError: raise userError(u"slice/1: Index out of bounds") # snapshot/0: Create a new constant collection with a copy of the # current collection's contents. if atom is SNAPSHOT_0: return self.snapshot() if atom is JOIN_1: l = unwrapList(args[0]) return self.join(l) return self._recv(atom, args)
def multiply(self, count): # multiply/1: Create a new list by repeating this list's contents. if count < 0: raise userError(u"multiply/1: Can't multiply list %d times" % count) elif count == 0: return [] else: return self.objs * count
def fromPairs(wrappedPairs): from typhon.objects.collections.lists import unwrapList d = monteMap() for obj in unwrapList(wrappedPairs): pair = unwrapList(obj) if len(pair) != 2: raise userError(u"fromPairs/1: Not a pair") d[pair[0]] = pair[1] return ConstMap(d)
def isSameEver(first, second): """ Call this instead of _equalizer.sameEver/2. """ result = optSame(first, second) if result is NOTYET: raise userError(u"Not yet settled!") return result is EQUAL
def eq(self, other): if not isinstance(other, DisconnectedRef): return False result = (isSameEver(self.handler, other.handler) and isSameEver( self.resolutionIdentity, other.resolutionIdentity)) if (result and not isSameEver(self._problem, other._problem)): raise userError(u"Ref invariant violation: disconnected refs with " u" same identity but different problems") return result
def ensureByte(char): """ Like chr(ord(char)), but throws on failure. """ if not u'\x00' <= char <= u'\xff': raise userError(u"Unacceptable bytestring character: " + quoteChar(char)) return chr(ord(char))
def run(self, data): if self.closed: raise userError(u"run/1: Couldn't write to closged file") sb = ruv.scopedBufs([data], self) bufs = sb.allocate() fs = ruv.alloc_fs() ruv.stashFS(fs, (self._vat, sb)) ruv.fsWrite(self._vat.uv_loop, fs, self._fd, bufs, 1, -1, writeFileCB)
def resolveKey(key): from typhon.objects.refs import Promise, isResolved if isinstance(key, Promise): key = key.resolution() # Do the resolution check again. if not isResolved(key): raise userError(u"Unresolved promises cannot be used as map keys") # If the original key wasn't a promise, then no checks are needed. return key
def run(self, data): if self.closed: raise userError(u"run/1: Couldn't send to closed stream") # XXX backpressure? uv_write = ruv.alloc_write() sb = ruv.scopedBufs([data], self) bufs = sb.allocate() ruv.stashWrite(uv_write, (self._vat, sb)) ruv.write(uv_write, self._stream._stream, bufs, 1, writeStreamCB)
def audit(self, audition): from typhon.objects.user import Audition if not isinstance(audition, Audition): raise userError(u"not an Audition") from typhon.metrics import globalRecorder with globalRecorder().context("Audition (DF)"): auditDeepFrozen(audition) audition.ask(deepFrozenStamp) return False
def ask(self, auditor): if not self.active: self.log(u"ask/1: Stolen audition: %s" % pemci, tags=["serious"]) raise userError(u"Audition is out of scope") cached = False self.askedLog.append(auditor) if auditor in self.cache: answer, asked, guards = self.cache[auditor] self.log(u"ask/1: %s: %s (cached)" % (auditor.toString(), boolStr(answer))) for name, value in guards: # We remember what the binding guards for the previous # invocation were. if self.guards[name] != value: # If any of them have changed, we need to re-audit. self.log(u"ask/1: %s: Invalidating" % name) break else: # XXX stopgap: Ignore negative answers in the cache. cached = answer if cached: for a in asked: # We remember the other auditors invoked during this # audition. Let's re-ask them since not all of them may have # cacheable results. self.log(u"ask/1: Reasking %s" % auditor.toString()) answer = self.ask(a) return answer else: # This seems a little convoluted, but the idea is that the logs # are written to during the course of the audition, and then # copied out to the cache afterwards. prevlogs = self.askedLog, self.guardLog self.askedLog = [] self.guardLog = [] try: result = unwrapBool(auditor.call(u"audit", [self])) if self.guardLog is None: self.log(u"ask/1: %s: %s (uncacheable)" % (auditor.toString(), boolStr(result))) else: self.log(u"ask/1: %s: %s" % (auditor.toString(), boolStr(result))) self.cache[auditor] = (result, self.askedLog[:], self.guardLog[:]) return result finally: self.askedLog, self.guardLog = prevlogs self.log(u"ask/1: %s: failure" % auditor.toString()) return False