def test_commandname_success(self): from muss.commands.help import Help from muss.commands.social import Chat from muss.commands.test import Poke for command_tuple in [("poke", Poke), ("help", Help), ("chat", Chat)]: name, command = command_tuple pattern = parser.CommandName()("command") parse_result = pattern.parseString(name, parseAll=True) self.assertEqual(parse_result["command"], command_tuple)
def test_require_full(self): from muss.commands.building import Destroy try: parse_result = parser.CommandName()("command").parseString("d") self.assertNotEqual(parse_result["command"][1], Destroy) # ^-- if there's only one other command starting with d # v-- if there's more than one except parser.AmbiguityError as e: self.assertNotIn(Destroy, [b for a, b in e.matches]) except: self.fail()
def test_commandname_ambiguous(self): self.assertRaises(parser.AmbiguityError, parser.CommandName().parseString, "test", parseAll=True)
def test_commandname_notfound(self): self.assertRaises(parser.NotFoundError, parser.CommandName().parseString, "noncommand", parseAll=True) self.assert_response("usage notacommand", 'I don\'t know of a command called "notacommand"')
def args(cls, player): return parser.CommandName()("command")
def handle(self, player, line): """ Parse the input line for a command and arguments, reporting any errors or unresolvable ambiguity. """ line = line.strip() if not line: return split_line = line.split(None, 1) if len(split_line) == 1: split_line.append("") first, rest_of_line = split_line name = "" command = None # Check for nospace commands nospace_matches = [] for command in all_commands(): for name in command().nospace_names: # We can't use find_by_name because we don't know where the # nospace command ends. if line.startswith(name): # No partial matching, for the same reason. nospace_matches.append((name, command)) if len(nospace_matches) == 1: name, command = nospace_matches[0] if len(line) > len(name): arguments = line[len(name):] else: arguments = "" try: try: if len(nospace_matches) > 1: raise parser.AmbiguityError(line, 0, parser.Command.errmsg, parser.Command, nospace_matches) # Check for normal command matches pattern = parser.CommandName(fullOnly=True)("command") parse_result = pattern.parseString(first, parseAll=True) matched = parse_result["command"] arguments = rest_of_line if nospace_matches: # We found a regular command, but already had a nospace # command. raise parser.AmbiguityError(line, 0, parser.Command.errmsg, parser.Command, nospace_matches + [matched]) else: name, command = parse_result["command"] except parser.NotFoundError as e: # No commands match, what about exits? exits = [(exit.name, exit) for exit in db.find_all(lambda x: x.type == 'exit' and x.location == player.location) ] try: pattern = parser.OneOf(exits)("exit").setName("exit") parse_result = pattern.parseString(first, parseAll=True) # OneOf(exits) parsed, so exactly one exit matches. if not nospace_matches: command = commands.world.Go arguments = first except parser.AmbiguityError as f: # Multiple exits match and no full commands do. if not nospace_matches: player.send(f.verbose()) return # At this point there can only be one nospace command. # Let it fall all the way through to execution. except parser.NotFoundError as _: raise e except parser.NotFoundError as e: if not nospace_matches: message = e.verbose() # Check whether a require_full command would have matched rf_commands = [c for c in all_commands() if c.require_full] # (ignoring perfect matches because we would have already seen # them) rf_matches = utils.find_by_name(e.pstr, rf_commands, attributes=["names"])[1] if len(rf_matches) == 1: rf_name, rf_command = rf_matches[0] message += (" (If you mean \"{},\" you'll need to use the " "whole command name.)".format(rf_name)) elif rf_matches: rf_names = [c[0] for c in rf_matches] message += (" (If you meant one of these, you'll need to " "use the whole command name: {}.)".format( ", ".join(rf_names))) player.send(message) return except parser.AmbiguityError as e: # it's not clear from the name which command the user intended, # so see if any of their argument specs match what we got parsable_matches = [] for possible_name, possible_command in e.matches + nospace_matches: try: if nospace_matches and ((possible_name, possible_command) == nospace_matches[0]): test_arguments = line.split(possible_name, 1)[1] else: test_arguments = rest_of_line pattern = possible_command.args(player) args = pattern.parseString(test_arguments, parseAll=True) parsable_matches.append((possible_name, possible_command)) except pyparsing.ParseException: # user probably didn't intend this command; skip it. pass except utils.UserError: parsable_matches.append((possible_name, possible_command)) if len(parsable_matches) == 1: name, command = parsable_matches[0] if len(line) > len(name): if parsable_matches[0] in nospace_matches: arguments = line[len(name):] else: arguments = rest_of_line else: arguments = "" else: if parsable_matches: # we can at least narrow the field a little e.matches = parsable_matches player.send(e.verbose()) return # okay! we have a command! let's parse it. try: args = command.args(player).parseString(arguments, parseAll=True) command().execute(player, args) except utils.UserError as e: if hasattr(e, "verbose"): player.send(e.verbose()) else: player.send(str(e)) except pyparsing.ParseException as e: usages = command().usages if len(usages) > 1: from commands.help import Usage player.send("Usage:") Usage().execute(player, {"command": (name, command)}, tabs=True) else: player.send("Usage: " + usages[0]) player.send("(Try \"help {}\" for more help.)".format(name))