| Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf-8 -*-
2 """Contains nodes supporting facading within a dependency graph - this can be used
3 for container tyoes or nodes containing their own subgraph even
4 """
5 __docformat__ = "restructuredtext"
6
7 from networkx import DiGraph, NetworkXError
8 from collections import deque
9 import inspect
10 import weakref
11 from util import iDuplicatable
12
13 from dge import NodeBase
14 from dge import _PlugShell
15 from dge import iPlug
16 from dge import Attribute
17
18 __all__ = ("FacadeNodeBase", "GraphNodeBase", "OIFacadePlug")
24 """Metaclass building the method wrappers for the _FacadeShell class - not
25 all methods should be overridden, just the ones important to use"""
26
27 @classmethod
29 def unfacadeMethod( self, *args, **kwargs ):
30 return getattr( self._toIShell(), funcname )( *args, **kwargs )
31 return unfacadeMethod
32
33 @classmethod
35 """in our case, connections just are handled by our own OI plug, staying
36 in the main graph"""
37 return list()
38
39 @classmethod
41 method = None
42 if facadetype == "unfacade":
43 method = cls.createUnfacadeMethod( funcname )
44 else:
45 method = cls.createFacadeMethod( funcname )
46
47 if method: # could be none if we do not overwrite the method
48 method.__name__ = funcname
49
50 return method
51
52
54 unfacadelist = clsdict.get( '__unfacade__' )
55 facadelist = clsdict.get( '__facade__' )
56
57 # create the wrapper functions for the methods that should wire to the
58 # original shell, thus we unfacade them
59 for funcnamelist, functype in ( ( unfacadelist, "unfacade" ), ( facadelist, "facade" ) ):
60 for funcname in funcnamelist:
61 method = metacls.createMethod( funcname, functype )
62 if method:
63 clsdict[ funcname ] = method
64 # END for each funcname in funcnamelist
65 # END for each type of functions
66
67 return type.__new__( metacls, name, bases, clsdict )
68
71 """Metaclass wrapping all unfacade attributes on the plugshell trying
72 to get an input connection """
73
74 @classmethod
76 """:return: wrapper method for funcname """
77 method = None
78 if funcname == "get": # drection to input
79 def unfacadeMethod( self, *args, **kwargs ):
80 """apply to the input shell"""
81 # behave like the base implementation and check the internal shell
82 # for caches first - if it exists, we use it.
83 # It would have been cleared if it is affecfted by another plug being set,
84 # thus its either still cached or somenone set the cache.
85 # if there is no cache, just trace the connections upwards.
86 # This means for get we specifiaclly override the normal "original last"
87 # behaviour to allow greater flexibility
88 oshell = self._getOriginalShell( )
89 if oshell.hasCache():
90 return oshell.cache()
91
92 return getattr( self._getShells( "input" )[0], funcname )( *args, **kwargs )
93 method = unfacadeMethod
94 else: # direction to output
95 def unfacadeMethod( self, *args, **kwargs ):
96 """Clear caches of all output plugs as well"""
97 for shell in self._getShells( "output" ):
98 getattr( shell, funcname )( *args, **kwargs )
99 # END unfacade method
100 method = unfacadeMethod
101 # END funk type handling
102 return method
103
104 @classmethod
106 """Call the main shell's function"""
107 def facadeMethod( self, *args, **kwargs ):
108 return getattr( self._getOriginalShell( ), funcname )( *args, **kwargs )
109 return facadeMethod
110
113 """All connections from and to the FacadeNode must actually start and end there.
114 Iteration over internal plugShells is not allowed.
115 Thus we override only the methods that matter and assure that the call is handed
116 to the acutal internal plugshell.
117 We know everything we require as we have been fed with an oiplug
118
119 * node = facacde node
120 * plug = oiplug containing inode and iplug ( internal node and internal plug )
121 * The internal node allows us to hand in calls to the native internal shell
122 """
123 # list all methods that should not be a facade to our facade node
124 __unfacade__ = [ 'set', 'get', 'clearCache', 'hasCache','setCache', 'cache' ]
125
126 # keep this list uptodate - otherwise a default shell will be used for the missing
127 # function
128 # TODO: parse the plugshell class itself to get the functions automatically
129 __facade__ = [ 'connect','disconnect','input', 'outputs','connections',
130 'iterShells' ]
131
132 __metaclass__ = _OIShellMeta
133
135 """Sanity checking"""
136 if not isinstance( args[1], OIFacadePlug ):
137 raise AssertionError( "Invalid PlugType: Need %r, got %r (%s)" % ( OIFacadePlug, args[1].__class__ , args[1]) )
138
139 # NOTE deprecated in python 2.6 and without effect in our case
140 super( _OIShell, self ).__init__( *args )
141
142
144 """Cut away our name in the possible oiplug ( printing an unnecessary long name then )"""
145 plugname = str( self.plug )
146 nodename = str( self.node )
147 plugname = plugname.replace( nodename+'.', "" )
148 return "%s.%s" % ( nodename, plugname )
149
154
157 """This callable class, when called, will create a IOShell using the
158 actual facade node, not the one given as input. This allows it to have the
159 facade system handle the plugshell, or simply satisfy the original request"""
160
161 __unfacade__ = [ 'get', 'clearCache' ]
162
163 # keep this list uptodate - otherwise a default shell will be used for the missing
164 # function
165 # TODO: parse the plugshell class itself to get the functions automatically
166 __facade__ = [ 'set','hasCache','setCache', 'cache',
167 'connect','disconnect','input','connections','outputs',
168 'iterShells' ]
169
170 __metaclass__ = _IOShellMeta
171
173 """Initialize this instance - we can be in creator mode or in shell mode.
174 ShellMode: we behave like a shell but apply customizations, true if 3 args ( node, plug, origshellcls )
175 CreatorMode: we only create shells of our type in ShellMode, true if 2 args
176
177 :param args:
178 * origshellcls[0] = the shell class used on the manipulated node before we , must always be set as last arg
179 * facadenode[1] = the facadenode we are connected to
180
181 :todo: optimize by creating the unfacade methods exactly as we need them and bind the respective instance
182 methods - currently this is solved with a simple if conditiion.
183 """
184 # find whether we are in shell mode or in class mode - depending on the
185 # types of the args
186 # CLASS MODE
187 if hasattr( args[0], '__call__' ) or isinstance( args[0], type ):
188 self.origshellcls = args[0]
189 self.facadenode = args[1]
190 self.iomap = dict() # plugname -> oiplug
191 super( _IOShell, self ).__init__( ) # initialize empty
192 # END class mode
193 #else:
194 # NOTE: This is deprecated in python 2.6 and doesnt do anything in our case
195 # we do not do anything special in shell mode ( at least value-wise
196 # super( _IOShell, self ).__init__( *args ) # init base
197 # END INSTANCE ( SHELL ) MODE
198
200 """This equals a constructor call to the shell class on the wrapped node.
201 Simply return an ordinary shell at its base, but we catch some callbacks
202 This applies to everything but connection handling
203
204 :note: the shells we create are default ones with some extra handlers
205 for exceptions"""
206 return self.__class__( *args )
207
208 #{ Helpers
209
211 """:return: oiplug suitable for this shell or None"""
212 try:
213 # cannot use weak references, don't want to use strong references
214 return self.node.shellcls.iomap[ self.plug.name() ]
215 except KeyError:
216 # plug not on facadenode - this is fine as we get always called
217 pass
218 #except AttributeError:
219 # TODO: take that back in once we use weak references or proper ids again ... lets see
220 # # facade node does not know an io plug - assure we do not try again
221 # del( self.node.shellcls[ self.plug.name() ] )
222
223 return None
224
226 """:return: instance of the original shell class that was replaced by our instance"""
227 return self.node.shellcls.origshellcls( self.node, self.plug )
228
230 """Recursive method to find the first facade parent having an OI shell
231
232 :return: topmost facade node shell or None if we are not a managed plug"""
233
234 # otherwise we have found the topmost parent
235 return facadeNodeShell
236
237
239 """:return: list of ( outside ) shells, depending on the shelltype and availability.
240 If no outside shell is avaiable, return the actual shell only
241 As facade nodes can be nested, we have to check each level of nesting
242 for connections into the outside world - if available, we use these, otherwise
243 we stay 'inside'
244
245 :param shelltype: "input" - outside input shell
246 "output" - output shells, and the default shell"""
247 if not isinstance( self.node.shellcls, _IOShell ):
248 raise AssertionError( "Shellclass of %s must be _IOShell, but is %s" % ( self.node, type( self.node.shellcls ) ) )
249
250 # GET FACADE SHELL
251 ####################
252 # get the oiplug on our node
253 oiplug = self._getoiplug( )
254 if not oiplug:
255 # plug not on facadenode, just ignore and return the original shell
256 return [ self._getOriginalShell( ) ]
257 # END if there is no cached oiplug
258
259
260 # Use the facade node shell type - we need to try to get connections now,
261 # either inputs or outputs on our facade node. In case it is facaded
262 # as well, we just use a default shell that will definetly handle connections
263 # the way we expect it
264 facadeNodeShell = self.node.shellcls.facadenode.toShell( oiplug )
265
266
267 # NESTED FACADE NODES SPECIAL CASE !
268 ######################################
269 # If a facade node is nested inside of another facade node, it will put
270 # it's IO shell above our OI shell.
271 # IOShells do not return connections - get a normal shell then
272 connectionShell = facadeNodeShell
273 if facadeNodeShell.__class__ is _IOShell:
274 connectionShell = _PlugShell( facadeNodeShell.node, facadeNodeShell.plug )
275 # END nested facade node special handling
276
277
278 outShells = list()
279 if shelltype == "input":
280
281 # HIGHER LEVEL INPUT SHELLS
282 ############################
283 # if we are nested, use an imput connection of our parent as they
284 # override lower level connections
285 if not connectionShell is facadeNodeShell:
286 aboveLevelInputShells = facadeNodeShell._getShells( shelltype )
287
288 # this is either the real input shell, or the original shell of the toplevel
289 # By convention, we return the facadeshell that is connected to the input
290 # in rval[1]
291 # The method that calls us only uses array index [0], which is the shell it needs !
292 # We just use the length as internal flag !
293 if len( aboveLevelInputShells ) == 2: # top level orverride !
294 return aboveLevelInputShells
295
296 # END aquire TL Input
297
298 # still here means no toplevel override
299 # TRY OUR LEVEL INPUT
300 inputShell = connectionShell.input( )
301
302 if inputShell:
303 # FLAGGED RETURN VALUE : this indicates to our callers that
304 # we have found a good input on our level and want to use it.
305 # if the caller is the metaclass wrapper, it will only use the outshell[0]
306 # anyways and not bother
307 outShells.append( inputShell )
308 outShells.append( self )
309 else:
310 outShells.append( self._getOriginalShell( ) )
311
312 # END outside INPUT shell handling
313 else:
314 outShells.extend( connectionShell.outputs( ) )
315
316 # ADD 'INSIDE' ORIGINAL SHELL
317 # always allow our 'inside' level to get informed as well
318 outShells.append( self._getOriginalShell( ) )
319
320 # NESTED SHELL SPECIAL CASE
321 ##############################
322 # query the IO Parent Shell for the shells on its level and add them
323 if not connectionShell is facadeNodeShell:
324 outShells.extend( facadeNodeShell._getShells( shelltype ) )
325 # END outside OUTPUT shell handling
326
327 return outShells
328
329 # } END helpers
330
331
332 # END shells
333
334
335 #{ Nodes
336
337 -class FacadeNodeBase( NodeBase ):
338 """Node having no own plugs, but retrieves them by querying other other nodes
339 and claiming its his own ones.
340
341 Using a non-default shell it is possibly to guide all calls through to the
342 virtual PlugShell.
343
344 Derived classes must override _plugshells which will be queried when
345 plugs or plugshells are requested. This node will cache the result and do
346 everything required to integrate itself.
347
348 It lies in the nature of this class that the plugs are dependent on a specific instance
349 of this node, thus classmethods of NodeBase have been overridden with instance versions
350 of it.
351
352 The facade node keeps a plug map allowing it to map plug-shells it got from
353 you back to the original shell respectively. If the map has been missed,
354 your node will be asked for information.
355
356 :note: facades are intrusive for the nodes they are facading - thus the nodes
357 returned by `_getNodePlugs` will be altered. Namely the instance will get a
358 shellcls and plug override to allow us to hook into the callchain. Thus you should have
359 your own instance of the node - otherwise things might behave differently for
360 others using your nodes from another angle
361
362 :note: this class could also be used for facades Container nodes that provide
363 an interface to their internal nodes"""
364 shellcls = _OIShell # overriden from NodeBase
365
366 #{ Configuration
367 caching_enabled = True # if true, the facade may cache plugs once queried
368 #} END configuration
369
371 """ Initialize the instance"""
372 self._cachedOIPlugs = list() # simple list of names
373 NodeBase.__init__( self, *args, **kwargs )
374
375
377 """:return: shell on attr made from our plugs - we do not have real ones, so we
378 need to call plugs and find it by name
379
380 :note: to make this work, you should always name the plug names equal to their
381 class attribute"""
382 check_ambigious = not attr.startswith( OIFacadePlug._fp_prefix ) # non long names are not garantueed to be unique
383
384 candidates = list()
385 for plug in self.plugs( ):
386 if plug.name() == attr or plug.iplug.name() == attr:
387 shell = self.toShell( plug )
388 if not check_ambigious:
389 return shell
390 candidates.append( shell )
391 # END if plugname matches
392 # END for each of our plugs
393
394 if not candidates:
395 raise AttributeError( "Attribute %s does not exist on %s" % (attr,self) )
396
397 if len( candidates ) == 1:
398 return candidates[0]
399
400 # must be more ...
401 raise AttributeError( "More than one plug with the local name %s exist on %s - use the long name, i.e. %snode_attr" % ( attr, self, OIFacadePlug._fp_prefix ) )
402
403
404
406 """Actually, it does nothing because our plugs are linked to the internal
407 nodes in a quite complex way. The good thing is that this is just a cache that
408 will be updated once someone queries connections again.
409 Basically it comes down to the graph duplicating itself using node and plug
410 methods instead of just doing his 'internal' magic"""
411 pass
412
413
414 #{ To be Subclass-Implemented
415
417 """Implement this as if it was your plugs method - it will be called by the
418 base - your result needs processing before it can be returned
419
420 :return: list( tuple( node, plug ) )
421 if you have an existing node that the plug or shell you gave is from,
422 return it in the tuple, otherwise set it to a node with a shell that allows you
423 to handle it - the only time the node is required is when it is used in and with
424 the shells of the node's own shell class.
425
426 The node will be altered slightly to allow input of your facade to be reached
427 from the inside
428
429 :note: a predicate is not supported as it must be applied on the converted
430 plugs, not on the ones you hand out"""
431 raise NotImplementedError( "Needs to be implemented in SubClass" )
432
433 #} END to be subclass implemented
434
435
437 """Calls `_getNodePlugs` method to ask you to actuallly return your
438 actual nodes and plugs or shells.
439 We prepare the returned value to assure we are being called in certain occasion,
440 which actually glues outside and inside worlds together """
441 # check args - currently only predicate is supported
442 predicate = kwargs.pop( 'predicate', lambda x: True )
443
444 if kwargs: # still args that we do not know ?
445 raise AssertionError( "Unhandled arguments found - update this method: %s" % kwargs.keys() )
446
447
448 # HAND OUT CACHE
449 #################
450 if self._cachedOIPlugs:
451 outresult = list()
452 for oiplug in self._cachedOIPlugs:
453 if predicate( oiplug ):
454 outresult.append( oiplug )
455 # END for each cached plug
456 return outresult
457 # END for each cached plug
458
459
460 # GATHER PLUGS FROM SUBCLASS
461 ##############################
462 yourResult = self._getNodePlugs( )
463
464
465 def toFacadePlug( node, plug ):
466 if isinstance( plug, OIFacadePlug )\
467 and self is plug.inode.shellcls.facadenode: # we can wrap other facade nodes as well
468 return plug
469 return OIFacadePlug( node, plug )
470 # END to facade plug helper
471
472 # PROCESS RETURNED PLUGS
473 finalres = list()
474 for orignode, plug in yourResult:
475 oiplug = toFacadePlug( orignode, plug )
476
477
478 # Cache all plugs, ignoring the predicate
479 if self.caching_enabled:
480 self._cachedOIPlugs.append( oiplug )
481 # END cache update
482
483
484 # MODIFY NODE INSTANCE
485 ##################################################
486 # Allowing us to get callbacks once the node is used inside of the internal
487 # structures
488
489 # ADD FACADE SHELL CLASS
490 ############################
491 # This can also handle facaded facade nodes, as they have the type
492 # of _IOShell as shellcls, but no instance
493 if not isinstance( orignode.shellcls, _IOShell ):
494 classShellCls = orignode.shellcls
495 orignode.shellcls = _IOShell( classShellCls, self )
496 # END for each shell to reconnect
497 # END if we have to swap in our facadeIOShell
498
499
500 # update facade shell class ( inst ) cache so that it can map our internal
501 # plug to the io plug on the outside node
502 # cannot create weakref to tuple type unfortunately - use name instead
503 orignode.shellcls.iomap[ oiplug.iplug.name() ] = oiplug
504
505
506 # UPDATE CONNECTIONS ( per plug, not per node )
507 ##########################
508 # update all connections with the new shells - they are required when
509 # walking the affects tree, as existing ones will be taken instead of
510 # our new shell then.
511 internalshell = orignode.toShell( oiplug.iplug )
512 all_shell_cons = internalshell.connections( 1, 1 ) # now we get old shells
513
514 # disconnect and reconnect with new
515 for edge in all_shell_cons:
516 nedge = list( ( None, None ) )
517 created_shell = False
518
519 for i,shell in enumerate( edge ):
520 nedge[ i ] = shell
521 # its enough to just have an io shell here, it just assures
522 # our callbacks
523 # edges are always ordered start->end - we could be any of these
524 # thus we have to check before
525 if shell == internalshell and not isinstance( shell, _IOShell ) :
526 nedge[ i ] = shell.node.toShell( shell.plug )
527 created_shell = True
528 # END for each shell in edge
529
530 if created_shell:
531 edge[0].disconnect( edge[1] )
532 nedge[0].connect( nedge[1] )
533 # END new shell needs connection
534 # END for each edge to update
535
536
537 # ONLY AFTER EVERYTHING HAS BEEN UPDATED, WE MAY DROP IT
538 ##########################################################
539 if not predicate( oiplug ):
540 continue
541
542 finalres.append( oiplug )
543
544 # END for each orignode,plug in result
545
546
547 # the final result has everything nicely put back together, but
548 # it has been altered as well
549 return finalres
550
552 """if a cache has been build as caching is enabled, this method clears
553 the cache forcing it to be updated on the next demand
554
555 :note: this could be more efficient by just deleting plugs that are
556 not required anymore, but probably this method can expect the whole
557 cache to be deleted right away ... so its fine"""
558 self._cachedOIPlugs = list()
559
562 """A node wrapping a graph, allowing it to be nested within the node
563 All inputs and outputs on this node are purely virtual, thus they internally connect
564 to the wrapped graph.
565
566 :todo: tests deletion of graphnodes and see whether they are being garbage collected.
567 It should work with the new collector as it can handle cyclic references - these
568 strong cycles we have a lot in this structure. Weakrefs will not work for nested
569 facade nodes as they are tuples not allowing weak refs.
570 """
571 #{ Configuration
572 duplicate_wrapped_graph = True # an independent copy of the wrapped graph usually is required - duplication assures that ( or the caller )
573 allow_auto_plugs = True # if True, plugs can be found automatically by iterating nodes on the graph and using their plugs
574 ignore_failed_includes = False # if True, node will not raise if a plug to be included cannot be found
575
576 # list of node.plug strings ( like "node.inName" ) and/or node names ( like "node" )
577 # defining the plugs you would like to specifically include on the facade
578 # If just a name is given, the node name is assumed and all plugs on that node will be included
579 include = list()
580
581 # same as include, but matching nodes/plugs will be excluded
582 exclude = list()
583 #}END configuration
584
586 """ Initialize the instance
587 :param wrappedGraph: graph we are wrapping"""
588 self.wgraph = wrappedGraph
589 if self.duplicate_wrapped_graph:
590 self.wgraph = self.wgraph.duplicate( )
591
592 FacadeNodeBase.__init__( self, *args, **kwargs )
593
595 """Create a copy of self and return it"""
596 return self.__class__( self.wgraph ) # graph will be duplicated in the constructor
597
598
599 #{ Base Methods
600
602 """:return: generator for nodes in our graph
603 :note: derived classes could override this to just return a filtered view on
604 their nodes"""
605 return self.wgraph.iterNodes( )
606
607 #} END base
608
609
611 """Add the plugs defined in include to the given output list"""
612 missingplugs = list()
613 nodes = self.wgraph.nodes()
614 nodenames = [ str( node ) for node in nodes ]
615
616 for nodeplugname in self.include:
617 nodename = plugname = None
618
619 # INCLUDE WHOLE NODE HANDLING
620 ##############################
621 if nodeplugname.find( '.' ) == -1 :
622 nodename = nodeplugname
623 else:
624 nodename, plugname = tuple( nodeplugname.split( "." ) )
625 # END wholenode check
626
627 # FIND NODE INSTANCE
628 ######################
629 try:
630 index = nodenames.index( nodename )
631 node = nodes[ index ]
632 except ValueError:
633 missingplugs.append( nodeplugname )
634 continue
635
636
637 # ADD INCLUDE PLUGS
638 ###################
639 if not plugname:
640 outset.update( ( (node,plug) for plug in node.plugs() ) )
641 else:
642 # find matching plugs
643 try:
644 plug = getattr( node, plugname ).plug
645 except AttributeError:
646 missingplugs.append( nodeplugname )
647 else:
648 # finally append the located plug
649 outset.add( ( node , plug ) )
650 continue
651 # END whole node handling
652 # END for each nodeplug name
653
654 if not self.ignore_failed_includes and missingplugs:
655 msg = "%s: Could not find following include plugs: %s" % ( self, ",".join( missingplugs ) )
656 raise AssertionError( msg )
657
659 """remove the plugs from our exclude list and modify the outset"""
660 if not self.exclude:
661 return
662
663 excludepairs = set()
664 excludeNameTuples = [ tuple( plugname.split( "." ) ) for plugname in self.exclude ]
665 for node,plug in outset:
666 for nodeplugname in self.exclude:
667
668 nodename = plugname = None
669 if nodeplugname.find( '.' ) == -1: # node mode
670 nodename = nodeplugname
671 else:
672 nodename,plugname = nodeplugname.split( '.' ) # node plug mode
673
674 if nodename == str( node ) and ( not plugname or plugname == plug.name() ):
675 excludepairs.add( ( node,plug ) )
676 # END for each nodename.plugname to exclude
677 # END for each node,plug pair
678
679 # substract our pairs accordingly to modify the set
680 outset -= excludepairs
681
683 """:return: all plugs on nodes we wrap ( as node,plug tuple )"""
684 outset = set()
685
686 # get the included plugs
687 self._addIncludeNodePlugs( outset )
688
689 if self.allow_auto_plugs:
690 for node in self._iterNodes():
691 plugresult = node.plugs( )
692 outset.update( set( ( (node,plug) for plug in plugresult ) ) )
693 # END update lut map
694 # END for node in nodes
695 # END allow auto plugs
696
697 # remove excluded plugs
698 self._removeExcludedPlugs( outset )
699
700 # the rest of the nitty gritty details, the base class will deal
701 return outset
702
708 """Facade Plugs are meant to be stored on instance level overriding the respective
709 class level plug descriptor.
710 If used directly, it will facade the internal affects relationships and just return
711 what really is affected on the facade node
712
713 Additionally they are associated to a node instance, and can thus be used to
714 find the original node once the plug is used in an OI facacde shell
715
716 Its a tuple as it will be more memory efficient that way. Additionally one
717 automatically has a proper hash and comparison if the same objects come together
718 """
719 _fp_prefix = "_FP_"
720
721 #{ Object Overridden Methods
722
724 """Store only weakrefs, throw if we do not get 3 inputs
725
726 :param args:
727 * arg[0] = internal node
728 * arg[1] = internal plug"""
729 count = 2
730 if len( args ) != count:
731 raise AssertionError( "Invalid Argument count, should be %i, was %i" % ( count, len( args ) ) )
732
733 #return tuple.__new__( cls, ( weakref.ref( arg ) for arg in args ) )
734 return tuple.__new__( cls, args ) # NOTE: have to use string refs for recursive facade plugs
735
736
738 """ Allow easy attribute access
739 inode: the internal node
740 iplug: the internal plug
741
742 Thus we must:
743 - Act as IOFacade returning additional information
744
745 - Act as original plug for attribute access
746
747 This will work as long as the method names are unique
748 """
749 if attr == 'inode':
750 return self[0]
751 if attr == 'iplug':
752 return self[1]
753
754 # still here ? try to return a value on the original plug
755 return getattr( self.iplug, attr )
756
757 #} END object overridden methods
758
759
761 """ Get name of facade plug
762
763 :return: name of (internal) plug - must be a unique key, unique enough
764 to allow connections to several nodes of the same type"""
765 return "%s%s_%s" % ( self._fp_prefix, self.inode, self.iplug )
766
767
769 """ Get affected shells into the given direction
770
771 :return: list of all oiplugs looking in direction, if
772 plugtestfunc says: False, do not prune the given shell"""
773 these = lambda shell: shell.plug is self.iplug or not isinstance( shell, _IOShell ) or shell._getoiplug() is None
774
775 iterShells = self.inode.toShell( self.iplug ).iterShells( direction=direction, prune = these, visit_once=True )
776 outlist = [ shell._getoiplug() for shell in iterShells ]
777
778 return outlist
779
781 """Affects relationships will be set on the original plug only"""
782 return self.iplug.affects( otherplug )
783
785 """Walk the internal affects using an internal plugshell
786
787 :note: only output plugs can be affected - this is a rule followed throughout the system
788 :return: tuple containing affected plugs ( plugs that are affected by our value )"""
789 return self._affectedList( "down" )
790
792 """Walk the graph upwards and return all input plugs that are being facaded
793 :return: tuple containing plugs that affect us ( plugs affecting our value )"""
794 return self._affectedList( "up" )
795
797 """:return: True if this is an output plug that can trigger computations """
798 return self.iplug.providesOutput( )
799
801 """:return: True if this is an input plug that will never cause computations"""
802 return self.iplug.providesInput( )
803
804
805 #} END plugs
806
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Apr 19 18:00:23 2011 | http://epydoc.sourceforge.net |