Ran formatting on code

This commit is contained in:
Griatch 2022-10-31 20:43:27 +01:00
parent 83154de19e
commit 353e4c0aa7
20 changed files with 296 additions and 327 deletions

View file

@ -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.')

View file

@ -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):

View file

@ -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"

View file

@ -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:

View file

@ -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

View file

@ -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"),
} }
@ -91,7 +91,7 @@ class Map(object):
""" """
self.start_time = time.time() self.start_time = time.time()
self.caller = caller self.caller = caller
self.max_width = int(size * 2 + 1) * 5 # This must be an odd number self.max_width = int(size * 2 + 1) * 5 # This must be an odd number
self.max_length = int(size * 2 + 1) * 3 # This must be an odd number self.max_length = int(size * 2 + 1) * 3 # This must be an odd number
self.has_mapped = {} self.has_mapped = {}
self.curX = None self.curX = None
@ -109,8 +109,8 @@ class Map(object):
board = [] board = []
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
@ -201,20 +203,20 @@ class Map(object):
continue continue
ex_character = _COMPASS_DIRECTIONS[ex_name][2] ex_character = _COMPASS_DIRECTIONS[ex_name][2]
delta_x = int(_COMPASS_DIRECTIONS[ex_name][1]/3) delta_x = int(_COMPASS_DIRECTIONS[ex_name][1] / 3)
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):

View file

@ -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")

View file

@ -1421,16 +1421,19 @@ 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": """
+ 0 1 + 0 1
@ -1439,24 +1442,25 @@ MAP_DATA = {
+ 0 1 + 0 1
""", """,
"zcoord": "map1", "zcoord": "map1",
"prototypes": { "prototypes": {
("*", "*"): { ("*", "*"): {
"key": "room", "key": "room",
"desc": "A room.", "desc": "A room.",
"prototype_parent": "xyz_room", "prototype_parent": "xyz_room",
}, },
("*", "*", "*"): { ("*", "*", "*"): {
"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):
super().setUp() super().setUp()
@ -1467,7 +1471,8 @@ class TestCallbacks(BaseEvenniaTest):
self.grid, err = xyzgrid.XYZGrid.create("testgrid") self.grid, err = xyzgrid.XYZGrid.create("testgrid")
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()]
)

View file

@ -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)

View file

@ -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,10 +67,14 @@ 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:
status_msg += f"Unstaged/uncommitted changes:|/ |g{'|/ '.join(changedFiles)}|n|/" status_msg += f"Unstaged/uncommitted changes:|/ |g{'|/ '.join(changedFiles)}|n|/"
if len(self.repo.untracked_files) > 0: if len(self.repo.untracked_files) > 0:
@ -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.

View file

@ -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()}"
)

View file

@ -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):
""" """

View file

@ -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

View file

@ -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,
)

View file

@ -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'
)

View file

@ -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)
@ -151,20 +153,24 @@ class TextToHTMLparser(object):
""" """
m = self.re_url.search(text) m = self.re_url.search(text)
if m: if m:
href = m.group(1) href = m.group(1)
label = href label = href
# if there is no protocol (i.e. starts with www or ftp) # if there is no protocol (i.e. starts with www or ftp)
# prefix with http:// so the link isn't treated as relative # prefix with http:// so the link isn't treated as relative
if not self.re_protocol.match(href): if not self.re_protocol.match(href):
if not self.re_valid_no_protocol.match(href): if not self.re_valid_no_protocol.match(href):
return text return text
href = "http://" + href href = "http://" + href
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
def sub_mxp_links(self, match): def sub_mxp_links(self, match):
""" """

View file

@ -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,166 +100,61 @@ 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": ( "your": ("2nd person", ("neutral", "male", "female", "plural"), "possessive adjective"),
"2nd person", "yours": ("2nd person", ("neutral", "male", "female", "plural"), "possessive pronoun"),
("neutral", "male", "female", "plural"), "yourself": ("2nd person", ("neutral", "male", "female"), "reflexive pronoun"),
"possessive adjective" "yourselves": ("2nd person", "plural", "reflexive pronoun"),
), "he": ("3rd person", "male", "subject pronoun"),
"yours": ( "him": ("3rd person", "male", "object pronoun"),
"2nd person", "his": (
("neutral", "male", "female", "plural"), "3rd person",
"possessive pronoun" "male",
), ("possessive pronoun", "possessive adjective"),
"yourself": ( ),
"2nd person", "himself": ("3rd person", "male", "reflexive pronoun"),
("neutral", "male", "female"), "she": ("3rd person", "female", "subject pronoun"),
"reflexive pronoun"
),
"yourselves": (
"2nd person",
"plural",
"reflexive pronoun"
),
"he": (
"3rd person",
"male",
"subject pronoun"
),
"him": (
"3rd person",
"male",
"object pronoun"
),
"his":(
"3rd person",
"male",
("possessive pronoun","possessive adjective"),
),
"himself": (
"3rd person",
"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",
("subject pronoun", "object pronoun"), ("subject pronoun", "object pronoun"),
), ),
"its": ( "its": (
"3rd person", "3rd person",
"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.
@ -395,9 +281,9 @@ def pronoun_to_viewpoints(
# special handling for the royal "we" # special handling for the royal "we"
if is_iter(source_gender): if is_iter(source_gender):
gender_opts = list(source_gender) gender_opts = list(source_gender)
else: else:
gender_opts = [source_gender] gender_opts = [source_gender]
if viewpoint == "1st person": if viewpoint == "1st person":
# make sure plural is always an option when converting to 1st person # make sure plural is always an option when converting to 1st person
# it doesn't matter if it's in the list twice, so don't bother checking # it doesn't matter if it's in the list twice, so don't bother checking

View file

@ -279,7 +279,7 @@ class TestPronounMapping(TestCase):
("you", "m", "you", "he"), ("you", "m", "you", "he"),
("you", "f op", "you", "her"), ("you", "f op", "you", "her"),
("I", "", "I", "it"), ("I", "", "I", "it"),
("I", "p", "I", "it"), # plural is invalid ("I", "p", "I", "it"), # plural is invalid
("I", "m", "I", "he"), ("I", "m", "I", "he"),
("Me", "n", "Me", "It"), ("Me", "n", "Me", "It"),
("your", "p", "your", "their"), ("your", "p", "your", "their"),