Merge branch 'evennia:develop' into develop
This commit is contained in:
commit
7878b600be
25 changed files with 339 additions and 345 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -59,3 +59,10 @@ docs/build
|
||||||
|
|
||||||
# Obsidian
|
# Obsidian
|
||||||
.obsidian
|
.obsidian
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv/
|
||||||
|
.env/
|
||||||
|
|
||||||
|
# Testing folder
|
||||||
|
.test_game_dir
|
||||||
|
|
|
||||||
5
Makefile
5
Makefile
|
|
@ -8,6 +8,7 @@ TESTS ?= evennia
|
||||||
default:
|
default:
|
||||||
@echo " Usage: "
|
@echo " Usage: "
|
||||||
@echo " make install - install evennia (recommended to activate virtualenv first)"
|
@echo " make install - install evennia (recommended to activate virtualenv first)"
|
||||||
|
@echo " make installextra - install evennia with extra-requirements (activate virtualenv first)"
|
||||||
@echo " make fmt/format - run the black autoformatter on the source code"
|
@echo " make fmt/format - run the black autoformatter on the source code"
|
||||||
@echo " make lint - run black in --check mode"
|
@echo " make lint - run black in --check mode"
|
||||||
@echo " make test - run evennia test suite with all default values."
|
@echo " make test - run evennia test suite with all default values."
|
||||||
|
|
@ -17,6 +18,10 @@ default:
|
||||||
install:
|
install:
|
||||||
pip install -e .
|
pip install -e .
|
||||||
|
|
||||||
|
installextra:
|
||||||
|
pip install -e .
|
||||||
|
pip install -r requirements_extra.txt
|
||||||
|
|
||||||
format:
|
format:
|
||||||
black $(BLACK_FORMAT_CONFIGS) evennia
|
black $(BLACK_FORMAT_CONFIGS) evennia
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -748,7 +748,9 @@ def cmdhandler(
|
||||||
)
|
)
|
||||||
if suggestions:
|
if suggestions:
|
||||||
sysarg += _(" Maybe you meant {command}?").format(
|
sysarg += _(" Maybe you meant {command}?").format(
|
||||||
command=utils.list_to_string(suggestions, endsep=_("or"), addquote=True)
|
command=utils.list_to_string(
|
||||||
|
suggestions, endsep=_("or"), addquote=True
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
sysarg += _(' Type "help" for help.')
|
sysarg += _(' Type "help" for help.')
|
||||||
|
|
|
||||||
|
|
@ -668,7 +668,9 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
|
||||||
self.msg(f"Option |w{new_name}|n was kept as '|w{old_val}|n'.")
|
self.msg(f"Option |w{new_name}|n was kept as '|w{old_val}|n'.")
|
||||||
else:
|
else:
|
||||||
flags[new_name] = new_val
|
flags[new_name] = new_val
|
||||||
self.msg(f"Option |w{new_name}|n was changed from '|w{old_val}|n' to '|w{new_val}|n'.")
|
self.msg(
|
||||||
|
f"Option |w{new_name}|n was changed from '|w{old_val}|n' to '|w{new_val}|n'."
|
||||||
|
)
|
||||||
return {new_name: new_val}
|
return {new_name: new_val}
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.msg(f"|rCould not set option |w{new_name}|r:|n {err}")
|
self.msg(f"|rCould not set option |w{new_name}|r:|n {err}")
|
||||||
|
|
@ -759,7 +761,9 @@ class CmdPassword(COMMAND_DEFAULT_CLASS):
|
||||||
account.set_password(newpass)
|
account.set_password(newpass)
|
||||||
account.save()
|
account.save()
|
||||||
self.msg("Password changed.")
|
self.msg("Password changed.")
|
||||||
logger.log_sec(f"Password Changed: {account} (Caller: {account}, IP: {self.session.address}).")
|
logger.log_sec(
|
||||||
|
f"Password Changed: {account} (Caller: {account}, IP: {self.session.address})."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CmdQuit(COMMAND_DEFAULT_CLASS):
|
class CmdQuit(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
|
||||||
|
|
@ -480,7 +480,7 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
|
||||||
# we supplied an argument on the form obj = perm
|
# we supplied an argument on the form obj = perm
|
||||||
locktype = "edit" if accountmode else "control"
|
locktype = "edit" if accountmode else "control"
|
||||||
if not obj.access(caller, locktype):
|
if not obj.access(caller, locktype):
|
||||||
accountstr = 'account' if accountmode else 'object'
|
accountstr = "account" if accountmode else "object"
|
||||||
caller.msg(f"You are not allowed to edit this {accountstr}'s permissions.")
|
caller.msg(f"You are not allowed to edit this {accountstr}'s permissions.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -521,9 +521,7 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
|
||||||
return
|
return
|
||||||
|
|
||||||
if perm in permissions:
|
if perm in permissions:
|
||||||
caller_result.append(
|
caller_result.append(f"\nPermission '{perm}' is already defined on {obj.name}.")
|
||||||
f"\nPermission '{perm}' is already defined on {obj.name}."
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
obj.permissions.add(perm)
|
obj.permissions.add(perm)
|
||||||
plystring = "the Account" if accountmode else "the Object/Character"
|
plystring = "the Account" if accountmode else "the Object/Character"
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,8 @@ class CmdNick(COMMAND_DEFAULT_CLASS):
|
||||||
_, _, old_nickstring, old_replstring = oldnick.value
|
_, _, old_nickstring, old_replstring = oldnick.value
|
||||||
caller.nicks.remove(old_nickstring, category=nicktype)
|
caller.nicks.remove(old_nickstring, category=nicktype)
|
||||||
caller.msg(
|
caller.msg(
|
||||||
f"{nicktypestr} removed: '|w{old_nickstring}|n' -> |w{old_replstring}|n.")
|
f"{nicktypestr} removed: '|w{old_nickstring}|n' -> |w{old_replstring}|n."
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
caller.msg("No matching nicks to remove.")
|
caller.msg("No matching nicks to remove.")
|
||||||
return
|
return
|
||||||
|
|
@ -242,9 +243,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS):
|
||||||
for nick in nicks:
|
for nick in nicks:
|
||||||
_, _, nick, repl = nick.value
|
_, _, nick, repl = nick.value
|
||||||
if nick.startswith(self.lhs):
|
if nick.startswith(self.lhs):
|
||||||
strings.append(
|
strings.append(f"{nicktype.capitalize()}-nick: '{nick}' -> '{repl}'")
|
||||||
f"{nicktype.capitalize()}-nick: '{nick}' -> '{repl}'"
|
|
||||||
)
|
|
||||||
if strings:
|
if strings:
|
||||||
caller.msg("\n".join(strings))
|
caller.msg("\n".join(strings))
|
||||||
else:
|
else:
|
||||||
|
|
@ -265,9 +264,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS):
|
||||||
for nick in nicks:
|
for nick in nicks:
|
||||||
_, _, nick, repl = nick.value
|
_, _, nick, repl = nick.value
|
||||||
if nick.startswith(self.lhs):
|
if nick.startswith(self.lhs):
|
||||||
strings.append(
|
strings.append(f"{nicktype.capitalize()}-nick: '{nick}' -> '{repl}'")
|
||||||
f"{nicktype.capitalize()}-nick: '{nick}' -> '{repl}'"
|
|
||||||
)
|
|
||||||
if strings:
|
if strings:
|
||||||
caller.msg("\n".join(strings))
|
caller.msg("\n".join(strings))
|
||||||
else:
|
else:
|
||||||
|
|
@ -288,9 +285,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS):
|
||||||
for nick in nicks:
|
for nick in nicks:
|
||||||
_, _, nick, repl = nick.value
|
_, _, nick, repl = nick.value
|
||||||
if nick.startswith(self.lhs):
|
if nick.startswith(self.lhs):
|
||||||
strings.append(
|
strings.append(f"{nicktype.capitalize()}-nick: '{nick}' -> '{repl}'")
|
||||||
f"{nicktype.capitalize()}-nick: '{nick}' -> '{repl}'"
|
|
||||||
)
|
|
||||||
if strings:
|
if strings:
|
||||||
caller.msg("\n".join(strings))
|
caller.msg("\n".join(strings))
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -343,6 +343,7 @@ class CraftingRecipeBase:
|
||||||
|
|
||||||
class NonExistentRecipe(CraftingRecipeBase):
|
class NonExistentRecipe(CraftingRecipeBase):
|
||||||
"""A recipe that does not exist and never produces anything."""
|
"""A recipe that does not exist and never produces anything."""
|
||||||
|
|
||||||
allow_craft = True
|
allow_craft = True
|
||||||
allow_reuse = True
|
allow_reuse = True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,21 +61,21 @@ from django.conf import settings
|
||||||
from evennia import CmdSet
|
from evennia import CmdSet
|
||||||
from evennia.commands.default.muxcommand import MuxCommand
|
from evennia.commands.default.muxcommand import MuxCommand
|
||||||
|
|
||||||
_BASIC_MAP_SIZE = settings.BASIC_MAP_SIZE if hasattr(settings, 'BASIC_MAP_SIZE') else 2
|
_BASIC_MAP_SIZE = settings.BASIC_MAP_SIZE if hasattr(settings, "BASIC_MAP_SIZE") else 2
|
||||||
_MAX_MAP_SIZE = settings.BASIC_MAP_SIZE if hasattr(settings, 'MAX_MAP_SIZE') else 10
|
_MAX_MAP_SIZE = settings.BASIC_MAP_SIZE if hasattr(settings, "MAX_MAP_SIZE") else 10
|
||||||
|
|
||||||
# _COMPASS_DIRECTIONS specifies which way to move the pointer on the x/y axes and what characters to use to depict the exits on the map.
|
# _COMPASS_DIRECTIONS specifies which way to move the pointer on the x/y axes and what characters to use to depict the exits on the map.
|
||||||
_COMPASS_DIRECTIONS = {
|
_COMPASS_DIRECTIONS = {
|
||||||
'north': (0, -3, ' | '),
|
"north": (0, -3, " | "),
|
||||||
'south': (0, 3, ' | '),
|
"south": (0, 3, " | "),
|
||||||
'east': (3, 0, '-'),
|
"east": (3, 0, "-"),
|
||||||
'west': (-3, 0, '-'),
|
"west": (-3, 0, "-"),
|
||||||
'northeast': (3, -3, '/'),
|
"northeast": (3, -3, "/"),
|
||||||
'northwest': (-3, -3, '\\'),
|
"northwest": (-3, -3, "\\"),
|
||||||
'southeast': (3, 3, '\\'),
|
"southeast": (3, 3, "\\"),
|
||||||
'southwest': (-3, 3, '/'),
|
"southwest": (-3, 3, "/"),
|
||||||
'up': (0, 0, '^'),
|
"up": (0, 0, "^"),
|
||||||
'down': (0, 0, 'v')
|
"down": (0, 0, "v"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -110,7 +110,7 @@ class Map(object):
|
||||||
for row in range(self.max_length):
|
for row in range(self.max_length):
|
||||||
board.append([])
|
board.append([])
|
||||||
for column in range(int(self.max_width / 5)):
|
for column in range(int(self.max_width / 5)):
|
||||||
board[row].extend([' ', ' ', ' '])
|
board[row].extend([" ", " ", " "])
|
||||||
return board
|
return board
|
||||||
|
|
||||||
def exit_name_as_ordinal(self, ex):
|
def exit_name_as_ordinal(self, ex):
|
||||||
|
|
@ -124,11 +124,13 @@ class Map(object):
|
||||||
"""
|
"""
|
||||||
exit_name = ex.name
|
exit_name = ex.name
|
||||||
if exit_name not in _COMPASS_DIRECTIONS:
|
if exit_name not in _COMPASS_DIRECTIONS:
|
||||||
compass_aliases = [direction in ex.aliases.all() for direction in _COMPASS_DIRECTIONS.keys()]
|
compass_aliases = [
|
||||||
|
direction in ex.aliases.all() for direction in _COMPASS_DIRECTIONS.keys()
|
||||||
|
]
|
||||||
if compass_aliases[0]:
|
if compass_aliases[0]:
|
||||||
exit_name = compass_aliases[0]
|
exit_name = compass_aliases[0]
|
||||||
if exit_name not in _COMPASS_DIRECTIONS:
|
if exit_name not in _COMPASS_DIRECTIONS:
|
||||||
return ''
|
return ""
|
||||||
return exit_name
|
return exit_name
|
||||||
|
|
||||||
def update_pos(self, room, exit_name):
|
def update_pos(self, room, exit_name):
|
||||||
|
|
@ -179,7 +181,7 @@ class Map(object):
|
||||||
# Additionally, if the name of the exit is not ordinal but an alias of it is, use that.
|
# Additionally, if the name of the exit is not ordinal but an alias of it is, use that.
|
||||||
for ex in [x for x in room.exits if x.access(self.caller, "traverse")]:
|
for ex in [x for x in room.exits if x.access(self.caller, "traverse")]:
|
||||||
ex_name = self.exit_name_as_ordinal(ex)
|
ex_name = self.exit_name_as_ordinal(ex)
|
||||||
if not ex_name or ex_name in ['up', 'down']:
|
if not ex_name or ex_name in ["up", "down"]:
|
||||||
continue
|
continue
|
||||||
if self.has_drawn(ex.destination):
|
if self.has_drawn(ex.destination):
|
||||||
continue
|
continue
|
||||||
|
|
@ -205,16 +207,16 @@ class Map(object):
|
||||||
delta_y = int(_COMPASS_DIRECTIONS[ex_name][0] / 3)
|
delta_y = int(_COMPASS_DIRECTIONS[ex_name][0] / 3)
|
||||||
|
|
||||||
# Make modifications if the exit has BOTH up and down exits
|
# Make modifications if the exit has BOTH up and down exits
|
||||||
if ex_name == 'up':
|
if ex_name == "up":
|
||||||
if 'v' in self.grid[x][y]:
|
if "v" in self.grid[x][y]:
|
||||||
self.render_room(room, x, y, p1='^', p2='v')
|
self.render_room(room, x, y, p1="^", p2="v")
|
||||||
else:
|
else:
|
||||||
self.render_room(room, x, y, here='^')
|
self.render_room(room, x, y, here="^")
|
||||||
elif ex_name == 'down':
|
elif ex_name == "down":
|
||||||
if '^' in self.grid[x][y]:
|
if "^" in self.grid[x][y]:
|
||||||
self.render_room(room, x, y, p1='^', p2='v')
|
self.render_room(room, x, y, p1="^", p2="v")
|
||||||
else:
|
else:
|
||||||
self.render_room(room, x, y, here='v')
|
self.render_room(room, x, y, here="v")
|
||||||
else:
|
else:
|
||||||
self.grid[x + delta_x][y + delta_y] = ex_character
|
self.grid[x + delta_x][y + delta_y] = ex_character
|
||||||
|
|
||||||
|
|
@ -234,7 +236,7 @@ class Map(object):
|
||||||
self.has_mapped[room] = [self.curX, self.curY]
|
self.has_mapped[room] = [self.curX, self.curY]
|
||||||
self.render_room(room, self.curX, self.curY)
|
self.render_room(room, self.curX, self.curY)
|
||||||
|
|
||||||
def render_room(self, room, x, y, p1='[', p2=']', here=None):
|
def render_room(self, room, x, y, p1="[", p2="]", here=None):
|
||||||
"""
|
"""
|
||||||
Draw a given room with ascii characters
|
Draw a given room with ascii characters
|
||||||
|
|
||||||
|
|
@ -253,7 +255,7 @@ class Map(object):
|
||||||
you[0] = f"{p1}|n"
|
you[0] = f"{p1}|n"
|
||||||
you[1] = f"{here if here else you[1]}"
|
you[1] = f"{here if here else you[1]}"
|
||||||
if room == self.caller.location:
|
if room == self.caller.location:
|
||||||
you[1] = '|[x|co|n' # Highlight the location you are currently in
|
you[1] = "|[x|co|n" # Highlight the location you are currently in
|
||||||
you[2] = f"{p2}|n"
|
you[2] = f"{p2}|n"
|
||||||
|
|
||||||
self.grid[x][y] = "".join(you)
|
self.grid[x][y] = "".join(you)
|
||||||
|
|
@ -300,6 +302,7 @@ class CmdMap(MuxCommand):
|
||||||
|
|
||||||
Usage: map (optional size)
|
Usage: map (optional size)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "map"
|
key = "map"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
|
||||||
|
|
@ -17,19 +17,32 @@ class TestIngameMap(BaseEvenniaCommandTest):
|
||||||
Expected output:
|
Expected output:
|
||||||
[ ]--[ ]
|
[ ]--[ ]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.west_room = create_object(rooms.Room, key="Room 1")
|
self.west_room = create_object(rooms.Room, key="Room 1")
|
||||||
self.east_room = create_object(rooms.Room, key="Room 2")
|
self.east_room = create_object(rooms.Room, key="Room 2")
|
||||||
create_object(exits.Exit, key="east", aliases=["e"], location=self.west_room, destination=self.east_room)
|
create_object(
|
||||||
create_object(exits.Exit, key="west", aliases=["w"], location=self.east_room, destination=self.west_room)
|
exits.Exit,
|
||||||
|
key="east",
|
||||||
|
aliases=["e"],
|
||||||
|
location=self.west_room,
|
||||||
|
destination=self.east_room,
|
||||||
|
)
|
||||||
|
create_object(
|
||||||
|
exits.Exit,
|
||||||
|
key="west",
|
||||||
|
aliases=["w"],
|
||||||
|
location=self.east_room,
|
||||||
|
destination=self.west_room,
|
||||||
|
)
|
||||||
|
|
||||||
def test_west_room_map_room(self):
|
def test_west_room_map_room(self):
|
||||||
self.char1.location = self.west_room
|
self.char1.location = self.west_room
|
||||||
map_here = ingame_map_display.Map(self.char1).show_map()
|
map_here = ingame_map_display.Map(self.char1).show_map()
|
||||||
self.assertEqual(map_here.strip(), '[|n|[x|co|n]|n--[|n ]|n')
|
self.assertEqual(map_here.strip(), "[|n|[x|co|n]|n--[|n ]|n")
|
||||||
|
|
||||||
def test_east_room_map_room(self):
|
def test_east_room_map_room(self):
|
||||||
self.char1.location = self.east_room
|
self.char1.location = self.east_room
|
||||||
map_here = ingame_map_display.Map(self.char1).show_map()
|
map_here = ingame_map_display.Map(self.char1).show_map()
|
||||||
self.assertEqual(map_here.strip(), '[|n ]|n--[|n|[x|co|n]|n')
|
self.assertEqual(map_here.strip(), "[|n ]|n--[|n|[x|co|n]|n")
|
||||||
|
|
|
||||||
|
|
@ -1421,14 +1421,17 @@ class TestBuildExampleGrid(BaseEvenniaTest):
|
||||||
mock_room_callbacks = mock.MagicMock()
|
mock_room_callbacks = mock.MagicMock()
|
||||||
mock_exit_callbacks = mock.MagicMock()
|
mock_exit_callbacks = mock.MagicMock()
|
||||||
|
|
||||||
|
|
||||||
class TestXyzRoom(xyzroom.XYZRoom):
|
class TestXyzRoom(xyzroom.XYZRoom):
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
mock_room_callbacks.at_object_creation()
|
mock_room_callbacks.at_object_creation()
|
||||||
|
|
||||||
|
|
||||||
class TestXyzExit(xyzroom.XYZExit):
|
class TestXyzExit(xyzroom.XYZExit):
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
mock_exit_callbacks.at_object_creation()
|
mock_exit_callbacks.at_object_creation()
|
||||||
|
|
||||||
|
|
||||||
MAP_DATA = {
|
MAP_DATA = {
|
||||||
"map": """
|
"map": """
|
||||||
|
|
||||||
|
|
@ -1449,13 +1452,14 @@ MAP_DATA = {
|
||||||
("*", "*", "*"): {
|
("*", "*", "*"): {
|
||||||
"desc": "A passage.",
|
"desc": "A passage.",
|
||||||
"prototype_parent": "xyz_exit",
|
"prototype_parent": "xyz_exit",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"map_visual_range": 1,
|
"map_visual_range": 1,
|
||||||
"map_mode": "scan",
|
"map_mode": "scan",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class TestCallbacks(BaseEvenniaTest):
|
class TestCallbacks(BaseEvenniaTest):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
@ -1468,6 +1472,7 @@ class TestCallbacks(BaseEvenniaTest):
|
||||||
|
|
||||||
def _log(msg):
|
def _log(msg):
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
self.grid.log = _log
|
self.grid.log = _log
|
||||||
|
|
||||||
self.map_data = map_data
|
self.map_data = map_data
|
||||||
|
|
@ -1489,5 +1494,9 @@ class TestCallbacks(BaseEvenniaTest):
|
||||||
self.grid.spawn()
|
self.grid.spawn()
|
||||||
|
|
||||||
# Two rooms and 2 exits, Each one should have gotten one `at_object_creation` callback.
|
# Two rooms and 2 exits, Each one should have gotten one `at_object_creation` callback.
|
||||||
self.assertEqual(mock_room_callbacks.at_object_creation.mock_calls, [mock.call(), mock.call()])
|
self.assertEqual(
|
||||||
self.assertEqual(mock_exit_callbacks.at_object_creation.mock_calls, [mock.call(), mock.call()])
|
mock_room_callbacks.at_object_creation.mock_calls, [mock.call(), mock.call()]
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
mock_exit_callbacks.at_object_creation.mock_calls, [mock.call(), mock.call()]
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,9 @@ class MapNode:
|
||||||
# with proper coordinates etc
|
# with proper coordinates etc
|
||||||
typeclass = self.prototype.get("typeclass")
|
typeclass = self.prototype.get("typeclass")
|
||||||
if typeclass is None:
|
if typeclass is None:
|
||||||
raise MapError(f"The prototype {self.prototype} for this node has no 'typeclass' key.", self)
|
raise MapError(
|
||||||
|
f"The prototype {self.prototype} for this node has no 'typeclass' key.", self
|
||||||
|
)
|
||||||
self.log(f" spawning room at xyz={xyz} ({typeclass})")
|
self.log(f" spawning room at xyz={xyz} ({typeclass})")
|
||||||
Typeclass = class_from_module(typeclass)
|
Typeclass = class_from_module(typeclass)
|
||||||
nodeobj, err = Typeclass.create(self.prototype.get("key", "An empty room"), xyz=xyz)
|
nodeobj, err = Typeclass.create(self.prototype.get("key", "An empty room"), xyz=xyz)
|
||||||
|
|
@ -405,7 +407,10 @@ class MapNode:
|
||||||
prot = maplinks[key.lower()][3].prototype
|
prot = maplinks[key.lower()][3].prototype
|
||||||
typeclass = prot.get("typeclass")
|
typeclass = prot.get("typeclass")
|
||||||
if typeclass is None:
|
if typeclass is None:
|
||||||
raise MapError(f"The prototype {self.prototype} for this node has no 'typeclass' key.", self)
|
raise MapError(
|
||||||
|
f"The prototype {self.prototype} for this node has no 'typeclass' key.",
|
||||||
|
self,
|
||||||
|
)
|
||||||
self.log(f" spawning/updating exit xyz={xyz}, direction={key} ({typeclass})")
|
self.log(f" spawning/updating exit xyz={xyz}, direction={key} ({typeclass})")
|
||||||
|
|
||||||
Typeclass = class_from_module(typeclass)
|
Typeclass = class_from_module(typeclass)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from evennia.server.sessionhandler import SESSIONS
|
||||||
import git
|
import git
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
class GitCommand(MuxCommand):
|
class GitCommand(MuxCommand):
|
||||||
"""
|
"""
|
||||||
The shared functionality between git/git evennia
|
The shared functionality between git/git evennia
|
||||||
|
|
@ -22,23 +23,27 @@ class GitCommand(MuxCommand):
|
||||||
split_args = self.args.strip().split(" ", 1)
|
split_args = self.args.strip().split(" ", 1)
|
||||||
self.action = split_args[0]
|
self.action = split_args[0]
|
||||||
if len(split_args) > 1:
|
if len(split_args) > 1:
|
||||||
self.args = ''.join(split_args[1:])
|
self.args = "".join(split_args[1:])
|
||||||
else:
|
else:
|
||||||
self.args = ''
|
self.args = ""
|
||||||
else:
|
else:
|
||||||
self.action = "status"
|
self.action = "status"
|
||||||
self.args = ""
|
self.args = ""
|
||||||
|
|
||||||
self.err_msgs = ["|rInvalid Git Repository|n:",
|
self.err_msgs = [
|
||||||
|
"|rInvalid Git Repository|n:",
|
||||||
"The {repo_type} repository is not recognized as a git directory.",
|
"The {repo_type} repository is not recognized as a git directory.",
|
||||||
"In order to initialize it as a git directory, you will need to access your terminal and run the following commands from within your directory:",
|
"In order to initialize it as a git directory, you will need to access your terminal and run the following commands from within your directory:",
|
||||||
" git init",
|
" git init",
|
||||||
" git remote add origin {remote_link}"]
|
" git remote add origin {remote_link}",
|
||||||
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.repo = git.Repo(self.directory, search_parent_directories=True)
|
self.repo = git.Repo(self.directory, search_parent_directories=True)
|
||||||
except git.exc.InvalidGitRepositoryError:
|
except git.exc.InvalidGitRepositoryError:
|
||||||
err_msg = '\n'.join(self.err_msgs).format(repo_type=self.repo_type, remote_link=self.remote_link)
|
err_msg = "\n".join(self.err_msgs).format(
|
||||||
|
repo_type=self.repo_type, remote_link=self.remote_link
|
||||||
|
)
|
||||||
self.caller.msg(err_msg)
|
self.caller.msg(err_msg)
|
||||||
raise InterruptCommand
|
raise InterruptCommand
|
||||||
|
|
||||||
|
|
@ -62,8 +67,12 @@ class GitCommand(MuxCommand):
|
||||||
Retrieves the status of the active git repository, displaying unstaged changes/untracked files.
|
Retrieves the status of the active git repository, displaying unstaged changes/untracked files.
|
||||||
"""
|
"""
|
||||||
time_of_commit = datetime.datetime.fromtimestamp(self.commit.committed_date)
|
time_of_commit = datetime.datetime.fromtimestamp(self.commit.committed_date)
|
||||||
status_msg = '\n'.join([f"Branch: |w{self.branch}|n ({self.repo.git.rev_parse(self.commit.hexsha, short=True)}) ({time_of_commit})",
|
status_msg = "\n".join(
|
||||||
f"By {self.commit.author.email}: {self.commit.message}"])
|
[
|
||||||
|
f"Branch: |w{self.branch}|n ({self.repo.git.rev_parse(self.commit.hexsha, short=True)}) ({time_of_commit})",
|
||||||
|
f"By {self.commit.author.email}: {self.commit.message}",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
changedFiles = {item.a_path for item in self.repo.index.diff(None)}
|
changedFiles = {item.a_path for item in self.repo.index.diff(None)}
|
||||||
if changedFiles:
|
if changedFiles:
|
||||||
|
|
@ -77,7 +86,9 @@ class GitCommand(MuxCommand):
|
||||||
Display current and available branches.
|
Display current and available branches.
|
||||||
"""
|
"""
|
||||||
remote_refs = self.repo.remote().refs
|
remote_refs = self.repo.remote().refs
|
||||||
branch_msg = f"Current branch: |w{self.branch}|n. Branches available: {list_to_string(remote_refs)}"
|
branch_msg = (
|
||||||
|
f"Current branch: |w{self.branch}|n. Branches available: {list_to_string(remote_refs)}"
|
||||||
|
)
|
||||||
return branch_msg
|
return branch_msg
|
||||||
|
|
||||||
def checkout(self):
|
def checkout(self):
|
||||||
|
|
@ -85,7 +96,9 @@ class GitCommand(MuxCommand):
|
||||||
Check out a specific branch.
|
Check out a specific branch.
|
||||||
"""
|
"""
|
||||||
remote_refs = self.repo.remote().refs
|
remote_refs = self.repo.remote().refs
|
||||||
to_branch = self.args.strip().removeprefix('origin/') # Slightly hacky, but git tacks on the origin/
|
to_branch = self.args.strip().removeprefix(
|
||||||
|
"origin/"
|
||||||
|
) # Slightly hacky, but git tacks on the origin/
|
||||||
|
|
||||||
if to_branch not in remote_refs:
|
if to_branch not in remote_refs:
|
||||||
self.caller.msg(f"Branch '{to_branch}' not available.")
|
self.caller.msg(f"Branch '{to_branch}' not available.")
|
||||||
|
|
@ -116,7 +129,9 @@ class GitCommand(MuxCommand):
|
||||||
self.caller.msg("No new code to pull, no need to reset.\n")
|
self.caller.msg("No new code to pull, no need to reset.\n")
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
self.caller.msg(f"You have pulled new code. Server restart initiated.|/Head now at {self.repo.git.rev_parse(self.repo.head.commit.hexsha, short=True)}.|/Author: {self.repo.head.commit.author.name} ({self.repo.head.commit.author.email})|/{self.repo.head.commit.message.strip()}")
|
self.caller.msg(
|
||||||
|
f"You have pulled new code. Server restart initiated.|/Head now at {self.repo.git.rev_parse(self.repo.head.commit.hexsha, short=True)}.|/Author: {self.repo.head.commit.author.name} ({self.repo.head.commit.author.email})|/{self.repo.head.commit.message.strip()}"
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
@ -139,6 +154,7 @@ class GitCommand(MuxCommand):
|
||||||
caller.msg("You can only git status, git branch, git checkout, or git pull.")
|
caller.msg("You can only git status, git branch, git checkout, or git pull.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
class CmdGitEvennia(GitCommand):
|
class CmdGitEvennia(GitCommand):
|
||||||
"""
|
"""
|
||||||
Pull the latest code from the evennia core or checkout a different branch.
|
Pull the latest code from the evennia core or checkout a different branch.
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import git
|
||||||
import mock
|
import mock
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
class TestGitIntegration(EvenniaTest):
|
class TestGitIntegration(EvenniaTest):
|
||||||
@mock.patch("git.Repo")
|
@mock.patch("git.Repo")
|
||||||
@mock.patch("git.Git")
|
@mock.patch("git.Git")
|
||||||
|
|
@ -48,8 +49,12 @@ class TestGitIntegration(EvenniaTest):
|
||||||
|
|
||||||
def test_git_status(self):
|
def test_git_status(self):
|
||||||
time_of_commit = datetime.datetime.fromtimestamp(self.test_cmd_git.commit.committed_date)
|
time_of_commit = datetime.datetime.fromtimestamp(self.test_cmd_git.commit.committed_date)
|
||||||
status_msg = '\n'.join([f"Branch: |w{self.test_cmd_git.branch}|n ({self.test_cmd_git.repo.git.rev_parse(self.test_cmd_git.commit.hexsha, short=True)}) ({time_of_commit})",
|
status_msg = "\n".join(
|
||||||
f"By {self.test_cmd_git.commit.author.email}: {self.test_cmd_git.commit.message}"])
|
[
|
||||||
|
f"Branch: |w{self.test_cmd_git.branch}|n ({self.test_cmd_git.repo.git.rev_parse(self.test_cmd_git.commit.hexsha, short=True)}) ({time_of_commit})",
|
||||||
|
f"By {self.test_cmd_git.commit.author.email}: {self.test_cmd_git.commit.message}",
|
||||||
|
]
|
||||||
|
)
|
||||||
self.assertEqual(status_msg, self.test_cmd_git.get_status())
|
self.assertEqual(status_msg, self.test_cmd_git.get_status())
|
||||||
|
|
||||||
def test_git_branch(self):
|
def test_git_branch(self):
|
||||||
|
|
@ -65,5 +70,6 @@ class TestGitIntegration(EvenniaTest):
|
||||||
|
|
||||||
def test_git_pull(self):
|
def test_git_pull(self):
|
||||||
self.test_cmd_git.pull()
|
self.test_cmd_git.pull()
|
||||||
self.char1.msg.assert_called_with(f"You have pulled new code. Server restart initiated.|/Head now at {self.repo.git.rev_parse(self.repo.head.commit.hexsha, short=True)}.|/Author: {self.repo.head.commit.author.name} ({self.repo.head.commit.author.email})|/{self.repo.head.commit.message.strip()}")
|
self.char1.msg.assert_called_with(
|
||||||
|
f"You have pulled new code. Server restart initiated.|/Head now at {self.repo.git.rev_parse(self.repo.head.commit.hexsha, short=True)}.|/Author: {self.repo.head.commit.author.name} ({self.repo.head.commit.author.email})|/{self.repo.head.commit.message.strip()}"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -98,18 +98,17 @@ except ImportError:
|
||||||
"copy 'evennia/game_template/server/conf/web_plugins.py to mygame/server/conf."
|
"copy 'evennia/game_template/server/conf/web_plugins.py to mygame/server/conf."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Maintenance function - this is called repeatedly by the server
|
# Maintenance function - this is called repeatedly by the server
|
||||||
|
|
||||||
|
_IDMAPPER_CACHE_MAXSIZE = settings.IDMAPPER_CACHE_MAXSIZE
|
||||||
|
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
||||||
|
_LAST_SERVER_TIME_SNAPSHOT = 0
|
||||||
|
|
||||||
_MAINTENANCE_COUNT = 0
|
_MAINTENANCE_COUNT = 0
|
||||||
_FLUSH_CACHE = None
|
_FLUSH_CACHE = None
|
||||||
_IDMAPPER_CACHE_MAXSIZE = settings.IDMAPPER_CACHE_MAXSIZE
|
|
||||||
_GAMETIME_MODULE = None
|
_GAMETIME_MODULE = None
|
||||||
_DEFAULTOBJECT = None
|
_DEFAULTOBJECT = None
|
||||||
|
|
||||||
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
|
||||||
_LAST_SERVER_TIME_SNAPSHOT = 0
|
|
||||||
|
|
||||||
|
|
||||||
def _server_maintenance():
|
def _server_maintenance():
|
||||||
"""
|
"""
|
||||||
|
|
@ -120,9 +119,11 @@ def _server_maintenance():
|
||||||
global _LAST_SERVER_TIME_SNAPSHOT
|
global _LAST_SERVER_TIME_SNAPSHOT
|
||||||
global _OBJECTDB
|
global _OBJECTDB
|
||||||
|
|
||||||
if not _FLUSH_CACHE:
|
if not _OBJECTDB:
|
||||||
from evennia.objects.models import ObjectDB as _OBJECTDB
|
from evennia.objects.models import ObjectDB as _OBJECTDB
|
||||||
|
if not _GAMETIME_MODULE:
|
||||||
from evennia.utils import gametime as _GAMETIME_MODULE
|
from evennia.utils import gametime as _GAMETIME_MODULE
|
||||||
|
if not _FLUSH_CACHE:
|
||||||
from evennia.utils.idmapper.models import conditional_flush as _FLUSH_CACHE
|
from evennia.utils.idmapper.models import conditional_flush as _FLUSH_CACHE
|
||||||
|
|
||||||
_MAINTENANCE_COUNT += 1
|
_MAINTENANCE_COUNT += 1
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ from evennia.utils.utils import callables_from_module, class_from_module
|
||||||
|
|
||||||
SCRIPTDB = None
|
SCRIPTDB = None
|
||||||
|
|
||||||
|
|
||||||
class Container:
|
class Container:
|
||||||
"""
|
"""
|
||||||
Base container class. A container is simply a storage object whose
|
Base container class. A container is simply a storage object whose
|
||||||
|
|
@ -203,7 +204,9 @@ class GlobalScriptContainer(Container):
|
||||||
self.typeclass_storage = {}
|
self.typeclass_storage = {}
|
||||||
for key, data in list(self.loaded_data.items()):
|
for key, data in list(self.loaded_data.items()):
|
||||||
typeclass = data.get("typeclass", settings.BASE_SCRIPT_TYPECLASS)
|
typeclass = data.get("typeclass", settings.BASE_SCRIPT_TYPECLASS)
|
||||||
self.typeclass_storage[key] = class_from_module(typeclass, fallback=settings.BASE_SCRIPT_TYPECLASS)
|
self.typeclass_storage[key] = class_from_module(
|
||||||
|
typeclass, fallback=settings.BASE_SCRIPT_TYPECLASS
|
||||||
|
)
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -352,7 +352,7 @@ class FuncParser:
|
||||||
|
|
||||||
if curr_func:
|
if curr_func:
|
||||||
# we are starting a nested funcdef
|
# we are starting a nested funcdef
|
||||||
if len(callstack) > _MAX_NESTING:
|
if len(callstack) >= _MAX_NESTING - 1:
|
||||||
# stack full - ignore this function
|
# stack full - ignore this function
|
||||||
if raise_errors:
|
if raise_errors:
|
||||||
raise ParsingError(
|
raise ParsingError(
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,6 @@ dependencies.
|
||||||
|
|
||||||
from evennia import nonexistent_module, DefaultScript
|
from evennia import nonexistent_module, DefaultScript
|
||||||
|
|
||||||
|
|
||||||
class BrokenScript(DefaultScript):
|
class BrokenScript(DefaultScript):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,16 @@ from evennia import DefaultScript
|
||||||
|
|
||||||
_BASE_TYPECLASS = class_from_module(settings.BASE_SCRIPT_TYPECLASS)
|
_BASE_TYPECLASS = class_from_module(settings.BASE_SCRIPT_TYPECLASS)
|
||||||
|
|
||||||
|
|
||||||
class GoodScript(DefaultScript):
|
class GoodScript(DefaultScript):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class InvalidScript:
|
class InvalidScript:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class TestGlobalScriptContainer(unittest.TestCase):
|
|
||||||
|
|
||||||
|
class TestGlobalScriptContainer(unittest.TestCase):
|
||||||
def test_init_with_no_scripts(self):
|
def test_init_with_no_scripts(self):
|
||||||
gsc = containers.GlobalScriptContainer()
|
gsc = containers.GlobalScriptContainer()
|
||||||
|
|
||||||
|
|
@ -29,7 +31,7 @@ class TestGlobalScriptContainer(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(len(gsc.typeclass_storage), 0)
|
self.assertEqual(len(gsc.typeclass_storage), 0)
|
||||||
|
|
||||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {}})
|
@override_settings(GLOBAL_SCRIPTS={"script_name": {}})
|
||||||
def test_start_with_typeclassless_script(self):
|
def test_start_with_typeclassless_script(self):
|
||||||
"""No specified typeclass should fallback to base"""
|
"""No specified typeclass should fallback to base"""
|
||||||
gsc = containers.GlobalScriptContainer()
|
gsc = containers.GlobalScriptContainer()
|
||||||
|
|
@ -37,10 +39,14 @@ class TestGlobalScriptContainer(unittest.TestCase):
|
||||||
gsc.start()
|
gsc.start()
|
||||||
|
|
||||||
self.assertEqual(len(gsc.typeclass_storage), 1)
|
self.assertEqual(len(gsc.typeclass_storage), 1)
|
||||||
self.assertIn('script_name', gsc.typeclass_storage)
|
self.assertIn("script_name", gsc.typeclass_storage)
|
||||||
self.assertEqual(gsc.typeclass_storage['script_name'], _BASE_TYPECLASS)
|
self.assertEqual(gsc.typeclass_storage["script_name"], _BASE_TYPECLASS)
|
||||||
|
|
||||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.test_containers.NoScript'}})
|
@override_settings(
|
||||||
|
GLOBAL_SCRIPTS={
|
||||||
|
"script_name": {"typeclass": "evennia.utils.tests.test_containers.NoScript"}
|
||||||
|
}
|
||||||
|
)
|
||||||
def test_start_with_nonexistent_script(self):
|
def test_start_with_nonexistent_script(self):
|
||||||
"""Missing script class should fall back to base"""
|
"""Missing script class should fall back to base"""
|
||||||
gsc = containers.GlobalScriptContainer()
|
gsc = containers.GlobalScriptContainer()
|
||||||
|
|
@ -48,20 +54,28 @@ class TestGlobalScriptContainer(unittest.TestCase):
|
||||||
gsc.start()
|
gsc.start()
|
||||||
|
|
||||||
self.assertEqual(len(gsc.typeclass_storage), 1)
|
self.assertEqual(len(gsc.typeclass_storage), 1)
|
||||||
self.assertIn('script_name', gsc.typeclass_storage)
|
self.assertIn("script_name", gsc.typeclass_storage)
|
||||||
self.assertEqual(gsc.typeclass_storage['script_name'], _BASE_TYPECLASS)
|
self.assertEqual(gsc.typeclass_storage["script_name"], _BASE_TYPECLASS)
|
||||||
|
|
||||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.test_containers.GoodScript'}})
|
@override_settings(
|
||||||
|
GLOBAL_SCRIPTS={
|
||||||
|
"script_name": {"typeclass": "evennia.utils.tests.test_containers.GoodScript"}
|
||||||
|
}
|
||||||
|
)
|
||||||
def test_start_with_valid_script(self):
|
def test_start_with_valid_script(self):
|
||||||
gsc = containers.GlobalScriptContainer()
|
gsc = containers.GlobalScriptContainer()
|
||||||
|
|
||||||
gsc.start()
|
gsc.start()
|
||||||
|
|
||||||
self.assertEqual(len(gsc.typeclass_storage), 1)
|
self.assertEqual(len(gsc.typeclass_storage), 1)
|
||||||
self.assertIn('script_name', gsc.typeclass_storage)
|
self.assertIn("script_name", gsc.typeclass_storage)
|
||||||
self.assertEqual(gsc.typeclass_storage['script_name'], GoodScript)
|
self.assertEqual(gsc.typeclass_storage["script_name"], GoodScript)
|
||||||
|
|
||||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.test_containers.InvalidScript'}})
|
@override_settings(
|
||||||
|
GLOBAL_SCRIPTS={
|
||||||
|
"script_name": {"typeclass": "evennia.utils.tests.test_containers.InvalidScript"}
|
||||||
|
}
|
||||||
|
)
|
||||||
def test_start_with_invalid_script(self):
|
def test_start_with_invalid_script(self):
|
||||||
"""Script class doesn't implement required methods methods"""
|
"""Script class doesn't implement required methods methods"""
|
||||||
gsc = containers.GlobalScriptContainer()
|
gsc = containers.GlobalScriptContainer()
|
||||||
|
|
@ -69,9 +83,16 @@ class TestGlobalScriptContainer(unittest.TestCase):
|
||||||
with self.assertRaises(AttributeError) as err:
|
with self.assertRaises(AttributeError) as err:
|
||||||
gsc.start()
|
gsc.start()
|
||||||
# check for general attribute failure on the invalid class to preserve against future code-rder changes
|
# check for general attribute failure on the invalid class to preserve against future code-rder changes
|
||||||
self.assertTrue(str(err.exception).startswith("type object 'InvalidScript' has no attribute"), err.exception)
|
self.assertTrue(
|
||||||
|
str(err.exception).startswith("type object 'InvalidScript' has no attribute"),
|
||||||
|
err.exception,
|
||||||
|
)
|
||||||
|
|
||||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.data.broken_script.BrokenScript'}})
|
@override_settings(
|
||||||
|
GLOBAL_SCRIPTS={
|
||||||
|
"script_name": {"typeclass": "evennia.utils.tests.data.broken_script.BrokenScript"}
|
||||||
|
}
|
||||||
|
)
|
||||||
def test_start_with_broken_script(self):
|
def test_start_with_broken_script(self):
|
||||||
"""Un-importable script should traceback"""
|
"""Un-importable script should traceback"""
|
||||||
gsc = containers.GlobalScriptContainer()
|
gsc = containers.GlobalScriptContainer()
|
||||||
|
|
@ -79,4 +100,7 @@ class TestGlobalScriptContainer(unittest.TestCase):
|
||||||
with self.assertRaises(Exception) as err:
|
with self.assertRaises(Exception) as err:
|
||||||
gsc.start()
|
gsc.start()
|
||||||
# exception raised by imported module
|
# exception raised by imported module
|
||||||
self.assertTrue(str(err.exception).startswith("cannot import name 'nonexistent_module' from 'evennia'"), err.exception)
|
self.assertTrue(
|
||||||
|
str(err.exception).startswith("cannot import name 'nonexistent_module' from 'evennia'"),
|
||||||
|
err.exception,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -252,23 +252,35 @@ class TestFuncParser(TestCase):
|
||||||
with self.assertRaises(funcparser.ParsingError):
|
with self.assertRaises(funcparser.ParsingError):
|
||||||
self.parser.parse(unparseable, raise_errors=True)
|
self.parser.parse(unparseable, raise_errors=True)
|
||||||
|
|
||||||
@patch("evennia.utils.funcparser._MAX_NESTING", 2)
|
@parameterized.expand(
|
||||||
def test_parse_max_nesting(self):
|
[
|
||||||
|
# max_nest, cause error for 4 nested funcs?
|
||||||
|
(0, False),
|
||||||
|
(1, False),
|
||||||
|
(2, False),
|
||||||
|
(3, False),
|
||||||
|
(4, True),
|
||||||
|
(5, True),
|
||||||
|
(6, True),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_parse_max_nesting(self, max_nest, ok):
|
||||||
"""
|
"""
|
||||||
Make sure it is an error if the max nesting value is reached.
|
Make sure it is an error if the max nesting value is reached. We test
|
||||||
|
four nested functions against differnt MAX_NESTING values.
|
||||||
|
|
||||||
TODO: Does this make sense? When it sees the first function, len(callstack)
|
TODO: Does this make sense? When it sees the first function, len(callstack)
|
||||||
is 0. It doesn't raise until the stack length is greater than the
|
is 0. It doesn't raise until the stack length is greater than the
|
||||||
_MAX_NESTING value, which means you can nest 4 values with a value of
|
_MAX_NESTING value, which means you can nest 4 values with a value of
|
||||||
2, as demonstrated by this test.
|
2, as demonstrated by this test.
|
||||||
"""
|
"""
|
||||||
string = "$add(1, $add(1, $add(1, $toint(42))))"
|
string = "$add(1, $add(1, $add(1, $eval(42))))"
|
||||||
ret = self.parser.parse(string)
|
|
||||||
|
|
||||||
# TODO: Does this return value actually make sense?
|
|
||||||
# It removed the spaces from the calls.
|
|
||||||
self.assertEqual("$add(1,$add(1,$add(1,$toint(42))))", ret)
|
|
||||||
|
|
||||||
|
with patch("evennia.utils.funcparser._MAX_NESTING", max_nest):
|
||||||
|
if ok:
|
||||||
|
ret = self.parser.parse(string, raise_errors=True)
|
||||||
|
self.assertEqual(ret, "45")
|
||||||
|
else:
|
||||||
with self.assertRaises(funcparser.ParsingError):
|
with self.assertRaises(funcparser.ParsingError):
|
||||||
self.parser.parse(string, raise_errors=True)
|
self.parser.parse(string, raise_errors=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -353,12 +353,9 @@ class TestTextToHTMLparser(TestCase):
|
||||||
|
|
||||||
def test_non_url_with_www(self):
|
def test_non_url_with_www(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.parser.convert_urls('Awwww.this should not be highlighted'),
|
self.parser.convert_urls("Awwww.this should not be highlighted"),
|
||||||
'Awwww.this should not be highlighted'
|
"Awwww.this should not be highlighted",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalid_www_url(self):
|
def test_invalid_www_url(self):
|
||||||
self.assertEqual(
|
self.assertEqual(self.parser.convert_urls("www.t"), "www.t")
|
||||||
self.parser.convert_urls('www.t'),
|
|
||||||
'www.t'
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -90,8 +90,10 @@ class TextToHTMLparser(object):
|
||||||
re_url = re.compile(
|
re_url = re.compile(
|
||||||
r'(?<!=")(\b(?:ftp|www|https?)\W+(?:(?!\.(?:\s|$)|&\w+;)[^"\',;$*^\\(){}<>\[\]\s])+)(\.(?:\s|$)|&\w+;|)'
|
r'(?<!=")(\b(?:ftp|www|https?)\W+(?:(?!\.(?:\s|$)|&\w+;)[^"\',;$*^\\(){}<>\[\]\s])+)(\.(?:\s|$)|&\w+;|)'
|
||||||
)
|
)
|
||||||
re_protocol = re.compile(r'^(?:ftp|https?)://')
|
re_protocol = re.compile(r"^(?:ftp|https?)://")
|
||||||
re_valid_no_protocol = re.compile(r'^(?:www|ftp)\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b[-a-zA-Z0-9@:%_\+.~#?&//=]*')
|
re_valid_no_protocol = re.compile(
|
||||||
|
r"^(?:www|ftp)\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b[-a-zA-Z0-9@:%_\+.~#?&//=]*"
|
||||||
|
)
|
||||||
re_mxplink = re.compile(r"\|lc(.*?)\|lt(.*?)\|le", re.DOTALL)
|
re_mxplink = re.compile(r"\|lc(.*?)\|lt(.*?)\|le", re.DOTALL)
|
||||||
re_mxpurl = re.compile(r"\|lu(.*?)\|lt(.*?)\|le", re.DOTALL)
|
re_mxpurl = re.compile(r"\|lu(.*?)\|lt(.*?)\|le", re.DOTALL)
|
||||||
|
|
||||||
|
|
@ -162,7 +164,11 @@ class TextToHTMLparser(object):
|
||||||
rest = m.group(2)
|
rest = m.group(2)
|
||||||
# -> added target to output prevent the web browser from attempting to
|
# -> added target to output prevent the web browser from attempting to
|
||||||
# change pages (and losing our webclient session).
|
# change pages (and losing our webclient session).
|
||||||
return text[:m.start()] + f'<a href="{href}" target="_blank">{label}</a>{rest}' + text[m.end():]
|
return (
|
||||||
|
text[: m.start()]
|
||||||
|
+ f'<a href="{href}" target="_blank">{label}</a>{rest}'
|
||||||
|
+ text[m.end() :]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,10 +59,7 @@ PRONOUN_MAPPING = {
|
||||||
"neutral": "mine",
|
"neutral": "mine",
|
||||||
"plural": "ours",
|
"plural": "ours",
|
||||||
},
|
},
|
||||||
"reflexive pronoun": {
|
"reflexive pronoun": {"neutral": "myself", "plural": "ourselves"},
|
||||||
"neutral": "myself",
|
|
||||||
"plural": "ourselves"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"2nd person": {
|
"2nd person": {
|
||||||
"subject pronoun": {
|
"subject pronoun": {
|
||||||
|
|
@ -80,26 +77,16 @@ PRONOUN_MAPPING = {
|
||||||
"reflexive pronoun": {
|
"reflexive pronoun": {
|
||||||
"neutral": "yourself",
|
"neutral": "yourself",
|
||||||
"plural": "yourselves",
|
"plural": "yourselves",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"3rd person": {
|
"3rd person": {
|
||||||
"subject pronoun": {
|
"subject pronoun": {"male": "he", "female": "she", "neutral": "it", "plural": "they"},
|
||||||
"male": "he",
|
"object pronoun": {"male": "him", "female": "her", "neutral": "it", "plural": "them"},
|
||||||
"female": "she",
|
|
||||||
"neutral": "it",
|
|
||||||
"plural": "they"
|
|
||||||
},
|
|
||||||
"object pronoun": {
|
|
||||||
"male": "him",
|
|
||||||
"female": "her",
|
|
||||||
"neutral": "it",
|
|
||||||
"plural": "them"
|
|
||||||
},
|
|
||||||
"possessive adjective": {
|
"possessive adjective": {
|
||||||
"male": "his",
|
"male": "his",
|
||||||
"female": "her",
|
"female": "her",
|
||||||
"neutral": "its",
|
"neutral": "its",
|
||||||
"plural": "their"
|
"plural": "their",
|
||||||
},
|
},
|
||||||
"possessive pronoun": {
|
"possessive pronoun": {
|
||||||
"male": "his",
|
"male": "his",
|
||||||
|
|
@ -113,126 +100,45 @@ PRONOUN_MAPPING = {
|
||||||
"neutral": "itself",
|
"neutral": "itself",
|
||||||
"plural": "themselves",
|
"plural": "themselves",
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
PRONOUN_TABLE = {
|
PRONOUN_TABLE = {
|
||||||
"I": (
|
"I": ("1st person", ("neutral", "male", "female"), "subject pronoun"),
|
||||||
"1st person",
|
"me": ("1st person", ("neutral", "male", "female"), "object pronoun"),
|
||||||
("neutral", "male", "female"),
|
"my": ("1st person", ("neutral", "male", "female"), "possessive adjective"),
|
||||||
"subject pronoun"
|
"mine": ("1st person", ("neutral", "male", "female"), "possessive pronoun"),
|
||||||
),
|
"myself": ("1st person", ("neutral", "male", "female"), "reflexive pronoun"),
|
||||||
"me": (
|
"we": ("1st person", "plural", "subject pronoun"),
|
||||||
"1st person",
|
"us": ("1st person", "plural", "object pronoun"),
|
||||||
("neutral", "male", "female"),
|
"our": ("1st person", "plural", "possessive adjective"),
|
||||||
"object pronoun"
|
"ours": ("1st person", "plural", "possessive pronoun"),
|
||||||
),
|
"ourselves": ("1st person", "plural", "reflexive pronoun"),
|
||||||
"my": (
|
|
||||||
"1st person",
|
|
||||||
("neutral", "male", "female"),
|
|
||||||
"possessive adjective"
|
|
||||||
),
|
|
||||||
"mine": (
|
|
||||||
"1st person",
|
|
||||||
("neutral", "male", "female"),
|
|
||||||
"possessive pronoun"
|
|
||||||
),
|
|
||||||
"myself": (
|
|
||||||
"1st person",
|
|
||||||
("neutral", "male", "female"),
|
|
||||||
"reflexive pronoun"
|
|
||||||
),
|
|
||||||
|
|
||||||
"we": (
|
|
||||||
"1st person",
|
|
||||||
"plural",
|
|
||||||
"subject pronoun"
|
|
||||||
),
|
|
||||||
"us": (
|
|
||||||
"1st person",
|
|
||||||
"plural",
|
|
||||||
"object pronoun"
|
|
||||||
),
|
|
||||||
"our": (
|
|
||||||
"1st person",
|
|
||||||
"plural",
|
|
||||||
"possessive adjective"
|
|
||||||
),
|
|
||||||
"ours": (
|
|
||||||
"1st person",
|
|
||||||
"plural",
|
|
||||||
"possessive pronoun"
|
|
||||||
),
|
|
||||||
"ourselves": (
|
|
||||||
"1st person",
|
|
||||||
"plural",
|
|
||||||
"reflexive pronoun"
|
|
||||||
),
|
|
||||||
"you": (
|
"you": (
|
||||||
"2nd person",
|
"2nd person",
|
||||||
("neutral", "male", "female", "plural"),
|
("neutral", "male", "female", "plural"),
|
||||||
("subject pronoun", "object pronoun")
|
("subject pronoun", "object pronoun"),
|
||||||
),
|
|
||||||
"your": (
|
|
||||||
"2nd person",
|
|
||||||
("neutral", "male", "female", "plural"),
|
|
||||||
"possessive adjective"
|
|
||||||
),
|
|
||||||
"yours": (
|
|
||||||
"2nd person",
|
|
||||||
("neutral", "male", "female", "plural"),
|
|
||||||
"possessive pronoun"
|
|
||||||
),
|
|
||||||
"yourself": (
|
|
||||||
"2nd person",
|
|
||||||
("neutral", "male", "female"),
|
|
||||||
"reflexive pronoun"
|
|
||||||
),
|
|
||||||
"yourselves": (
|
|
||||||
"2nd person",
|
|
||||||
"plural",
|
|
||||||
"reflexive pronoun"
|
|
||||||
),
|
|
||||||
"he": (
|
|
||||||
"3rd person",
|
|
||||||
"male",
|
|
||||||
"subject pronoun"
|
|
||||||
),
|
|
||||||
"him": (
|
|
||||||
"3rd person",
|
|
||||||
"male",
|
|
||||||
"object pronoun"
|
|
||||||
),
|
),
|
||||||
|
"your": ("2nd person", ("neutral", "male", "female", "plural"), "possessive adjective"),
|
||||||
|
"yours": ("2nd person", ("neutral", "male", "female", "plural"), "possessive pronoun"),
|
||||||
|
"yourself": ("2nd person", ("neutral", "male", "female"), "reflexive pronoun"),
|
||||||
|
"yourselves": ("2nd person", "plural", "reflexive pronoun"),
|
||||||
|
"he": ("3rd person", "male", "subject pronoun"),
|
||||||
|
"him": ("3rd person", "male", "object pronoun"),
|
||||||
"his": (
|
"his": (
|
||||||
"3rd person",
|
"3rd person",
|
||||||
"male",
|
"male",
|
||||||
("possessive pronoun", "possessive adjective"),
|
("possessive pronoun", "possessive adjective"),
|
||||||
),
|
),
|
||||||
"himself": (
|
"himself": ("3rd person", "male", "reflexive pronoun"),
|
||||||
"3rd person",
|
"she": ("3rd person", "female", "subject pronoun"),
|
||||||
"male",
|
|
||||||
"reflexive pronoun"
|
|
||||||
),
|
|
||||||
"she": (
|
|
||||||
"3rd person",
|
|
||||||
"female",
|
|
||||||
"subject pronoun"
|
|
||||||
),
|
|
||||||
"her": (
|
"her": (
|
||||||
"3rd person",
|
"3rd person",
|
||||||
"female",
|
"female",
|
||||||
("object pronoun", "possessive adjective"),
|
("object pronoun", "possessive adjective"),
|
||||||
),
|
),
|
||||||
"hers": (
|
"hers": ("3rd person", "female", "possessive pronoun"),
|
||||||
"3rd person",
|
"herself": ("3rd person", "female", "reflexive pronoun"),
|
||||||
"female",
|
|
||||||
"possessive pronoun"
|
|
||||||
),
|
|
||||||
"herself": (
|
|
||||||
"3rd person",
|
|
||||||
"female",
|
|
||||||
"reflexive pronoun"
|
|
||||||
),
|
|
||||||
"it": (
|
"it": (
|
||||||
"3rd person",
|
"3rd person",
|
||||||
"neutral",
|
"neutral",
|
||||||
|
|
@ -243,36 +149,12 @@ PRONOUN_TABLE = {
|
||||||
"neutral",
|
"neutral",
|
||||||
("possessive pronoun", "possessive adjective"),
|
("possessive pronoun", "possessive adjective"),
|
||||||
),
|
),
|
||||||
"itself": (
|
"itself": ("3rd person", "neutral", "reflexive pronoun"),
|
||||||
"3rd person",
|
"they": ("3rd person", "plural", "subject pronoun"),
|
||||||
"neutral",
|
"them": ("3rd person", "plural", "object pronoun"),
|
||||||
"reflexive pronoun"
|
"their": ("3rd person", "plural", "possessive adjective"),
|
||||||
),
|
"theirs": ("3rd person", "plural", "possessive pronoun"),
|
||||||
"they": (
|
"themselves": ("3rd person", "plural", "reflexive pronoun"),
|
||||||
"3rd person",
|
|
||||||
"plural",
|
|
||||||
"subject pronoun"
|
|
||||||
),
|
|
||||||
"them": (
|
|
||||||
"3rd person",
|
|
||||||
"plural",
|
|
||||||
"object pronoun"
|
|
||||||
),
|
|
||||||
"their": (
|
|
||||||
"3rd person",
|
|
||||||
"plural",
|
|
||||||
"possessive adjective"
|
|
||||||
),
|
|
||||||
"theirs": (
|
|
||||||
"3rd person",
|
|
||||||
"plural",
|
|
||||||
"possessive pronoun"
|
|
||||||
),
|
|
||||||
"themselves": (
|
|
||||||
"3rd person",
|
|
||||||
"plural",
|
|
||||||
"reflexive pronoun"
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# define the default viewpoint conversions
|
# define the default viewpoint conversions
|
||||||
|
|
@ -304,7 +186,11 @@ ALIASES = {
|
||||||
|
|
||||||
|
|
||||||
def pronoun_to_viewpoints(
|
def pronoun_to_viewpoints(
|
||||||
pronoun, options=None, pronoun_type=DEFAULT_PRONOUN_TYPE, gender=DEFAULT_GENDER, viewpoint=DEFAULT_VIEWPOINT
|
pronoun,
|
||||||
|
options=None,
|
||||||
|
pronoun_type=DEFAULT_PRONOUN_TYPE,
|
||||||
|
gender=DEFAULT_GENDER,
|
||||||
|
viewpoint=DEFAULT_VIEWPOINT,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Access function for determining the forms of a pronount from different viewpoints.
|
Access function for determining the forms of a pronount from different viewpoints.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue