This function is very similar to Section 28.4, “EqBind.lookup(): What is the referenced
taxon?”, except that it keeps track of which direct equivalent
codes have been seen so far, so as to avoid an infinite loop.
# - - - E q B i n d . _ _ c h a i n C l o s u r e
def __chainClosure ( self, seenAbbrs, sym ):
'''Find a taxon at sym or what it refers to
[ (seenAbbrs is a set of bird codes) and
(sym is an AbSym instance) ->
if sym is a direct equivalent that is in seenAbbrs
or leads indirectly to a code in seenAbbrs ->
raise ValueError
else if sym is a direct equivalent that is not in
seenAbbrs and leads indirectly to a taxon ->
return that taxon
else -> raise ValueError ]
NB: In the last case, the chain leads to a CollBind
or unbound symbol.
'''
First we need to eliminate the case where sym
does not even have a binding.
#-- 1 --
# [ if sym is unbound ->
# raise ValueError
# else ->
# symBind = sym.binding
symBind = sym.binding
if symBind is None:
raise ValueError ( "Code '%s' is referenced to code "
"'%s', which is undefined." %
(self.abbr, sym.abbr) )
If sym is another EqBind
instance, set symAbbr to that instance's code.
If symBind is not an EqBind
instance, we can call its .lookup() method
to see if the trail leads eventually to a taxon; if not,
that method returns None.
#-- 2 --
# [ if symBind is an EqBind ->
# symAbbr := symBind.abbr
# else if symBind defines a taxon ->
# return that taxon as a Taxon instance
# else -> raise ValueError ]
if isinstance(symBind, EqBind):
symAbbr = symBind.abbr
else:
taxon = symBind.lookup()
if taxon is None:
raise ValueError ( "Code '%s' is not associated "
"with a taxon." % self.abbr )
else:
return taxon
At this point we know that sym is a direct
equivalent. Check for an infinite loop.
#-- 3 --
# [ if symAbbr is an element of seenAbbrs ->
# raise ValueError
# else ->
# newSet := union ( seenAbbrs, set([symAbbr]) ) ]
if symAbbr in seenAbbrs:
raise ValueError ( "Code '%s' leads to a circuit of "
"references." % self.abbr )
else:
newSet = seenAbbrs.union ( set ( [symAbbr] ) )
Add the new bird code from sym to the set of
codes we have seen, then call ourself recursively to chase the
reference chain the rest of the way.
#-- 4 --
# [ symBind is an EqBind ->
# if symAbbr is directly equivalent to a code that is in
# union(seenAbbrs, set([symAbbr]) ->
# raise ValueError
# else if sym is a direct equivalent that is not in
# seenAbbrs and leads indirectly to a taxon ->
# return that taxon
# else -> raise ValueError ]
return self.__chainClosure ( newSet, symBind.prefSym )