Fix examine using wrong cmdset target for accounts. Resolve #1886

This commit is contained in:
Griatch 2019-10-17 00:15:09 +02:00
parent 0ef2e667e4
commit bc5210eb9c
6 changed files with 139 additions and 106 deletions

View file

@ -180,7 +180,7 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
except ImportError as exc: except ImportError as exc:
if len(trace()) > 2: if len(trace()) > 2:
# error in module, make sure to not hide it. # error in module, make sure to not hide it.
_, _, tb = sys.exc_info() dum, dum, tb = sys.exc_info()
raise exc.with_traceback(tb) raise exc.with_traceback(tb)
else: else:
# try next suggested path # try next suggested path
@ -191,7 +191,7 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
except AttributeError as exc: except AttributeError as exc:
if len(trace()) > 2: if len(trace()) > 2:
# Attribute error within module, don't hide it # Attribute error within module, don't hide it
_, _, tb = sys.exc_info() dum, dum, tb = sys.exc_info()
raise exc.with_traceback(tb) raise exc.with_traceback(tb)
else: else:
errstring += _("\n(Unsuccessfully tried '%s')." % python_path) errstring += _("\n(Unsuccessfully tried '%s')." % python_path)

View file

@ -1596,7 +1596,7 @@ class CmdSetAttribute(ObjManipCommand):
or |c{ |n. or |c{ |n.
Once you have stored a Python primitive as noted above, you can include Once you have stored a Python primitive as noted above, you can include
|c[<key>]|n in <attr> to reference nested values in e.g. a list or dict. |c[<key>]|n in <attr> to reference nested values in e.g. a list or dict.
Remember that if you use Python primitives like this, you must Remember that if you use Python primitives like this, you must
write proper Python syntax too - notably you must include quotes write proper Python syntax too - notably you must include quotes
@ -2352,7 +2352,6 @@ class CmdExamine(ObjManipCommand):
returns a string. returns a string.
""" """
string = "\n|wName/key|n: |c%s|n (%s)" % (obj.name, obj.dbref) string = "\n|wName/key|n: |c%s|n (%s)" % (obj.name, obj.dbref)
if hasattr(obj, "aliases") and obj.aliases.all(): if hasattr(obj, "aliases") and obj.aliases.all():
string += "\n|wAliases|n: %s" % (", ".join(utils.make_iter(str(obj.aliases)))) string += "\n|wAliases|n: %s" % (", ".join(utils.make_iter(str(obj.aliases))))
@ -2534,9 +2533,11 @@ class CmdExamine(ObjManipCommand):
# If we don't have special info access, just look at the object instead. # If we don't have special info access, just look at the object instead.
self.msg(caller.at_look(obj)) self.msg(caller.at_look(obj))
return return
obj_session = obj.sessions.get()[0] if obj.sessions.count() else None
# using callback for printing result whenever function returns. # using callback for printing result whenever function returns.
get_and_merge_cmdsets( get_and_merge_cmdsets(
obj, self.session, self.account, obj, "object", self.raw_string obj, obj_session, self.account, obj, "object", self.raw_string
).addCallback(get_cmdset_callback) ).addCallback(get_cmdset_callback)
else: else:
self.msg("You need to supply a target to examine.") self.msg("You need to supply a target to examine.")
@ -2578,15 +2579,25 @@ class CmdExamine(ObjManipCommand):
# we are only interested in specific attributes # we are only interested in specific attributes
caller.msg(self.format_attributes(obj, attrname, crop=False)) caller.msg(self.format_attributes(obj, attrname, crop=False))
else: else:
session = obj.sessions.get()[0]
if obj.sessions.count(): if obj.sessions.count():
mergemode = "session" mergemode = "session"
elif self.account_mode: elif self.account_mode:
mergemode = "account" mergemode = "account"
else: else:
mergemode = "object" mergemode = "object"
account = None
objct = None
if self.account_mode:
account = obj
else:
account = obj.account
objct = obj
# using callback to print results whenever function returns. # using callback to print results whenever function returns.
get_and_merge_cmdsets( get_and_merge_cmdsets(
obj, self.session, self.account, obj, mergemode, self.raw_string obj, session, account, objct, mergemode, self.raw_string
).addCallback(get_cmdset_callback) ).addCallback(get_cmdset_callback)

View file

@ -15,7 +15,7 @@ system but they don't necessarily need to follow each other in the exact
sequence. sequence.
Each state module must make a class `State` available in the global scope. This Each state module must make a class `State` available in the global scope. This
should be a child of `evennia.contribs.evscaperoom.state.BaseState`. The should be a child of `evennia.contrib.evscaperoom.state.BaseState`. The
methods on this class will be called to initialize the state and clean up etc. methods on this class will be called to initialize the state and clean up etc.
There are no other restrictions on the module. There are no other restrictions on the module.

View file

@ -417,7 +417,7 @@ CMDSET_CHARACTER = "commands.default_cmdsets.CharacterCmdSet"
# Command set for accounts without a character (ooc) # Command set for accounts without a character (ooc)
CMDSET_ACCOUNT = "commands.default_cmdsets.AccountCmdSet" CMDSET_ACCOUNT = "commands.default_cmdsets.AccountCmdSet"
# Location to search for cmdsets if full path not given # Location to search for cmdsets if full path not given
CMDSET_PATHS = ["commands", "evennia", "contribs"] CMDSET_PATHS = ["commands", "evennia", "evennia.contrib"]
# Fallbacks for cmdset paths that fail to load. Note that if you change the path for your # Fallbacks for cmdset paths that fail to load. Note that if you change the path for your
# default cmdsets, you will also need to copy CMDSET_FALLBACKS after your change in your # default cmdsets, you will also need to copy CMDSET_FALLBACKS after your change in your
# settings file for it to detect the change. # settings file for it to detect the change.

View file

@ -233,7 +233,7 @@ def read_batchfile(pythonpath, file_ending=".py"):
continue continue
break break
if not text and decoderr: if not text and decoderr:
raise UnicodeDecodeError("\n".join(decoderr), bytearray(), 0, 0, '') raise UnicodeDecodeError("\n".join(decoderr), bytearray(), 0, 0, "")
return text return text

View file

@ -8,26 +8,24 @@ import textwrap
class TestBatchprocessorErrors(TestCase): class TestBatchprocessorErrors(TestCase):
@mock.patch.object(utils, "pypath_to_realpath", return_value=[])
@mock.patch.object(utils, 'pypath_to_realpath', return_value=[])
def test_read_batchfile_raises_IOError(self, _): def test_read_batchfile_raises_IOError(self, _):
with self.assertRaises(IOError): with self.assertRaises(IOError):
batchprocessors.read_batchfile('foopath') batchprocessors.read_batchfile("foopath")
@mock.patch.object(utils, 'pypath_to_realpath', return_value=['pathfoo']) @mock.patch.object(utils, "pypath_to_realpath", return_value=["pathfoo"])
@mock.patch.object(codecs, 'open', side_effect=ValueError('decodeerr')) @mock.patch.object(codecs, "open", side_effect=ValueError("decodeerr"))
@mock.patch.object(batchprocessors, '_ENCODINGS', ['fooencoding']) @mock.patch.object(batchprocessors, "_ENCODINGS", ["fooencoding"])
def test_read_batchfile_raises_UnicodeDecodeError(self, *_): def test_read_batchfile_raises_UnicodeDecodeError(self, *_):
with self.assertRaises(UnicodeDecodeError, msg='decodeerr'): with self.assertRaises(UnicodeDecodeError, msg="decodeerr"):
batchprocessors.read_batchfile('foopath') batchprocessors.read_batchfile("foopath")
class TestBatchCommandProcessor(TestCase): class TestBatchCommandProcessor(TestCase):
@mock.patch.object(batchprocessors, "read_batchfile")
@mock.patch.object(batchprocessors, 'read_batchfile')
def test_parses_2_commands(self, mocked_read): def test_parses_2_commands(self, mocked_read):
mocked_read.return_value = textwrap.dedent( mocked_read.return_value = textwrap.dedent(
r""" r"""
@create rock @create rock
# #
@ -35,93 +33,102 @@ class TestBatchCommandProcessor(TestCase):
A big rock. You can tell is ancient. A big rock. You can tell is ancient.
# #
""") """
commands = batchprocessors.BATCHCMD.parse_file('foopath') )
self.assertEqual([ commands = batchprocessors.BATCHCMD.parse_file("foopath")
'@create rock', '@set rock/desc =\nA big rock. You can tell is ancient.'], self.assertEqual(
commands) ["@create rock", "@set rock/desc =\nA big rock. You can tell is ancient."], commands
)
@mock.patch.object(batchprocessors, 'read_batchfile') @mock.patch.object(batchprocessors, "read_batchfile")
def test_parses_INSERT(self, mocked_read): def test_parses_INSERT(self, mocked_read):
mocked_read.side_effect = [ mocked_read.side_effect = [
textwrap.dedent(r""" textwrap.dedent(
r"""
@create sky @create sky
# #
#INSERT another.ev #INSERT another.ev
# #
@create sun @create sun
# #
"""), """
textwrap.dedent(r""" ),
textwrap.dedent(
r"""
@create bird @create bird
# #
@create cloud @create cloud
# #
""") """
),
] ]
commands = batchprocessors.BATCHCMD.parse_file('foopath') commands = batchprocessors.BATCHCMD.parse_file("foopath")
self.assertEqual(commands, ["@create sky", "@create bird", "@create cloud", "@create sun"])
self.assertEqual( self.assertEqual(
commands, mocked_read.mock_calls,
['@create sky', '@create bird', '@create cloud', '@create sun']) [mock.call("foopath", file_ending=".ev"), mock.call("another.ev", file_ending=".ev")],
self.assertEqual(mocked_read.mock_calls, [ )
mock.call('foopath', file_ending='.ev'),
mock.call('another.ev', file_ending='.ev')])
@mock.patch.object(batchprocessors, 'read_batchfile') @mock.patch.object(batchprocessors, "read_batchfile")
def test_parses_INSERT_raises_IOError(self, mocked_read): def test_parses_INSERT_raises_IOError(self, mocked_read):
mocked_read.side_effect = [ mocked_read.side_effect = [
textwrap.dedent(r""" textwrap.dedent(
r"""
@create sky @create sky
# #
#INSERT x #INSERT x
# #
@create sun @create sun
# #
"""), """
IOError ),
IOError,
] ]
with self.assertRaises(IOError, msg='#INSERT x failed.'): with self.assertRaises(IOError, msg="#INSERT x failed."):
batchprocessors.BATCHCMD.parse_file('foopath') batchprocessors.BATCHCMD.parse_file("foopath")
self.assertEqual(mocked_read.mock_calls, [ self.assertEqual(
mock.call('foopath', file_ending='.ev'), mocked_read.mock_calls,
mock.call('x', file_ending='.ev')]) [mock.call("foopath", file_ending=".ev"), mock.call("x", file_ending=".ev")],
)
class TestBatchCodeProcessor(TestCase): class TestBatchCodeProcessor(TestCase):
@mock.patch.object(batchprocessors, "read_batchfile")
@mock.patch.object(batchprocessors, 'read_batchfile')
def test_parses_one_codeblock(self, mocked_read): def test_parses_one_codeblock(self, mocked_read):
mocked_read.return_value = textwrap.dedent( mocked_read.return_value = textwrap.dedent(
r""" r"""
print("Hello") print("Hello")
""") """
commands = batchprocessors.BATCHCODE.parse_file('foopath') )
self.assertEqual([ commands = batchprocessors.BATCHCODE.parse_file("foopath")
'# batchcode code:\n\nprint("Hello")\n'], self.assertEqual(['# batchcode code:\n\nprint("Hello")\n'], commands)
commands)
@mock.patch.object(batchprocessors, 'read_batchfile') @mock.patch.object(batchprocessors, "read_batchfile")
def test_parses_codeblocks(self, mocked_read): def test_parses_codeblocks(self, mocked_read):
mocked_read.return_value = textwrap.dedent( mocked_read.return_value = textwrap.dedent(
r""" r"""
#CODE #CODE
print("Hello") print("Hello")
#CODE #CODE
a = 1 a = 1
b = [1, b = [1,
2, 3] 2, 3]
""") """
commands = batchprocessors.BATCHCODE.parse_file('foopath') )
self.assertEqual([ commands = batchprocessors.BATCHCODE.parse_file("foopath")
'# batchcode code:\n\n', self.assertEqual(
'# batchcode code:\n\nprint("Hello")\n', [
'# batchcode code:\n\na = 1\nb = [1,\n2, 3]\n'], "# batchcode code:\n\n",
commands) '# batchcode code:\n\nprint("Hello")\n',
"# batchcode code:\n\na = 1\nb = [1,\n2, 3]\n",
],
commands,
)
@mock.patch.object(batchprocessors, 'read_batchfile') @mock.patch.object(batchprocessors, "read_batchfile")
def test_parses_header_and_two_codeblock(self, mocked_read): def test_parses_header_and_two_codeblock(self, mocked_read):
mocked_read.return_value = textwrap.dedent( mocked_read.return_value = textwrap.dedent(
r""" r"""
#HEADER #HEADER
a = 100 a = 100
#CODE #CODE
@ -129,71 +136,86 @@ class TestBatchCodeProcessor(TestCase):
#CODE #CODE
a += 100 a += 100
a == 100 a == 100
""") """
commands = batchprocessors.BATCHCODE.parse_file('foopath') )
self.assertEqual([ commands = batchprocessors.BATCHCODE.parse_file("foopath")
'# batchcode header:\n\na = 100\n\n\n# batchcode code:\n\n', self.assertEqual(
'# batchcode header:\n\na = 100\n\n\n# batchcode code:\n\na += 100\n', [
'# batchcode header:\n\na = 100\n\n\n# batchcode code:\n\na += 100\na == 100\n'], "# batchcode header:\n\na = 100\n\n\n# batchcode code:\n\n",
commands) "# batchcode header:\n\na = 100\n\n\n# batchcode code:\n\na += 100\n",
"# batchcode header:\n\na = 100\n\n\n# batchcode code:\n\na += 100\na == 100\n",
],
commands,
)
@mock.patch.object(batchprocessors, 'read_batchfile') @mock.patch.object(batchprocessors, "read_batchfile")
def test_parses_INSERT(self, mocked_read): def test_parses_INSERT(self, mocked_read):
mocked_read.side_effect = [ mocked_read.side_effect = [
textwrap.dedent(r""" textwrap.dedent(
r"""
a = 1 a = 1
#INSERT another.py #INSERT another.py
"""), """
textwrap.dedent(r""" ),
textwrap.dedent(
r"""
print("Hello") print("Hello")
""") """
),
] ]
commands = batchprocessors.BATCHCODE.parse_file('foopath') commands = batchprocessors.BATCHCODE.parse_file("foopath")
self.assertEqual( self.assertEqual(
commands, [ commands,
'# batchcode code:\n' [
'\n' "# batchcode code:\n"
'a = 1\n' "\n"
'# batchcode insert (another.py):# batchcode code:\n' "a = 1\n"
'\n' "# batchcode insert (another.py):# batchcode code:\n"
'print("Hello")\n' "\n"
'\n']) 'print("Hello")\n'
self.assertEqual(mocked_read.mock_calls, [ "\n"
mock.call('foopath', file_ending='.py'), ],
mock.call('another.py', file_ending='.py')]) )
self.assertEqual(
mocked_read.mock_calls,
[mock.call("foopath", file_ending=".py"), mock.call("another.py", file_ending=".py")],
)
@mock.patch.object(batchprocessors, 'read_batchfile') @mock.patch.object(batchprocessors, "read_batchfile")
def test_parses_INSERT_raises_IOError(self, mocked_read): def test_parses_INSERT_raises_IOError(self, mocked_read):
mocked_read.side_effect = [ mocked_read.side_effect = [
textwrap.dedent(r""" textwrap.dedent(
r"""
#INSERT x #INSERT x
"""), """
IOError ),
IOError,
] ]
with self.assertRaises(IOError, msg='#INSERT x failed.'): with self.assertRaises(IOError, msg="#INSERT x failed."):
batchprocessors.BATCHCODE.parse_file('foopath') batchprocessors.BATCHCODE.parse_file("foopath")
self.assertEqual(mocked_read.mock_calls, [ self.assertEqual(
mock.call('foopath', file_ending='.py'), mocked_read.mock_calls,
mock.call('x', file_ending='.py')]) [mock.call("foopath", file_ending=".py"), mock.call("x", file_ending=".py")],
)
@mock.patch('builtins.exec') @mock.patch("builtins.exec")
def test_execs_codeblock(self, mocked_exec): def test_execs_codeblock(self, mocked_exec):
err = batchprocessors.BATCHCODE.code_exec( err = batchprocessors.BATCHCODE.code_exec(
'# batchcode code:\n\nprint("Hello")\n', '# batchcode code:\n\nprint("Hello")\n', extra_environ={}
extra_environ={}) )
self.assertIsNone(err) self.assertIsNone(err)
@mock.patch('builtins.exec') @mock.patch("builtins.exec")
def test_execs_codeblock_with_extra_environ(self, mocked_exec): def test_execs_codeblock_with_extra_environ(self, mocked_exec):
err = batchprocessors.BATCHCODE.code_exec( err = batchprocessors.BATCHCODE.code_exec(
'# batchcode code:\n\nprint("Hello")\n', '# batchcode code:\n\nprint("Hello")\n', extra_environ={"foo": "bar", "baz": True}
extra_environ={'foo': 'bar', 'baz': True}) )
self.assertIsNone(err) self.assertIsNone(err)
@mock.patch('builtins.exec') @mock.patch("builtins.exec")
def test_execs_codeblock_raises(self, mocked_exec): def test_execs_codeblock_raises(self, mocked_exec):
mocked_exec.side_effect = Exception mocked_exec.side_effect = Exception
err = batchprocessors.BATCHCODE.code_exec( err = batchprocessors.BATCHCODE.code_exec(
'# batchcode code:\n\nprint("Hello")\nprint("Evennia")', '# batchcode code:\n\nprint("Hello")\nprint("Evennia")', extra_environ={}
extra_environ={}) )
self.assertIsNotNone(err) self.assertIsNotNone(err)