Dirty
Dirty
import loc.strongBox as sb
import sw.ns.bootstrap.application as app
import threading
import time
import sys
import random
import os
import re
import collections
unstableEvent = threading.Event()
_nothing_ = object()
class _Track(dict):
"Utility class, should not be instantiated manually. " \
"A _Track instance is return by Spooler.subscribe(). " \
"Behaves like a {element:value} dictionary. " \
"The view is notified whenever elements in self are updated or destroyed, " \
"as well as children cretead in those. "
def __init__(self,view):
dict.__init__(self)
self.view = view
self._thread = None
self._living = True
self._soft = False
self._suspended = False
def __setitem__(self,element,val):
if element not in self:
assert self._living
element._trackCount += 1
track,dirty,root,renames,changed_sb_items =
app.theSpooler._subscriptions[self.view]
assert track is self
assert isinstance(element,Element)
assert element.descendsOf(root)
if self.view.receiveNotification :
for sub in element :
dirty.setdefault(sub,app.theSpooler.CREATED)
dirty[element]=app.theSpooler.UPDATED
dict.__setitem__(self,element,val)
def update(self,dic):
print("update")
assert self._living
for key,val in dic.iteritems():
self[key] = val # So that it calls __setitem__
def __delitem__(self,element):
x = dict.pop(self,element,_nothing_)
if x is _nothing_ : raise KeyError
elif self._living : element._trackCount -= 1
def pop(self,element,default=_nothing_):
x = dict.pop(self,element,_nothing_)
if x is _nothing_ :
if default is _nothing_ : raise KeyError
else : return default
else :
if self._living : element._trackCount -= 1
return x
def _kill(self):
self._living = False
for element in self :
element._trackCount -= 1
def running(self): return self._thread is not None
def setSoft(self,soft):
self._soft = soft
def setSuspended(self,suspended):
self._suspended = suspended
if not suspended and self._thread is None :
app.theSpooler.startStabilizingThread(self.view)
class View(object):
receiveNotification = True
skip_network_requests = False
class Spooler(object):
DELETED = 0
UPDATED = 1
CREATED = 2
def __init__(self):
self._lock = threading.RLock()
self._subscriptions = {}
self._all_update_subscriptions = set()
self._gatherer = None
self._viewLevel = 0
self._revision = 0
self._need_to_notify_reload = False
app.controller.subscribe(self,self.closeTransaction,self.openTransaction)
def subscribe(self,view,root=None):
"Registers a new subscription. " \
"A view can only register once. " \
"Returns an empty Track instance. "
with self._lock :
assert isinstance(view,View) , "only Views can subscribe to the
Spooler."
assert view not in self._subscriptions , "View %s has already
subscribed."
if root is None : root = self.root()
track = _Track(view)
track._viewLevel = self._viewLevel
# Track, dirty, root, renames, sbItems changed
self._subscriptions[view] = ( track , {} , root, {}, set())
if getattr(view, 'ALL_UPDATES', False):
self._all_update_subscriptions.add(view)
def unsubscribe(self,view):
"Unregisters a subscription. "
with self._lock :
if view in self._all_update_subscriptions:
self._all_update_subscriptions.remove(view)
track,dirty,root,renames,changed_sb_items =
self._subscriptions.pop(view)
track._kill()
thread = track._thread
if thread is not None : thread._poison = True
track._thread = None
def __enter__(self):
self._viewLevel += 1
def __exit__(self,excType,excValue,excTb):
if theLicenser.haveLicense("LaunchDevelopper") and excValue is not
None:
raise excValue
self._viewLevel -=1
self.kick()
def announceCreation(self,element):
if element._parent is None : return
if element._parent._trackCount :
with self._lock :
hit = False
for view,(track,dirty,root,renames,changed_sb_items) in
self._subscriptions.iteritems() :
if (element._parent in track and element not in
track) or view in self._all_update_subscriptions:
hit = True
dirty[element] = self.CREATED
assert hit or LogBook.warning('%s pretends to be tracked
but is not [C] !' % element._parent) or True
else:
with self._lock :
for view in self._all_update_subscriptions:
track, dirty, root, renames, changed_sb_items =
self._subscriptions[view]
dirty[element] = self.CREATED
def announceDeletion(self,element):
assert not element._stable
# Cancels a furtive creation
if element._parent is not None and element._parent._trackCount :
with self._lock :
hit = False
for view,(track,dirty,root,renames,changed_sb_items) in
self._subscriptions.iteritems() :
if element._parent in track or view in
self._all_update_subscriptions:
hit = True
if dirty.get(element,None) is self.CREATED :
del dirty[element]
assert hit or LogBook.warning('%s pretends to be tracked
but is not [D1] !' % element._parent) or True
if element._trackCount :
with self._lock :
hit = False
for view,(track,dirty,root,renames,changed_sb_items) in
self._subscriptions.iteritems() :
if element in track or view in
self._all_update_subscriptions:
hit = True
dirty[element] = self.DELETED
assert hit or LogBook.warning('%s pretends to be tracked
but is not [D2] !' % element) or True
else:
with self._lock :
for view in self._all_update_subscriptions:
track, dirty, root, renames, changed_sb_items =
self._subscriptions[view]
dirty[element] = self.CREATED
def announceUpdate(self,element):
if element._trackCount :
with self._lock :
hit = False
for view,(track,dirty,root,renames,changed_sb_items) in
self._subscriptions.iteritems() :
if element in track or view in
self._all_update_subscriptions:
hit = True
if element not in dirty:
dirty[element] = self.UPDATED
assert hit or LogBook.warning('%s pretends to be tracked
but is not [U] !' % element) or True
else:
with self._lock :
for view in self._all_update_subscriptions:
track, dirty, root, renames, changed_sb_items =
self._subscriptions[view]
dirty[element] = self.CREATED
def announceTreeUpdate(self,element):
"Announces an update on all elements rooted at 'element'."
self.announceUpdate(element)
for subElement in element :
self.announceTreeUpdate(subElement)
for dependency in element._fwdDependencies :
self.announceUpdate(dependency)
def registerGatherer(self,gatherer):
self._gatherer = gatherer
def unregisterGatherer(self,gatherer):
assert self._gatherer==gatherer,"Unregistering wrong gatherer"
self._gatherer = None
def root(self):
return app.root.getElement()
def exitCleanUp(self):
threads = [ thread for thread in threading.enumerate() if
isinstance(thread, StabilizingThread) ]
for thread in threads : thread._poison = True
for thread in threads : thread.join()
def clearProject(self):
with self._lock :
for view, (track, dirty, root, renames, changed_sb_items) in
self._subscriptions.iteritems():
if hasattr(view, 'handleClearProject'):
view.handleClearProject()
def reloadProject(self):
with self._lock:
self._need_to_notify_reload = True
def openTransaction(self):
"Called back before any change to the strong box." \
"Poisons any running stablizing thread ."
threads = set()
with self._lock :
self._revision += 1
for view,(track,dirty,root,renames,changed_sb_items) in
self._subscriptions.iteritems() :
thread = track._thread
if thread is not None :
thread._poison = True
threads.add(thread)
for thread in threads : thread.join()
## Unstabilize all element for which the new sbItems will be associated
# This requires multiple iteration as a full hierarchy can be created
at once
# but can only be handled one level at a time.
itemToElt = { i : i.getElement() for i in sbItems }
todoStack = [ i for i in sbItems if itemToElt[i] is Ellipsis ]
prevSize = 0
while 1:
if len(todoStack) == prevSize:
break
stack = todoStack
todoStack = []
prevSize = len(stack)
for sbItem in stack:
pElement =
itemToElt.get(sbItem.getParent(),sbItem.getParent().getElement())
if pElement is not Ellipsis :
if not sbItem.out() :
e = pElement.itemCreated(sbItem)
if e is not None: itemToElt[sbItem] = e
for dep in set(pElement._fwdChildDependencies) :
dep.unstabilize()
pElement._fwdChildDependencies.clear()
pElement.unstabilize()
else:
todoStack.append(sbItem)
self.kick(skip_stabilize, network_request)
def threadCount(self):
i = 0
for track,view,root,renames,changed_sb_items in
self._subscriptions.itervalues():
if track._thread is not None : i += 1
return i
def trigger(self,view):
"Called back on the death of a stabilizing thread." \
"Arms the gatherer, to call self.flush(), in the main thread."
if view not in self._subscriptions : return
if self._gatherer is not None :
self._gatherer.arm(view)
while dirty:
old_dirty = set(dirty.items())
deletions = set()
updates = set()
creations = set()
d = (deletions, updates, creations)
# print("dirty", dirty)
# print("dirty.type", type(dirty))
# import ipdb; ipdb.set_trace()
else:
dirty = False
if set(dirty.items()) == old_dirty:
break
def flush(self,view):
"Called in the main thread by the gatherer at the end of the stabiling
thread." \
"Summarizes the changes and calls the view. "
with self._lock :
if self._need_to_notify_reload:
self._need_to_notify_reload = False
for view_ in self._subscriptions.keys():
if hasattr(view_, 'handleReloadProject'):
view_.handleReloadProject()
thread = None
with self._lock :
self._revision += 1
if root.isDead() :
try:
view.notifyDeletedRoot()
except:
sys.excepthook(*sys.exc_info())
self.notifyView(view)
try:
view.notifyStableRoot()
except:
sys.excepthook(*sys.exc_info())
track._thread = None
# Start the thread if we created one. This must be done outside of the
lock!
if thread is not None:
thread.start()
def pollSoft(self):
with self._lock :
for view , (track,dirty,root,renames,changed_sb_items) in
self._subscriptions.items() :
if track._soft and not root.isDead() : # and track._thread
if dirty :
# import ipdb; ipdb.set_trace()
self.notifyView(view)
class StabilizingThread(threading.Thread):
def __init__(self,view,element, skip_stabilize=False, network_request=False):
self.view = view
self.skip_stabilize = skip_stabilize
self.element = element
self.stack = []
self.stack_set = set()
self._poison = False
self.network_request = network_request
self.__logStack__ = [ LogBook.rootLogger() ]
self.start_time = time.time()
super(StabilizingThread,self).__init__()
self.setName("%s for %s" % (element,view))
def start(self):
super(StabilizingThread,self).start()
unstableEvent.set()
if __options__.noThread : self.join()
def run(self):
os.nice(10)
if __options__.profile :
import cProfile
cProfile.runctx('self.run0()',globals(),locals(),"%s_%x.pyProf"%
(__options__.profile,threading.currentThread().ident))
else :
self.run0()
def run0(self):
# XXX ! We can only start a thread from an ObjectElement !
# Otherwise it may die _while_ we are stabilizing it !
try :
if not self.skip_stabilize:
self.element.parentObject().stabilize()
except PoisonException:
pass
finally :
# When quitting while thread are active strange things, add
protections
try:
app.theSpooler.trigger(self.view)
except:
pass
def sortedElementList(l):
""" Sort the element by pathname."""
convert = lambda text: int(text) if text.isdigit() else text
alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)',
key.pathname(key.projectAncestor())) ]
return sorted(l, key = alphanum_key)
@lru_cache
def lru_methods(class_):
return [
obj for (name, obj) in inspect.getmembers(class_)
if inspect.ismethod(obj) and hasattr(obj, 'cache_clear')
]
cnt = 0
class Element(object):
_uLock = threading.RLock()
displayName = 'UNSET DISPLAY NAME'
_dead = False
_parent = None
_item = None
_selfWeak = False
isObject = False
isProjectAncestor = False
isParameter = False
isIssue = False
isCompiled = False
canBeDisabled = False
createDisabled = False
twtTopic = None
publishableAttributes = ()
_frozen = None
def __init__(self,parent,key):
# TODO: remove sb.Item when C conversion progressed
assert isinstance(key,(str,sb.Item,Element)) , "Key be %s (%s)" %
(key,type(key))
if type(key) is str :
sKey = True
rKey = key
else:
if isinstance(key,Element):
key = key._item
sKey = False
rKey = key.objectOrigin()
self.toxic = False
self._parent = parent
self._children = collections.OrderedDict()
self._lock = threading.Lock()
self._status = False
self._trackCount = 0
self._key = key
self._fullSortKey = None
# TODO When we have a C++ implementation of weakref, use. This will
avoid a memory leak
self._fwdDependencies = set()
self._fwdChildDependencies = set()
self._fwdDieDependencies = set()
self._stable = False
self._path = None
self._cachedAbstractionIn = {}
if parent is not None :
assert rKey not in parent._children, "Key %s already present in
%s" % (rKey,dict([(k,str(v)) for k,v in parent._children.iteritems()]))
parent._children[rKey] = self
parent.unstabilize()
if not sKey:
key.addElement(self)
app.theSpooler.announceCreation(self)
def __copy__(self):
return self
def _resetPathCache(self):
for function in self._lru_methods:
function.cache_clear()
def recursiveChildren(self):
"""
:returns: All of the recursive children of this element.
:rtype: Set[Element]
"""
children = set()
for child in self:
children.add(child)
children |= child.recursiveChildren()
return children
def destroy(self):
"Interal the wheel of fortune, do not call."
if self._dead : return
with self._uLock :
if self._dead : return
self._dead = True
self.unstabilize()
for child in self._children.values():
if not child._dead: child.destroy()
app.theSpooler.announceDeletion(self)
key = self._key
if type(key) is str :
self._parent._children.pop(key,None) #Sometimes seems to be
already removed Bug#18429
else :
key.removeElement(self)
if key.getElement() is self :
key.setObjectElement(Ellipsis)
assert self._parent is not None
self._parent._children.pop(key.objectOrigin(),None)
#Sometimes seems to be already removed Bug#18429
self.resetAttrs()
# del self._parent
@lru_cache(maxsize=None)
def path(self):
if self._path is None:
path = []
scan = self
while scan : path.append(scan) ; scan = scan._parent
path.reverse()
self._path = path
return self._path
@lru_cache(maxsize=None)
def __str__(self):
return '<%s:%s>' % (type(self).__name__,self.pathname())
def hasKey(self,key):
if type(key) is str : return key in self._children
if isinstance(key,Element):
key = key._item
return key.objectOrigin() in self._children
def __iter__(self):
"Iterates over self's children. "
#used in code: for key in self
return self._children.itervalues()
def iterObjects(self):
for x in self :
if x.isObject : yield x
def childCount(self):
"Returns the number of self's children."
return len(self._children)
def __enter__(self):
thread = threading.currentThread()
thread.stack.append(self)
thread.stack_set.add(self)
def __exit__(self,excType,excValue,excTraceback):
try:
thread = threading.currentThread()
elt = thread.stack.pop()
thread.stack_set.remove(elt)
assert elt is self
if excValue :
if type(excValue) in (PoisonException,SystemExit) :
self._status = None ; return False
self._status = False
sys.excepthook(excType,excValue,excTraceback)
self._stable = True
finally:
if self._lock.locked():
self._lock.release()
app.theSpooler.announceUpdate(self)
return True
def orderedChildren(self):
"Iterates over self's children, using a deterministic order. "
return sorted(self,key=sortKey)
def _resetFullSortKey(self):
if self._fullSortKey is not None:
self._fullSortKey = None
for c in self:
c._resetFullSortKey()
def fullSortKey(self):
if self._fullSortKey is None:
if self._parent:
self._fullSortKey = self._parent.fullSortKey() +
(self.sortKey(),)
else:
self._fullSortKey = (self.sortKey(),)
return self._fullSortKey
@lru_cache(maxsize=None)
def sortKey(self):
return str(self._key)
@lru_cache(maxsize=None)
def descendsOf(self,element):
"Returns True is element is an ancestor of self."
scan = self
while scan :
if scan is element : return True
scan = scan._parent
return False
def parent(self):
"returns self's parent."
return self._parent
def origin(self):
"returns self's origin. [ Only for ObjectElements. ]"
return None
def key (self):
"Returns self's key in parent. " \
"Result is either a string or an Item instance. "
return self._key
@lru_cache(maxsize=None)
def name(self):
"Returns a reasonable name for self in self's parent. " \
"Result is a string, but cannot be used as a key. "
return str(self._key)
@lru_cache(maxsize=None)
def pathname(self,in_ = None,watch=None):
"Returns self's pathname. Result is a string. "
parentObject = self.parentObject()
parentObjectItem = parentObject._item
@classmethod
def fromPath(cls,pathname):
"Retrieves an element, out of its pathname. "
path = pathname.split('..')
rootItem = app.root.getitem_str(path[0][1:])
scan = rootItem.getElement()
for z in path[1:] :
if z[0] == '(':
ref = rootItem.getReferenceByName(z)
if ref not in scan and not scan.stable():
try:
scan.stabilize()
except StabilizeException:
pass
scan = scan[ref]
else:
if z not in scan and not scan.stable():
try:
scan.stabilize()
except StabilizeException:
pass
scan = scan[z]
return scan
@lru_cache(maxsize=None)
def exportName(self,in_=None):
if isinstance(in_,type): in_ = self.ancestor(in_)
pathname = self.pathname(in_)
if pathname.startswith('/'): pathname = pathname[1:]
# @.. is for clockGaters
return
pathname.replace('..@..','_').replace('@..','').replace('/','_').replace('..','_').
replace('.','_')
@lru_cache(maxsize=None)
def nameListIn(self,ancestor):
current = self
nameList = []
while current is not ancestor and current is not None:
nameList.insert(0,current.name())
current = current.parent()
assert current is ancestor, "Ancestor is not a parent of self"
return nameList
def abstractionIn(self,scope):
if scope.isProjectAncestor:
if scope not in self._cachedAbstractionIn or
self._cachedAbstractionIn[scope].isDead():
selfPath = []
scan = self
while scan is not None and not scan.isProjectAncestor:
selfPath.insert(0,scan._key)
scan = scan._parent
if scan is None: return None
if not scan._item.shareOriginLineWith(scope._item): return
None
scan = scope
for key in selfPath:
try:
scan = scan._children[key] if type(key) is str
else scan._children[key.objectOrigin()]
except KeyError:
return None
self._cachedAbstractionIn[scope] = scan
assert not self._cachedAbstractionIn[scope].isDead()
return self._cachedAbstractionIn[scope]
else:
selfPath = []
scopePath = []
scan = self
while scan is not None : selfPath.append(scan._key) ;
scan=scan._parent
scan = scope
while scan is not None : scopePath.append(scan._key) ;
scan=scan._parent
if len(scopePath)>len(selfPath) :
return None
while scopePath :
selfKey = selfPath.pop()
scopeKey = scopePath.pop()
if selfKey == scopeKey : continue
if (
not isinstance(selfKey,sb.CoreItem)
or not isinstance(scopeKey,sb.CoreItem)
or not selfKey.shareOriginLineWith(scopeKey)
):
return None
scan = scope
try :
while selfPath :
scan = scan[selfPath.pop()]
except KeyError :
return None
return scan
def asObjects(self):
return set()
def asParameters(self):
return set()
def stable(self):
return self._stable
def status(self):
if not self._stable : return None
return self._status
def watch(self,for_):
self._fwdDependencies.add(for_)
return True
def watchChildren(self,for_):
self._fwdChildDependencies.add(for_)
return True
def watchDie(self,for_):
self._fwdDieDependencies.add(for_)
return True
def walkObjects(self,cls,sort=True):
stack = collections.deque([self])
while stack :
x = stack.popleft()
if isinstance(x,cls):
yield x
if sort:
stack.extend(sorted([child for child in x if
isinstance(child,cls)],reverse=True,key=lambda c:c.sortKey()))
else:
stack.extend(child for child in x if isinstance(child,cls))
def walkKinds(self,kinds,sort):
assert sort in (True,False)
assert type(kinds) is not str
stack = collections.deque([self])
kinds = {k for k in kinds}
while stack :
current = stack.popleft()
if current.isObject and current.kind in kinds:
yield current
if sort:
stack.extend(sorted((child for child in current if
child.isObject and child.kind in kinds),key=lambda c:c.sortKey()))
else:
stack.extend(child for child in current if child.isObject
and child.kind in kinds)
def walkKind(self,kind,sort):
assert sort in (True,False)
stack = collections.deque([self])
while stack :
current = stack.popleft()
if current.isObject and (kind is None or current.kind == kind):
yield current
if sort:
stack.extend(sorted((child for child in current if
child.isObject and (kind is None or child.kind == kind)),key=lambda c:c.sortKey()))
else:
stack.extend(child for child in current if child.isObject
and (kind is None or child.kind == kind))
def recursiveChildren(self):
"""
:returns: All of the recursive children of this element.
:rtype: Set[Element]
"""
children = set()
for child in self:
children.add(child)
children |= child.recursiveChildren()
return children
def stabilize(self,weak=False,block_on_acquire_lock=True):
thread = threading.currentThread()
if thread.stack: self._fwdDependencies.add(thread.stack[-1])
if self._stable and self._status is not None and not self.toxic:
return self._status
assert self not in thread.stack_set, "Dependency loop : %s"%
([x.pathname() for x in thread.stack+[self]],)
if not self._lock.acquire(block_on_acquire_lock):
raise StabilizeException
if self._dead:
self._lock.release()
return False
else:
self._selfWeak = weak
if self.isParameter:
self.toxic = False
try:
self._status = self.stabilizeJob()
return self._status
def unstabilize(self):
with self._uLock :
if self._stable :
app.theSpooler.announceUpdate(self)
#global cnt
#cnt += 1
self._stable = False
self._status = None
self._resetFullSortKey()
for element in list(self._fwdDependencies) :
element.unstabilize()
self.unstabilizeJob()
self._fwdDependencies.clear()
@lru_cache(maxsize=None)
def parentObject(self):
scan = self
while scan and not scan.isObject : scan=scan._parent
return scan
@lru_cache(maxsize=None)
def projectAncestor(self):
scan = self
while scan and not scan.isProjectAncestor : scan=scan._parent
return scan
@lru_cache(maxsize=None)
def ancestor(self,cls):
"""
Return the first parent of self that is of type 'cls'.
Can return None if there is no such parent
"""
scan = self
while scan is not None and not isinstance(scan,cls): scan =
scan._parent
return scan
@lru_cache(maxsize=None)
def ancestorOfKind(self,kind):
"""
Return the first parent of self that is of type 'cls'.
Can return None if there is no such parent
"""
scan = self
while scan is not None and not (scan.isObject and scan.kind == kind):
scan = scan._parent
return scan
def htmlRef(self,for_=None):
return '<a href="%s"> %s </a>' % (self.url(),self.reprName(for_ if for_
is not self else for_.projectAncestor()))
def url(self):
return "ans:%s" % self.pathname()
def htmlDescription(self):
return (
"<b>%s</b>"% self.reprName()
)
class CompiledElement(Element):
isCompiled = True
def __init__(self,parent,key):
self._dynamically_compiled_children = []
super(CompiledElement, self).__init__(parent, key)
def itemDestroyed(self):
self.destroy()
def reprName(self,in_=None):
if self._key is '@' : return 'Synthesis of %s' %
self.parent().reprName(in_)
p = self.parentObject()
if (in_ is None or in_.isObject) and '@' in p:
return 'Synthesized %s of %s' % ( self.pathname(p['@']) ,
p.reprName(in_) )
else :
return self.pathname(in_)
def niceName( self , in_ = None ) :
if in_ is None : return self.pathname()
elif in_ is not self : return self.pathname(in_)
else : return "[%s]" % self.displayName
return self.pathName
def stabilizeJob(self):
self.buildCompiledChildren()
try :
return all([child.stabilize() for child in self])
except None:
raise
def unstabilizeJob(self):
for child in self._dynamically_compiled_children:
child.destroy()
self._dynamically_compiled_children = []
super(CompiledElement, self).unstabilizeJob()
def _buildCompiledChildren(self):
return []
def buildCompiledChildren(self):
if self._dynamically_compiled_children:
return
self._dynamically_compiled_children = self._buildCompiledChildren()
class CompiledWithOriginElement(CompiledElement):
_origin = None
_conversions = {}
def stabilizeJob(self):
self._origin = origin = self.parent().origin()[self._key]
origin.stabilize()
for child in list(self) : child.destroy() # Is this massive destruction
too violent ?
for child in origin :
newType = self._conversions.get(type(child),None)
if newType is not None :
if isinstance(child._key, sb.Item):
key =
child._key.getElement().abstractionIn(self.projectAncestor())._item
else:
key = child._key
newType(self,key)
return super(CompiledWithOriginElement,self).stabilizeJob()
def origin(self):
return self._origin
class SortedCompiledElement(CompiledElement):
def __init__(self,parent,key):
super(SortedCompiledElement,self).__init__(parent,key)
self.order = self.buildChildren()
def stabilizeJob(self):
try :
return all([self[key].stabilize() for key in self.order])
except None:
raise
class ObjectElement(Element):
isObject = True
# Maintained as a (parentElementClass,kind) -> elementClass
# XXX As it is, only leaf classes can be used
_objectKinds = {}
_shadowKinds = {}
_hasLicenseRight = False
kind = "????"
allowedParents = tuple()
allowedOrigins = tuple()
defaultNewName = "Object"
displayName = "Object"
icon = "eltUnknown"
shadow = False
creationByUserIsAllowed = True
deletionByUserIsAllowed = True
self._frozen = item._frozen
@lru_cache(maxsize=None)
def name(self):
return str(self._key.getName())
def sortKey(self):
return str(self._key.getName())
def origin(self):
return None if self._itemOrigin is None else
self._itemOrigin.getElement()
def disabled(self):
return self.canBeDisabled and self._item.disabled()
def delete(self):
"""
Deletes self's strong box item, leading to self's destruction.
"""
# We need to make sure that switch pipes are preserved when their
neighbors are deleted.
if (
not isinstance(self, ForbiddenObjectElement) and
self.projectAncestor().kind == 'switchBasedArchitecture'
and
hasattr(self, 'network') and
'info' in self['@'][self.network]
):
arch = self.projectAncestor()._item
# Get a mapping of all items after the one we are deleting in the
routes to
# the items that come before the one we are deleting.
# We need to use the items because this can happen between
stabilizations
# and the items are always up to date.
after_befores = defaultdict(set)
route = list(sorted(target[direction],
key=lambda x: int(x.name())))
for index, entry in enumerate(route):
if entry.value() is self._item:
if index == 0:
if direction ==
'requestPath':
before = init._key
else:
before =
target._key
else:
before = route[index -
1].value()
if index + 1 == len(route):
if direction ==
'requestPath':
after =
target._key
else:
after = init._key
else:
after = route[index +
1].value()
after_befores[after].add(before)
route_element =
target[direction].getElement()
if route_element and route_element is not
Ellipsis:
route_element.unstabilize()
# Iterate through all of the neighbors and make sure that their
new pipes will be the same
# as before.
for after, befores in after_befores.items():
# If the item is unrecoverably unstable, we skip trying to
update it.
if '$' not in after:
continue
if self.network in before['$']:
if 'outputPipes' in before['$'][self.network]
and self._item in before['$'][self.network]['outputPipes']:
if after not in before['$'][self.network]
['outputPipes']:
new_before_entry =
sb.EntryItem(before['$'][self.network]['outputPipes'], after)
else:
new_before_entry = before['$']
[self.network]['outputPipes'][after]
if self._item in before['$']
[self.network]['outputPipes']:
for i in before['$'][self.network]
['outputPipes'][self._item] or []:
i._duplicateEntries(new_before_entry, {})
if 'domainCrossings' in before['$']
[self.network] and self._item in before['$'][self.network]['domainCrossings']:
if after not in before['$'][self.network]
['domainCrossings']:
new_before_entry =
sb.EntryItem(before['$'][self.network]['domainCrossings'], after)
else:
new_before_entry = before['$']
[self.network]['domainCrossings'][after]
if self._item in before['$']
[self.network]['domainCrossings']:
for i in before['$'][self.network]
['domainCrossings'][self._item] or []:
i._duplicateEntries(new_before_entry, {})
if self.network in after['$']:
if 'inputPipes' in after['$'][self.network] and
self._item in after['$'][self.network]['inputPipes']:
if before not in after['$'][self.network]
['inputPipes']:
new_after_entry =
sb.EntryItem(after['$'][self.network]['inputPipes'], before)
else:
new_after_entry = after['$']
[self.network]['inputPipes'][before]
if self._item in after['$'][self.network]
['inputPipes']:
for i in after['$'][self.network]
['inputPipes'][self._item] or []:
i._duplicateEntries(new_after_entry, {})
if 'domainCrossings' in after['$']
[self.network] and self._item in after['$'][self.network]['domainCrossings']:
if before not in after['$'][self.network]
['domainCrossings']:
new_after_entry =
sb.EntryItem(after['$'][self.network]['domainCrossings'], before)
else:
new_after_entry = after['$']
[self.network]['domainCrossings'][before]
if self._item in after['$'][self.network]
['domainCrossings']:
for i in after['$'][self.network]
['domainCrossings'][self._item] or []:
i._duplicateEntries(new_after_entry, {})
app.controller.destroy(self._item)
def duplicate(self):
item = self._item
return app.controller.duplicate(
item, item.getParent().proposeChildName(item.name())
)
def duplicateWithOccurrences(
self, folderName, commonFolderNumber, newName=None, newOrigin=None
):
item = self._item
folderList = self.name().split(".")
simpleName = folderList[-1]
createFolder = folderList[commonFolderNumber:-1]
baseName = folderName
project = app.theSpooler.root()
fromPath = project.fromPath
for name in createFolder:
if baseName == "":
baseName = name
else:
baseName = ".".join((baseName, name))
try:
fromPath("/%s" % baseName)
except:
sb.ObjectItem(project._item, baseName, "folder", None)
if baseName == "":
if newName is None:
name = simpleName
else:
name = newName
else:
if newName is None:
name = ".".join((baseName, simpleName))
else:
name = ".".join((baseName, newName))
name = item.getParent().proposeChildName(name)
LogBook.info("From %s create %s." % (self.name(), name))
newOrigin = app.controller.duplicate(item, name, newOrigin=newOrigin)
for occurrence in list(item.getOccurrences()):
if occurrence.kind() not in (
"architectureResult",
"scenarioResult",
"structureArea",
"structureFloorplan",
):
occurrenceFolderList = occurrence.name().split(".")[:-1]
if occurrenceFolderList ==
occurrence.origin().name().split(".") or (
newOrigin.origin() is not None
and newOrigin.origin().name().split(".")
== newOrigin.name().split(".")[:-1]
):
if folderName == "":
newFolderName = name.split(".")[-1]
else:
newFolderName = ".".join((folderName,
name.split(".")[-1]))
if len(createFolder) > 0:
newFolderName = ".".join(createFolder +
[newFolderName])
newCommonFolderNumber = commonFolderNumber + 1 +
len(createFolder)
else:
newFolderName = folderName
newCommonFolderNumber = commonFolderNumber
occurrence.getElement().duplicateWithOccurrences(
newFolderName, newCommonFolderNumber,
newOrigin=newOrigin
)
def signature(self):
return self._item.signature()
def itemDestroyed(self):
self.destroy()
@classmethod
def createElementFromItem(cls, parent, item):
# type: (ObjectElement, sw.ns.kernel.strongBox.ObjectItem) ->
ObjectElement
""" Get XML item from a pdd and return the matching Object Element
Args:
parent: The element to attach the newly created element to
item: The xml node representing the element we want to
create
"""
if item.getClass() == "ObjectItem":
types = (type(parent), item.kind())
else:
origin = item.origin()
if origin is None:
hasBadOrigins = cls.allowedOrigins
else:
allowedOrigins = (allowed.kind for allowed in
cls.allowedOrigins)
hasBadOrigins = origin.kind() not in allowedOrigins
if hasBadOrigins:
cls = BadOriginObjectElement
def stabilizeJob(self):
origin = self.origin()
a = origin.stabilize() if origin is not None else True
b = all([child.stabilize() for child in self])
if (
self.parent() is not None
and self.parent().kind != "project"
and "." in self.name()
):
DotInNameIssue(self)
return False
return a and b
def unstabilizeJob(self):
newName = self._item.name()
if self._itemName != newName:
app.theSpooler.announceTreeUpdate(self)
self._itemName = newName
super(ObjectElement, self).unstabilizeJob()
def asObjects(self):
return {self}
def asParameters(self):
if theLicenser.haveLicense("WaiverFullObjectHandling"):
return {self["$"]}
else:
return set()
@lru_cache(maxsize=None)
def pathname(self, in_=None, watch=None):
if in_ is not None:
assert in_.isObject
assert self.descendsOf(in_)
in_ = in_._key
if watch:
parent = self
while parent.parent() is not None or (
in_ and parent is not None and parent._item is not in_
):
parent.watch(watch)
parent = parent.parent()
return self._item.path(in_)
@classmethod
def _buildFolderNames(cls, parentItem, name):
"""
return newName,folderNames
"""
folderNames = set()
if parentItem.getElement() is app.theSpooler.root():
splitNames = name.split(".")
folderName = None
i = 0
while i < len(splitNames) - 1:
splitName = splitNames[i]
newFolderName = (
"%s.%s" % (folderName, splitName)
if folderName is not None
else splitName
)
try:
folder = parentItem[newFolderName]
if folder.kind() != "folder":
splitNames[i] =
parentItem.proposeChildName(newFolderName)
continue
except KeyError:
folderNames.add(newFolderName)
folderName = newFolderName
i += 1
newName = ".".join(splitNames)
else:
newName = name
return newName, folderNames
@classmethod
def createIn(cls, parent, name=None, copies=1, overWrite=False):
if name is None:
name = cls.defaultNewName
if isinstance(parent, ObjectElement):
assert isinstance(parent, cls.allowedParents)
parentItem = parent._item
else:
parentItem = parent
newName, folderNames = cls._buildFolderNames(parentItem, name)
if copies > 1:
copieMsg = "%i copies of " % (copies)
else:
copieMsg = ""
with app.controller.transaction(
"Create %s%s %s in %s" % (copieMsg, cls.displayName, name,
parentItem)
):
for folderName in folderNames:
sb.ObjectItem(parentItem, folderName, "folder", None)
item = sb.ObjectItem(
parentItem, newName, cls.kind, None, overWrite=overWrite
)
if copies != 1:
items = [item]
for i in xrange(copies - 1):
items.append(
app.controller.duplicate(
item, item.getParent().proposeChildName(name)
)
)
if copies != 1:
return items
else:
return item
@classmethod
def createFromOrigin(cls, origin, name=None):
if name is None:
name = cls.defaultNewName
if isinstance(origin, ObjectElement):
assert isinstance(origin, cls.allowedOrigins)
originItem = origin._item
else:
originItem = origin
parentItem = originItem.getParent()
if originItem.name() == ".".join(name.split(".")[:-1]):
folderNames = []
newName = name
else:
newName, folderNames = cls._buildFolderNames(parentItem, name)
with app.controller.transaction(
"Create %s of %s '%s' in %s"
% (cls.displayName, originItem.path(), name, parentItem)
):
for folderName in folderNames:
sb.ObjectItem(parentItem, folderName, "folder", None)
return sb.ObjectItem(parentItem, newName, cls.kind, originItem)
@classmethod
def getCreators(cls):
withOrigin = bool(cls.allowedOrigins)
return (
(
cls.displayName,
withOrigin,
cls.createFromOrigin if withOrigin else cls.createIn,
cls.allowedOrigins if withOrigin else cls.allowedParents,
),
)
def propertiesElement(self):
return parameterElement.StructPElt
def compiledElement(self):
return CompiledElement
def issueElement(self):
return issueElement.IssueRecord
# Is this really the good place ... probably not.
def _stringifyThis(x,scope=None):
if isinstance(x,str) and x.startswith('@html@'): return x[6:]
if isinstance(x,Element ): return str(x.niceName(scope))
if isinstance(x,(list,tuple)): return ', '.join([_stringifyThis(y,scope) for
y in x])
return str(x)
def stringifyInfos(d,scope):
return { k:_stringifyThis(v,scope) for k,v in d.iteritems() }
def _htmlifyThis(x,scope=None):
if isinstance(x,basestring ):
if x.startswith('@html@'): return x[6:]
else: return
x.replace('<','<').replace('>','>')
if isinstance(x,Element ): return str(x.htmlRef(scope))
if isinstance(x,(list,tuple)): return ', '.join([_htmlifyThis(y,scope) for y
in x])
return str(x)
def htmlifyInfos(d,scope):
return { k:_htmlifyThis(v,scope) for k,v in d.iteritems() }
# A bit of comment :
# The Context class represents the 'active content' to be passed from an engine to
another,
# It consists in 3 topics :
# - the selection :
# a set of Elements, representing the selection
# - the root :
# a single Element, root of any element in the selection,
# representing the context where the selection was produced.
# - the possible creations :
# a set of Element subClasses, representing the objects meaningfull to create
'now'.
#
# A new context object is produced at each 'Grab' action
# context.abstractionIn( element ) returns a new Context object, caching the result
#
def or_(l):
s = set()
for i in l : s|=i
return frozenset(s)
class Context(object):
isContext = True # For bootstrap to detect
def __init__(self, selection , root = None , creations = None ) :
self._revision = -1
self._cachedStable = False
selection = {s for s in selection}
selection.discard(None)
self._selection = selection
assert not any( element for element in self._selection if element is
Ellipsis ) , 'Ellipsis element in context'
if root is None : root = app.root.getElement()
self._root = root
if creations is not None : creations = tuple(creations)
self._creations = creations
self.isStable()
def abstractionIn(self,scope):
r = self._abstractionCache.get(scope,None)
if r is None :
self._abstractionCache[scope] = r = Context(
{ element.abstractionIn(scope) for element in
self._selection } - {None}
, self._root.abstractionIn(scope)
, self._creations
)
return r
def filterBy(self,func):
r = self._preprocessCache.get(func,None)
if r is None :
self._preprocessCache[func] = r = func(self)
return r
def needsReassess(self):
return self._revision != app.theSpooler._revision
@staticmethod
def objectsFilter(context):
return or_( element.asObjects() for element in context )
@staticmethod
def objectFilter(context):
objs = context.filterBy(context.objectsFilter)
return iter(objs).next() if len(objs) == 1 else None
@staticmethod
def parameterFilter(context):
# Remove issues to be allowed to provide an 'asParameter' more all
issueElement to be seen as parameters in customizer
return or_( element.asParameters() for element in context if not
isinstance(element,issueElement.IssueElement) )
import loc.parameterElement
import loc.issueElement
class UnknownObjectIssue(issueElement.IssueElement):
_messageTemplate = TWT("PDD object $object belongs to an unknown class,
$kind.")
_descriptionTemplate = TWT(
"This class of PDD object cannot be identified by the program, "
"and will be ignored."
)
_code = "UOBJ"
_reason = "Unknown Object"
_severity = issueElement.IssueElement.Severity.WARNING
class ForbiddenObjectIssue(issueElement.IssueElement):
_messageTemplate = TWT("Object $object ($kind) requires a license. ")
_descriptionTemplate = TWT(
"Object $object ($kind) requires a specific license feature '$feature'
that is currently lacking or out-of-date in your license configuration. The object
will be ignored."
)
_code = "RLIC"
_reason = "License Required"
_severity = issueElement.IssueElement.Severity.WARNING
class BadOriginIssue(issueElement.IssueElement):
_messageTemplate = TWT("Object $object ($kind) violates PDD internal
formatting rules. ")
_descriptionTemplate = TWT(
"Object $object ($kind) is incorrectly formatted within the PDD, thus
disrupting the object hierarchy of the XML-based file."
)
_code = "PFRV"
_reason = "PDD Formatting Rules Violation"
class DotInNameIssue(issueElement.IssueElement):
_messageTemplate = TWT("$element contains a '.' in its name. ")
_descriptionTemplate = TWT("$element contains a '.' in its name. ")
_code = "NAMI"
_reason = "Naming Rules Violation"
class BrokenObjectElement(ObjectElement):
displayName = 'Broken'
defaultNewName = 'Broken'
_allowedParents = (ObjectElement,)
icon = 'eltBroken'
def __init__(self,parent,key):
super(BrokenObjectElement,self).__init__(parent,key)
self.issue=None
def htmlDescription(self):
if self.issue is not None:
return self.issue.htmlDescription()
return super(BrokenObjectElement,self).htmlDescription()
def stabilizeJob(self):
return False
class UnknownObjectElement(BrokenObjectElement):
displayName = 'Unknown'
defaultNewName = 'Unknown'
def __init__(self,parent,key):
super(UnknownObjectElement,self).__init__(parent,key)
self.shadow = key.objectOrigin() is not key
if not self.shadow and not isinstance(parent,BrokenObjectElement) :
self.issue=UnknownObjectIssue(self,kind=key.kind())
class ForbiddenObjectElement(BrokenObjectElement):
displayName = 'Forbidden'
defaultNewName = 'Forbidden'
def __init__(self,parent,key):
super(ForbiddenObjectElement,self).__init__(parent,key)
self.shadow = key.objectOrigin() is not key
if not self.shadow and not isinstance(parent,BrokenObjectElement) :
kind = key.kind()
licName = ""
if theLicenser.haveLicense("LaunchDevelopper") :
licName =
theLicenser.getFeatureName('Object'+kind[0].upper()+kind[1:])
self.issue=ForbiddenObjectIssue(self,kind=kind,feature=licName)
class BadOriginObjectElement(BrokenObjectElement):
displayName = 'BadOrigin'
defaultNewName = 'BadOrigin'
def __init__(self,parent,key):
super(BadOriginObjectElement,self).__init__(parent,key)
self.shadow = key.objectOrigin() is not key
if not isinstance(parent,BrokenObjectElement):
kind = key.kind()
self.issue=BadOriginIssue(self,kind=kind)
threading.currentThread().stack = []
threading.currentThread().stack_set = set()
threading.currentThread()._poison = False