# Buggy code
def add(self, cmd, allow_duplicates=False):
    """
    Add a new command or commands to this CmdSet, a list of
    commands or a cmdset to this cmdset. Note that this is *not*
    a merge operation (that is handled by the + operator).

    Args:
        cmd (Command, list, Cmdset): This allows for adding one or
            more commands to this Cmdset in one go. If another Cmdset
            is given, all its commands will be added.
        allow_duplicates (bool, optional): If set, will not try to remove
            duplicate cmds in the set. This is needed during the merge process
            to avoid wiping commands coming from cmdsets with duplicate=True.

    Notes:
        If cmd already exists in set, it will replace the old one
        (no priority checking etc happens here). This is very useful
        when overloading default commands).

        If cmd is another cmdset class or -instance, the commands of
        that command set is added to this one, as if they were part of
        the original cmdset definition. No merging or priority checks
        are made, rather later added commands will simply replace
        existing ones to make a unique set.

    """

    if inherits_from(cmd, "evennia.commands.cmdset.CmdSet"):
        # cmd is a command set so merge all commands in that set
        # to this one. We raise a visible error if we created
        # an infinite loop (adding cmdset to itself somehow)
        try:
            cmd = self._instantiate(cmd)
        except RuntimeError:
            string = "Adding cmdset %(cmd)s to %(class)s lead to an "
            string += "infinite loop. When adding a cmdset to another, "
            string += "make sure they are not themself cyclically added to "
            string += "the new cmdset somewhere in the chain."
            raise RuntimeError(_(string) % {"cmd": cmd, "class": self.__class__})
        cmds = cmd.commands
    elif is_iter(cmd):
        cmds = [self._instantiate(c) for c in cmd]
    else:
        cmds = [self._instantiate(cmd)]
    commands = self.commands
    system_commands = self.system_commands
    for cmd in cmds:
        # add all commands
        if not hasattr(cmd, "obj"):
            cmd.obj = self.cmdsetobj
        try:
            ic = commands.index(cmd)
            commands[ic] = cmd  # replace
        except ValueError:
            commands.append(cmd)
        self.commands = commands
        if not allow_duplicates:
            # extra run to make sure to avoid doublets
            self.commands = list(set(self.commands))
        # add system_command to separate list as well,
        # for quick look-up
        if cmd.key.startswith("__"):
            try:
                ic = system_commands.index(cmd)
                system_commands[ic] = cmd  # replace
            except ValueError:
                system_commands.append(cmd)


# Traceback (builtins.AttributeError)
20-10-09 00:23:47 Unhandled Error
Traceback (most recent call last):
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/evennia/commands/cmdhandler.py", line 631, in _run_command
ret = cmd.func()
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/evennia/commands/default/building.py", line 2658, in func
).addCallback(get_cmdset_callback)
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/twisted/internet/defer.py", line 322, in addCallback
callbackKeywords=kw)
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/twisted/internet/defer.py", line 311, in addCallbacks
self._runCallbacks()
--- <exception caught here> ---
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/evennia/commands/default/building.py", line 2581, in get_cmdset_callback
string = self.format_output(obj, cmdset)
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/evennia/commands/default/building.py", line 2512, in format_output
avail_cmdset = sorted([cmd.key for cmd in avail_cmdset if cmd.access(obj, "cmd")])
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/evennia/commands/default/building.py", line 2512, in <listcomp>
avail_cmdset = sorted([cmd.key for cmd in avail_cmdset if cmd.access(obj, "cmd")])
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/evennia/commands/command.py", line 328, in access
return self.lockhandler.check(srcobj, access_type, default=default)
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/evennia/locks/lockhandler.py", line 555, in check
bool(tup[0](accessing_obj, self.obj, *tup[1], **tup[2])) for tup in func_tup
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/evennia/locks/lockhandler.py", line 555, in <genexpr>
bool(tup[0](accessing_obj, self.obj, *tup[1], **tup[2])) for tup in func_tup
File "/home/ubuntu/workspace/E37/lib/python3.7/site-packages/evennia/locks/lockfuncs.py", line 633, in holds
return hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.dbid)
builtins.AttributeError: 'NoneType' object has no attribute 'dbid'

# Fixed code
def add(self, cmd, allow_duplicates=False):
    """
    Add a new command or commands to this CmdSet, a list of
    commands or a cmdset to this cmdset. Note that this is *not*
    a merge operation (that is handled by the + operator).

    Args:
        cmd (Command, list, Cmdset): This allows for adding one or
            more commands to this Cmdset in one go. If another Cmdset
            is given, all its commands will be added.
        allow_duplicates (bool, optional): If set, will not try to remove
            duplicate cmds in the set. This is needed during the merge process
            to avoid wiping commands coming from cmdsets with duplicate=True.

    Notes:
        If cmd already exists in set, it will replace the old one
        (no priority checking etc happens here). This is very useful
        when overloading default commands).

        If cmd is another cmdset class or -instance, the commands of
        that command set is added to this one, as if they were part of
        the original cmdset definition. No merging or priority checks
        are made, rather later added commands will simply replace
        existing ones to make a unique set.

    """

    if inherits_from(cmd, "evennia.commands.cmdset.CmdSet"):
        # cmd is a command set so merge all commands in that set
        # to this one. We raise a visible error if we created
        # an infinite loop (adding cmdset to itself somehow)
        cmdset = cmd
        try:
            cmdset = self._instantiate(cmdset)
        except RuntimeError:
            err = (
                "Adding cmdset {cmdset} to {cls} lead to an "
                "infinite loop. When adding a cmdset to another, "
                "make sure they are not themself cyclically added to "
                "the new cmdset somewhere in the chain."
            )
            raise RuntimeError(_(err.format(cmdset=cmdset, cls=self.__class__)))
        cmds = cmdset.commands
    elif is_iter(cmd):
        cmds = [self._instantiate(c) for c in cmd]
    else:
        cmds = [self._instantiate(cmd)]
    commands = self.commands
    system_commands = self.system_commands
    for cmd in cmds:
        # add all commands
        if not hasattr(cmd, "obj") or cmd.obj is None:
            cmd.obj = self.cmdsetobj
        try:
            ic = commands.index(cmd)
            commands[ic] = cmd  # replace
        except ValueError:
            commands.append(cmd)
        self.commands = commands
        if not allow_duplicates:
            # extra run to make sure to avoid doublets
            self.commands = list(set(self.commands))
        # add system_command to separate list as well,
        # for quick look-up
        if cmd.key.startswith("__"):
            try:
                ic = system_commands.index(cmd)
                system_commands[ic] = cmd  # replace
            except ValueError:
                system_commands.append(cmd)