# Buggy code
def __or__(self, other):
    # These could be implemented in each individual class,
    # I'm sure, but for now we have this.
    if isinstance(self, group):
        # group() | task -> chord
        return chord(self, body=other, app=self._app)
    elif isinstance(other, group):
        # unroll group with one member
        other = maybe_unroll_group(other)
        if isinstance(self, _chain):
            # chain | group() -> chain
            return _chain(seq_concat_item(self.unchain_tasks(), other), app=self._app)
        # task | group() -> chain
        return _chain(self, other, app=self.app)

    if not isinstance(self, _chain) and isinstance(other, _chain):
        # task | chain -> chain
        return _chain(seq_concat_seq((self,), other.unchain_tasks()), app=self._app)
    elif isinstance(other, _chain):
        # chain | chain -> chain
        return _chain(
            seq_concat_seq(self.unchain_tasks(), other.unchain_tasks()), app=self._app
        )
    elif isinstance(self, chord):
        # chord | task ->  attach to body
        sig = self.clone()
        sig.body = sig.body | other
        return sig
    elif isinstance(other, Signature):
        if isinstance(self, _chain):
            if self.tasks and isinstance(self.tasks[-1], group):
                # CHAIN [last item is group] | TASK -> chord
                sig = self.clone()
                sig.tasks[-1] = chord(sig.tasks[-1], other, app=self._app)
                return sig
            elif self.tasks and isinstance(self.tasks[-1], chord):
                # CHAIN [last item is chord] -> chain with chord body.
                sig = self.clone()
                sig.tasks[-1].body = sig.tasks[-1].body | other
                return sig
            else:
                # chain | task -> chain
                return _chain(
                    seq_concat_item(self.unchain_tasks(), other), app=self._app
                )
        # task | task -> chain
        return _chain(self, other, app=self._app)
    return NotImplemented


# Traceback (RuntimeError)
Traceback (most recent call last):
File "test.py", line 30, in <module>
chain([chain(s2)]).apply_async() # issue
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 642, in apply_async
dict(self.options, **options) if options else self.options))
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 660, in run
task_id, group_id, chord,
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 721, in prepare_steps
task = task.clone(args, kwargs)
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 620, in clone
for sig in signature.kwargs['tasks']
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 1513, in maybe_signature
d = d.clone()
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 620, in clone
for sig in signature.kwargs['tasks']
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 1513, in maybe_signature
d = d.clone()
...
..
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 620, in clone
for sig in signature.kwargs['tasks']
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 1513, in maybe_signature
d = d.clone()
keeps repeating
..
..
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 617, in clone
signature = Signature.clone(self, *args, **kwargs)
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 272, in clone
app=self._app)
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 153, in from_dict
return target_cls.from_dict(d, app=app)
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 599, in from_dict
return _upgrade(d, _chain(tasks, app=app, **d['options']))
File "/bb/bin/dl/celery/4.4/celery/canvas.py", line 602, in __init__
tasks = (regen(tasks[0]) if len(tasks) == 1 and is_list(tasks[0])
File "/bb/bin/dl/celery/4.4/kombu/utils/functional.py", line 256, in is_list
return isinstance(l, iters) and not isinstance(l, scalars or ())
File "/opt/bb/lib/python2.7/abc.py", line 132, in __instancecheck__
if subclass is not None and subclass in cls._abc_cache:
File "/opt/bb/lib/python2.7/_weakrefset.py", line 72, in __contains__
wr = ref(item)
RuntimeError: maximum recursion depth exceeded

# Fixed code
def __or__(self, other):
    # These could be implemented in each individual class,
    # I'm sure, but for now we have this.
    if isinstance(self, group):
        # group() | task -> chord
        return chord(self, body=other, app=self._app)
    elif isinstance(other, group):
        # unroll group with one member
        other = maybe_unroll_group(other)
        if isinstance(self, _chain):
            # chain | group() -> chain
            tasks = self.unchain_tasks()
            if not tasks:
                # If the chain is empty, return the group
                return other
            return _chain(seq_concat_item(tasks, other), app=self._app)
        # task | group() -> chain
        return _chain(self, other, app=self.app)

    if not isinstance(self, _chain) and isinstance(other, _chain):
        # task | chain -> chain
        return _chain(seq_concat_seq((self,), other.unchain_tasks()), app=self._app)
    elif isinstance(other, _chain):
        # chain | chain -> chain
        return _chain(
            seq_concat_seq(self.unchain_tasks(), other.unchain_tasks()), app=self._app
        )
    elif isinstance(self, chord):
        # chord | task ->  attach to body
        sig = self.clone()
        sig.body = sig.body | other
        return sig
    elif isinstance(other, Signature):
        if isinstance(self, _chain):
            if self.tasks and isinstance(self.tasks[-1], group):
                # CHAIN [last item is group] | TASK -> chord
                sig = self.clone()
                sig.tasks[-1] = chord(sig.tasks[-1], other, app=self._app)
                return sig
            elif self.tasks and isinstance(self.tasks[-1], chord):
                # CHAIN [last item is chord] -> chain with chord body.
                sig = self.clone()
                sig.tasks[-1].body = sig.tasks[-1].body | other
                return sig
            else:
                # chain | task -> chain
                return _chain(
                    seq_concat_item(self.unchain_tasks(), other), app=self._app
                )
        # task | task -> chain
        return _chain(self, other, app=self._app)
    return NotImplemented