def expandAtClient(self, constraints):
        """
        The expansion itself. See class comments for details.

        :param constraints: array of global constraining (filter) methods
        """
        self.checkForEagerTermination()

        # if there are no more statements, that means that the constraints
        # have been fully expanded
        if self.statement :
            # decompose the statement into subject, predicate and object
            # default setting for the search statement
            # see if subject (resp. predicate and object) is already bound. This
            # is done by taking over the content of self.dict if not None and replacing
            # the subject with that binding
            # the (search_subject,search_predicate,search_object) is then created
            (s,p,o,func) = self.statement
            # put the bindings we have so far into the statement; this may add None values,
            # but that is exactly what RDFLib uses in its own search methods!
            (search_s,search_p,search_o) = (self._bind(s),self._bind(p),self._bind(o))
            # We need to keep track of the original Graph associated with the tripleStore
            # so we can switch back to it after changing the active graph (if we do)
            # otherwise we will effect later evaluations which use the same tripleStore instance
            originalGraph = None

            if self.tripleStore.graphVariable:
                if hasattr(self.tripleStore.graph, 'quads'):
                    if self.tripleStore.graphVariable not in self.bindings:
                        searchRT = self.tripleStore.graph.quads(
                                (search_s, search_p,search_o)
                                )
                    else:
                        graphName = self.bindings[self.tripleStore.graphVariable]
                        assert not self.tripleStore.DAWG_DATASET_COMPLIANCE \
                           or isinstance(graphName,URIRef), \
                           "Cannot formally return graph name solutions for the default graph!"

                        unifiedGraph = Graph(self.tripleStore.graph.store, identifier=graphName)

                        originalGraph = self.tripleStore.graph
                        self.tripleStore.graph = unifiedGraph

                        searchRT = [(_s,_p,_o,unifiedGraph)
                                     for _s,_p,_o in unifiedGraph.triples(
                                                (search_s,search_p,search_o))]

                else:
                    assert not self.tripleStore.DAWG_DATASET_COMPLIANCE \
                       or isinstance(self.tripleStore.graph.identifier,URIRef),\
                       "Cannot formally return graph name solutions for the default graph"

                    searchRT = [(_s,_p,_o,self.tripleStore.graph)
                                  for _s,_p,_o in self.tripleStore.graph.triples(
                                        (search_s, search_p, search_o))]

            elif self.tripleStore.DAWG_DATASET_COMPLIANCE \
                 and isinstance(self.tripleStore.graph,ConjunctiveGraph):

                # For query-constructed datasets, match against the 'default graph' -
                # the first Graph with a non-URIRef identifier (or an empty, default graph)
                if isinstance(self.tripleStore.graph, ReadOnlyGraphAggregate):

                        for g in self.tripleStore.graph.graphs:
                            searchRT = []
                            if isinstance(g.identifier, BNode):
                                searchRT = g.triples((search_s, search_p, search_o))
                                break

                else:
                    # match against the default graph
                    searchRT = self.tripleStore.graph.default_context.triples(
                                       (search_s, search_p, search_o))
            else:
                #otherwise, the default graph is the graph queried
                searchRT = self.tripleStore.graph.triples(
                                        (search_s,search_p,search_o))

            if originalGraph:
                self.tripleStore.graph = originalGraph

            for tripleOrQuad in searchRT:

                if self.tripleStore.graphVariable:
                    (result_s,result_p,result_o,parentGraph) = tripleOrQuad
                    if isinstance(self.tripleStore.graph,ConjunctiveGraph) \
                       and self.tripleStore.DAWG_DATASET_COMPLIANCE \
                       and isinstance(parentGraph.identifier, BNode):
                        continue

                    assert isinstance(parentGraph.identifier,URIRef)

                else:
                    (result_s,result_p,result_o) = tripleOrQuad

                # if a user defined constraint has been added, it should be checked now
                if func != None and func(result_s, result_p, result_o) == False:
                    # Oops, this result is not acceptable, jump over it!
                    continue

                # create a copy of the current bindings, by also adding the new
                # ones from result of the search
                new_bindings = self.bindings.copy()
                # @@FIXME: unused code
                # queryTerms = [s,p,o]

                preClash = False
                for searchSlot,searchTerm,result in [
                                (search_s, s, result_s),
                                (search_p, p, result_p),
                                (search_o, o, result_o)]:

                    # searchSlot is what we searched with (variables become none)
                    # searchTerm is the term in the triple pattern
                    # result is the unified term from the dataset
                    if searchSlot == None:

                        # An unknown
                        currBound = new_bindings.get(searchTerm)
                        if currBound is not None:
                            if currBound != result:
                                preClash = True
                        else:
                            new_bindings[searchTerm] = result

                if self.tripleStore.graphVariable:
                    new_bindings[self.tripleStore.graphVariable] = parentGraph.identifier

                # Recursion starts here: create and expand a new child
                child = _SPARQLNode(self, new_bindings, self.rest,
                                    self.tripleStore, expr=self.expr)

                if preClash:
                    child.clash = True
                else:
                    child.expand(constraints)

                # if the child is a clash then no use adding it to the tree,
                # it can be forgotten
                if self.clash == False:
                    self.children.append(child)

            if len(self.children) == 0:
                # this means that the constraints could not be met at all
                # with this binding!!!!
                self.clash = True

        else:
            # this is if all bindings are done; the conditions (ie, global
            # constraints) are still to be checked
            if self.bound == True and self.clash == False:

                for func in constraints:

                    try:
                        if func(self.bindings) == False:
                            self.clash = True
                            break

                    except __HOLE__:
                        self.clash=True

                if not self.clash and self.expr in self.queryProlog.rightMostBGPs:
                    self.noteTopLevelAnswer(self.queryProlog)
dataset/ETHPy150Open RDFLib/rdfextras/rdfextras/sparql/query.py/_SPARQLNode.expandAtClient