This commit is contained in:
Brenden Tuck 2020-10-26 18:06:33 -04:00
commit 782a74f036
271 changed files with 14731 additions and 5617 deletions

View file

@ -1 +1 @@
0.9.0
0.9.5

View file

@ -5,7 +5,7 @@ This is the main top-level API for Evennia. You can explore the evennia library
by accessing evennia.<subpackage> directly. From inside the game you can read
docs of all object by viewing its `__doc__` string, such as through
@py evennia.ObjectDB.__doc__
py evennia.ObjectDB.__doc__
For full functionality you should explore this module via a django-
aware shell. Go to your game directory and use the command
@ -20,27 +20,13 @@ See www.evennia.com for full documentation.
# docstring header
DOCSTRING = """
|cEvennia|n 'flat' API (use |wevennia.<component>.__doc__|n to read doc-strings
and |wdict(evennia.component)|n or
|wevennia.component.__dict__ to see contents)
|cTypeclass-bases:|n |cDatabase models|n:
DefaultAccount DefaultObject AccountDB ObjectDB
DefaultGuest DefaultCharacter ChannelDB
DefaultRoom ScriptDB
DefaultChannel DefaultExit Msg
DefaultScript
|cSearch functions:|n |cCommand parents and helpers:|n
search_account search_object default_cmds
search_script search_channel Command InterruptCommand
search_help search_message CmdSet
search_tag managers |cUtilities:|n
|cCreate functions:|n settings lockfuncs
create_account create_object logger gametime
create_script create_channel ansi spawn
create_help_entry create_message contrib managers
|cGlobal handlers:|n set_trace
TICKER_HANDLER TASK_HANDLER EvMenu EvTable
SESSION_HANDLER CHANNEL_HANDLER EvForm EvEditor """
Evennia MU* creation system.
Online manual and API docs are found at http://www.evennia.com.
Flat-API shortcut names:
{}
"""
# Delayed loading of properties
@ -248,10 +234,6 @@ def _init():
from .utils.containers import GLOBAL_SCRIPTS
from .utils.containers import OPTION_CLASSES
# initialize the doc string
global __doc__
__doc__ = ansi.parse_ansi(DOCSTRING)
# API containers
class _EvContainer(object):
@ -414,7 +396,7 @@ def _init():
GLOBAL_SCRIPTS.start()
def set_trace(term_size=(140, 40), debugger="auto"):
def set_trace(term_size=(140, 80), debugger="auto"):
"""
Helper function for running a debugger inside the Evennia event loop.
@ -461,9 +443,21 @@ def set_trace(term_size=(140, 40), debugger="auto"):
dbg = pdb.Pdb(stdout=sys.__stdout__)
try:
# Start debugger, forcing it up one stack frame (otherwise `set_trace` will start debugger
# this point, not the actual code location)
# Start debugger, forcing it up one stack frame (otherwise `set_trace`
# will start debugger this point, not the actual code location)
dbg.set_trace(sys._getframe().f_back)
except Exception:
# Stopped at breakpoint. Press 'n' to continue into the code.
dbg.set_trace()
# initialize the doc string
global __doc__
__doc__ = DOCSTRING.format(
"\n- "
+ "\n- ".join(
f"evennia.{key}"
for key in sorted(globals())
if not key.startswith("_") and key not in ("DOCSTRING",)
)
)

View file

@ -410,7 +410,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
"""
Checks if a given username or IP is banned.
Kwargs:
Keyword Args:
ip (str, optional): IP address.
username (str, optional): Username.
@ -481,7 +481,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
password (str): Password of account
ip (str, optional): IP address of client
Kwargs:
Keyword Args:
session (Session, optional): Session requesting authentication
Returns:
@ -611,7 +611,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
Args:
password (str): Password to validate
Kwargs:
Keyword Args:
account (DefaultAccount, optional): Account object to validate the
password for. Optional, but Django includes some validators to
do things like making sure users aren't setting passwords to the
@ -705,7 +705,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
with default (or overridden) permissions and having joined them to the
appropriate default channels.
Kwargs:
Keyword Args:
username (str): Username of Account owner
password (str): Password of Account owner
email (str, optional): Email address of Account owner
@ -872,7 +872,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
default send behavior for the current
MULTISESSION_MODE.
options (list): Protocol-specific options. Passed on to the protocol.
Kwargs:
Keyword Args:
any (dict): All other keywords are passed on to the protocol.
"""
@ -920,7 +920,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
session (Session, optional): The session to be responsible
for the command-send
Kwargs:
Keyword Args:
kwargs (any): Other keyword arguments will be added to the
found command object instance as variables before it
executes. This is unused by default Evennia but may be
@ -1036,7 +1036,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
no_superuser_bypass (bool, optional): Turn off superuser
lock bypassing. Be careful with this one.
Kwargs:
Keyword Args:
kwargs (any): Passed to the at_access hook along with the result.
Returns:
@ -1180,7 +1180,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
check.
access_type (str): The type of access checked.
Kwargs:
Keyword Args:
kwargs (any): These are passed on from the access check
and can be used to relay custom instructions from the
check mechanism.
@ -1385,7 +1385,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
text (str, optional): The message received.
from_obj (any, optional): The object sending the message.
Kwargs:
Keyword Args:
This includes any keywords sent to the `msg` method.
Returns:
@ -1407,7 +1407,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
text (str, optional): Text to send.
to_obj (any, optional): The object to send to.
Kwargs:
Keyword Args:
Keywords passed from msg()
Notes:
@ -1566,7 +1566,7 @@ class DefaultGuest(DefaultAccount):
"""
Gets or creates a Guest account object.
Kwargs:
Keyword Args:
ip (str, optional): IP address of requestor; used for ban checking,
throttling and logging

View file

@ -278,7 +278,7 @@ class IRCBot(Bot):
Args:
text (str, optional): Incoming text from channel.
Kwargs:
Keyword Args:
options (dict): Options dict with the following allowed keys:
- from_channel (str): dbid of a channel this text originated from.
- from_obj (list): list of objects sending this text.
@ -308,7 +308,7 @@ class IRCBot(Bot):
session (Session, optional): Session responsible for this
command. Note that this is the bot.
txt (str, optional): Command string.
Kwargs:
Keyword Args:
user (str): The name of the user who sent the message.
channel (str): The name of channel the message was sent to.
type (str): Nature of message. Either 'msg', 'action', 'nicklist'
@ -518,7 +518,7 @@ class GrapevineBot(Bot):
Args:
text (str, optional): Incoming text from channel.
Kwargs:
Keyword Args:
options (dict): Options dict with the following allowed keys:
- from_channel (str): dbid of a channel this text originated from.
- from_obj (list): list of objects sending this text.

View file

@ -553,7 +553,7 @@ def cmdhandler(
is made available as `self.cmdstring` when the Command runs.
If not given, the command will be assumed to be called as `cmdobj.key`.
Kwargs:
Keyword Args:
kwargs (any): other keyword arguments will be assigned as named variables on the
retrieved command object *before* it is executed. This is unused
in default Evennia but may be used by code to set custom flags or

View file

@ -358,14 +358,18 @@ class CmdSet(object, metaclass=_CmdSetMeta):
"""
perm = "perm" if self.permanent else "non-perm"
options = ", ".join([
"{}:{}".format(opt, "T" if getattr(self, opt) else "F")
for opt in ("no_exits", "no_objs", "no_channels", "duplicates")
if getattr(self, opt) is not None
])
options = ", ".join(
[
"{}:{}".format(opt, "T" if getattr(self, opt) else "F")
for opt in ("no_exits", "no_objs", "no_channels", "duplicates")
if getattr(self, opt) is not None
]
)
options = (", " + options) if options else ""
return f"<CmdSet {self.key}, {self.mergetype}, {perm}, prio {self.priority}{options}>: " + ", ".join(
[str(cmd) for cmd in sorted(self.commands, key=lambda o: o.key)])
return (
f"<CmdSet {self.key}, {self.mergetype}, {perm}, prio {self.priority}{options}>: "
+ ", ".join([str(cmd) for cmd in sorted(self.commands, key=lambda o: o.key)])
)
def __iter__(self):
"""

View file

@ -331,7 +331,7 @@ class CmdSetHandler(object):
if mergelist:
# current is a result of mergers
mergelist="+".join(mergelist)
mergelist = "+".join(mergelist)
strings.append(f" <Merged {mergelist}>: {self.current}")
else:
# current is a single cmdset

View file

@ -343,7 +343,7 @@ class Command(object, metaclass=CommandMeta):
session (Session, optional): Supply data only to a unique
session (ignores the value of `self.msg_all_sessions`).
Kwargs:
Keyword Args:
options (dict): Options to the protocol.
any (any): All other keywords are interpreted as th
name of send-instructions.
@ -369,7 +369,7 @@ class Command(object, metaclass=CommandMeta):
obj (Object or Account, optional): Object or Account on which to call the execute_cmd.
If not given, self.caller will be used.
Kwargs:
Keyword Args:
Other keyword arguments will be added to the found command
object instace as variables before it executes. This is
unused by default Evennia but may be used to set flags and
@ -510,6 +510,20 @@ Command {self} has no defined `func()` - showing on-command variables:
)[0]
return settings.CLIENT_DEFAULT_WIDTH
def client_height(self):
"""
Get the client screenheight for the session using this command.
Returns:
client height (int): The height (in characters) of the client window.
"""
if self.session:
return self.session.protocol_flags.get(
"SCREENHEIGHT", {0: settings.CLIENT_DEFAULT_HEIGHT}
)[0]
return settings.CLIENT_DEFAULT_HEIGHT
def styled_table(self, *args, **kwargs):
"""
Create an EvTable styled by on user preferences.
@ -517,7 +531,7 @@ Command {self} has no defined `func()` - showing on-command variables:
Args:
*args (str): Column headers. If not colored explicitly, these will get colors
from user options.
Kwargs:
Keyword Args:
any (str, int or dict): EvTable options, including, optionally a `table` dict
detailing the contents of the table.
Returns:
@ -570,7 +584,7 @@ Command {self} has no defined `func()` - showing on-command variables:
"""
Helper for formatting a string into a pretty display, for a header, separator or footer.
Kwargs:
Keyword Args:
header_text (str): Text to include in header.
fill_character (str): This single character will be used to fill the width of the
display.

View file

@ -2516,12 +2516,14 @@ class CmdExamine(ObjManipCommand):
def _format_options(cmdset):
"""helper for cmdset-option display"""
def _truefalse(string, value):
if value is None:
return ""
if value:
return f"{string}: T"
return f"{string}: F"
options = ", ".join(
_truefalse(opt, getattr(cmdset, opt))
for opt in ("no_exits", "no_objs", "no_channels", "duplicates")
@ -2538,7 +2540,8 @@ class CmdExamine(ObjManipCommand):
continue
options = _format_options(cmdset)
stored.append(
f"{cmdset.path} [{cmdset.key}] ({cmdset.mergetype}, prio {cmdset.priority}{options})")
f"{cmdset.path} [{cmdset.key}] ({cmdset.mergetype}, prio {cmdset.priority}{options})"
)
output["Stored Cmdset(s)"] = "\n " + "\n ".join(stored)
# this gets all components of the currently merged set
@ -2576,13 +2579,15 @@ class CmdExamine(ObjManipCommand):
# the resulting merged cmdset
options = _format_options(current_cmdset)
merged = [
f"<Current merged cmdset> ({current_cmdset.mergetype} prio {current_cmdset.priority}{options})"]
f"<Current merged cmdset> ({current_cmdset.mergetype} prio {current_cmdset.priority}{options})"
]
# the merge stack
for cmdset in all_cmdsets:
options = _format_options(cmdset)
merged.append(
f"{cmdset.path} [{cmdset.key}] ({cmdset.mergetype} prio {cmdset.priority}{options})")
f"{cmdset.path} [{cmdset.key}] ({cmdset.mergetype} prio {cmdset.priority}{options})"
)
output["Merged Cmdset(s)"] = "\n " + "\n ".join(merged)
# list the commands available to this object

View file

@ -833,7 +833,7 @@ class CmdPage(COMMAND_DEFAULT_CLASS):
receiver = f"|n,{clr}".join([obj.name for obj in page.receivers])
if sending:
template = to_template
sender = f"{sender} " if multi_send else ""
sender = f"{sender} " if multi_send else ""
receiver = f" {receiver}" if multi_recv else f" {receiver}"
else:
template = from_template
@ -848,7 +848,6 @@ class CmdPage(COMMAND_DEFAULT_CLASS):
receiver=receiver,
message=page.message,
)
)
lastpages = "\n ".join(listing)

View file

@ -379,10 +379,13 @@ class CmdInventory(COMMAND_DEFAULT_CLASS):
string = "You are not carrying anything."
else:
from evennia.utils.ansi import raw as raw_ansi
table = self.styled_table(border="header")
for item in items:
table.add_row(f"|C{item.name}|n",
"{}|n".format(utils.crop(raw_ansi(item.db.desc), width=50) or ""))
table.add_row(
f"|C{item.name}|n",
"{}|n".format(utils.crop(raw_ansi(item.db.desc), width=50) or ""),
)
string = f"|wYou are carrying:\n{table}"
self.caller.msg(string)

View file

@ -175,7 +175,7 @@ class CmdHelp(Command):
False: the command shouldn't appear in the table.
"""
return True
return cmd.access(caller, "view", default=True)
def parse(self):
"""
@ -222,8 +222,11 @@ class CmdHelp(Command):
# system, but not be displayed in the table, or be displayed differently.
for cmd in all_cmds:
if self.should_list_cmd(cmd, caller):
key = (cmd.auto_help_display_key
if hasattr(cmd, "auto_help_display_key") else cmd.key)
key = (
cmd.auto_help_display_key
if hasattr(cmd, "auto_help_display_key")
else cmd.key
)
hdict_cmd[cmd.help_category].append(key)
[hdict_topic[topic.help_category].append(topic.key) for topic in all_topics]
# report back
@ -271,10 +274,7 @@ class CmdHelp(Command):
cmd = match[0]
key = cmd.auto_help_display_key if hasattr(cmd, "auto_help_display_key") else cmd.key
formatted = self.format_help_entry(
key,
cmd.get_help(caller, cmdset),
aliases=cmd.aliases,
suggested=suggestions,
key, cmd.get_help(caller, cmdset), aliases=cmd.aliases, suggested=suggestions,
)
self.msg_help(formatted)
return
@ -294,10 +294,16 @@ class CmdHelp(Command):
# try to see if a category name was entered
if query in all_categories:
self.msg_help(
self.format_help_list({
query: [
cmd.auto_help_display_key if hasattr(cmd, "auto_help_display_key") else cmd.key
for cmd in all_cmds if cmd.help_category == query]},
self.format_help_list(
{
query: [
cmd.auto_help_display_key
if hasattr(cmd, "auto_help_display_key")
else cmd.key
for cmd in all_cmds
if cmd.help_category == query
]
},
{query: [topic.key for topic in all_topics if topic.help_category == query]},
)
)

View file

@ -971,8 +971,11 @@ class TestBuilding(CommandTest):
self.call(building.CmdSetHome(), "Obj = Room2", "Home location of Obj was set to Room")
def test_list_cmdsets(self):
self.call(building.CmdListCmdSets(), "",
"<CmdSetHandler> stack:\n <CmdSet DefaultCharacter, Union, perm, prio 0>:")
self.call(
building.CmdListCmdSets(),
"",
"<CmdSetHandler> stack:\n <CmdSet DefaultCharacter, Union, perm, prio 0>:",
)
self.call(building.CmdListCmdSets(), "NotFound", "Could not find 'NotFound'")
def test_typeclass(self):

View file

@ -294,7 +294,6 @@ You are not yet logged into the game. Commands available at this point:
|wquit|n - abort the connection
First create an account e.g. with |wcreate Anna c67jHL8p|n
(If you have spaces in your name, use double quotes: |wcreate "Anna the Barbarian" c67jHL8p|n
Next you can connect to the game: |wconnect Anna c67jHL8p|n
You can use the |wlook|n command if you want to see the connect screen again.

View file

@ -339,7 +339,7 @@ class TestOptionTransferTrue(TestCase):
b.no_objs = False
d.duplicates = False
# higher-prio sets will change the option up the chain
cmdset_f = d + c + b + a # reverse, high prio
cmdset_f = d + c + b + a # reverse, high prio
self.assertTrue(cmdset_f.no_exits)
self.assertTrue(cmdset_f.no_objs)
self.assertTrue(cmdset_f.no_channels)
@ -407,7 +407,7 @@ class TestOptionTransferTrue(TestCase):
c.priority = 1
d.priority = 2
c.no_exits = False
c.no_channels = None # passthrough
c.no_channels = None # passthrough
b.no_objs = False
d.duplicates = False
# higher-prio sets will change the option up the chain
@ -639,7 +639,7 @@ class TestOptionTransferFalse(TestCase):
b.no_objs = True
d.duplicates = True
# higher-prio sets will change the option up the chain
cmdset_f = d + c + b + a # reverse, high prio
cmdset_f = d + c + b + a # reverse, high prio
self.assertFalse(cmdset_f.no_exits)
self.assertFalse(cmdset_f.no_objs)
self.assertFalse(cmdset_f.no_channels)
@ -663,7 +663,7 @@ class TestOptionTransferFalse(TestCase):
b.no_objs = True
d.duplicates = True
# higher-prio sets will change the option up the chain
cmdset_f = a + b + c + d # forward, high prio, never happens
cmdset_f = a + b + c + d # forward, high prio, never happens
self.assertFalse(cmdset_f.no_exits)
self.assertFalse(cmdset_f.no_objs)
self.assertFalse(cmdset_f.no_channels)
@ -707,7 +707,7 @@ class TestOptionTransferFalse(TestCase):
c.priority = 1
d.priority = 2
c.no_exits = True
c.no_channels = None # passthrough
c.no_channels = None # passthrough
b.no_objs = True
d.duplicates = True
# higher-prio sets will change the option up the chain
@ -908,6 +908,7 @@ class TestOptionTransferReplace(TestCase):
"""
Test option transfer through more complex merge types.
"""
def setUp(self):
super().setUp()
self.cmdset_a = _CmdSetA()

View file

@ -255,7 +255,7 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase):
key (str): This must be unique.
account (Account): Account to attribute this object to.
Kwargs:
Keyword Args:
aliases (list of str): List of alternative (likely shorter) keynames.
description (str): A description of the channel, for use in listings.
locks (str): Lockstring.

View file

@ -21,7 +21,9 @@ class ObjectCreationTest(EvenniaTest):
class ChannelWholistTests(EvenniaTest):
def setUp(self):
super().setUp()
self.default_channel, _ = DefaultChannel.create("coffeetalk", description="A place to talk about coffee.")
self.default_channel, _ = DefaultChannel.create(
"coffeetalk", description="A place to talk about coffee."
)
self.default_channel.connect(self.obj1)
def test_wholist_shows_subscribed_objects(self):
@ -31,7 +33,9 @@ class ChannelWholistTests(EvenniaTest):
def test_wholist_shows_none_when_empty(self):
# No one hates dogs
empty_channel, _ = DefaultChannel.create("doghaters", description="A place where dog haters unite.")
empty_channel, _ = DefaultChannel.create(
"doghaters", description="A place where dog haters unite."
)
expected = "<None>"
result = empty_channel.wholist
self.assertEqual(expected, result)

View file

@ -175,7 +175,7 @@ def _call_or_get(value, menu=None, choice=None, string=None, obj=None, caller=No
Args:
value (any): the value to obtain. It might be a callable (see note).
Kwargs:
Keyword Args:
menu (BuildingMenu, optional): the building menu to pass to value
if it is a callable.
choice (Choice, optional): the choice to pass to value if a callable.

View file

@ -160,7 +160,7 @@ def get_worn_clothes(character, exclude_covered=False):
Args:
character (obj): The character to get a list of worn clothes from.
Kwargs:
Keyword Args:
exclude_covered (bool): If True, excludes clothes covered by other
clothing from the returned list.
@ -237,7 +237,7 @@ class Clothing(DefaultObject):
wearer (obj): character object wearing this clothing object
wearstyle (True or str): string describing the style of wear or True for none
Kwargs:
Keyword Args:
quiet (bool): If false, does not message the room
Notes:
@ -276,7 +276,7 @@ class Clothing(DefaultObject):
Args:
wearer (obj): character object wearing this clothing object
Kwargs:
Keyword Args:
quiet (bool): If false, does not message the room
"""
self.db.worn = False

View file

@ -95,7 +95,7 @@ def gametime_to_realtime(format=False, **kwargs):
in-game, you will be able to find the number of real-world seconds this
corresponds to (hint: Interval events deal with real life seconds).
Kwargs:
Keyword Args:
format (bool): Formatting the output.
days, month etc (int): These are the names of time units that must
match the `settings.TIME_UNITS` dict keys.
@ -131,7 +131,7 @@ def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0, weeks=0, months=0, yrs=0
interval would correspond to. This is usually a lot less
interesting than the other way around.
Kwargs:
Keyword Args:
times (int): The various components of the time.
format (bool): Formatting the output.

View file

@ -219,7 +219,7 @@ class BaseState(object):
"""
This is a convenience-wrapper for quickly building EvscapeRoom objects.
Kwargs:
Keyword Args:
typeclass (str): This can take just the class-name in the evscaperoom's
objects.py module. Otherwise, a full path or the actual class
is needed (for custom state objects, just give the class directly).

View file

@ -27,7 +27,7 @@ def create_evscaperoom_object(
Note that for the purpose of the Evscaperoom, we only allow one instance
of each *name*, deleting the old version if it already exists.
Kwargs:
Keyword Args:
typeclass (str): This can take just the class-name in the evscaperoom's
objects.py module. Otherwise, a full path is needed.
key (str): Name of object.
@ -69,7 +69,7 @@ def create_fantasy_word(length=5, capitalize=True):
"""
Create a random semi-pronouncable 'word'.
Kwargs:
Keyword Args:
length (int): The desired length of the 'word'.
capitalize (bool): If the return should be capitalized or not
Returns:

View file

@ -155,7 +155,7 @@ class CallbackHandler(object):
callback_name (str): the callback name to call.
*args: additional variables for this callback.
Kwargs:
Keyword Args:
number (int, optional): call just a specific callback.
parameters (str, optional): call a callback with parameters.
locals (dict, optional): a locals replacement.

View file

@ -28,7 +28,7 @@ def get(**kwargs):
"""
Return an object with the given search option or None if None is found.
Kwargs:
Keyword Args:
Any searchable data or property (id, db_key, db_location...).
Returns:

View file

@ -397,7 +397,7 @@ class EventHandler(DefaultScript):
callback_name (str): the callback name to call.
*args: additional variables for this callback.
Kwargs:
Keyword Args:
number (int, optional): call just a specific callback.
parameters (str, optional): call a callback with parameters.
locals (dict, optional): a locals replacement.

View file

@ -421,7 +421,7 @@ class EventCharacter(DefaultCharacter):
Args:
message (str): The suggested say/whisper text spoken by self.
Kwargs:
Keyword Args:
whisper (bool): If True, this is a whisper rather than
a say. This is sent by the whisper command by default.
Other verbal commands could use this hook in similar
@ -477,7 +477,7 @@ class EventCharacter(DefaultCharacter):
(by default only used by whispers).
msg_receiver(str, optional): Specific message for receiver only.
mapping (dict, optional): Additional mapping in messages.
Kwargs:
Keyword Args:
whisper (bool): If this is a whisper rather than a say. Kwargs
can be used by other verbal commands in a similar way.

View file

@ -283,7 +283,7 @@ def parse_language(speaker, emote):
the markers and a tuple (langname, saytext), where
langname can be None.
Raises:
LanguageError: If an invalid language was specified.
rplanguage.LanguageError: If an invalid language was specified.
Notes:
Note that no errors are raised if the wrong language identifier
@ -1420,7 +1420,7 @@ class ContribRPObject(DefaultObject):
looker (TypedObject): The object or account that is looking
at/getting inforamtion for this object.
Kwargs:
Keyword Args:
pose (bool): Include the pose (if available) in the return.
Returns:
@ -1508,7 +1508,7 @@ class ContribRPCharacter(DefaultCharacter, ContribRPObject):
looker (TypedObject): The object or account that is looking
at/getting inforamtion for this object.
Kwargs:
Keyword Args:
pose (bool): Include the pose (if available) in the return.
Returns:
@ -1557,7 +1557,7 @@ class ContribRPCharacter(DefaultCharacter, ContribRPObject):
Args:
message (str): The suggested say/whisper text spoken by self.
Kwargs:
Keyword Args:
whisper (bool): If True, this is a whisper rather than a say.
"""

View file

@ -70,7 +70,7 @@ class AuditedServerSession(ServerSession):
Extracts messages and system data from a Session object upon message
send or receive.
Kwargs:
Keyword Args:
src (str): Source of data; 'client' or 'server'. Indicates direction.
text (str or list): Client sends messages to server in the form of
lists. Server sends messages to client as string.
@ -216,7 +216,7 @@ class AuditedServerSession(ServerSession):
"""
Generic hook for sending data out through the protocol.
Kwargs:
Keyword Args:
kwargs (any): Other data to the protocol.
"""
@ -234,7 +234,7 @@ class AuditedServerSession(ServerSession):
"""
Hook for protocols to send incoming data to the engine.
Kwargs:
Keyword Args:
kwargs (any): Other data from the protocol.
"""

View file

@ -279,7 +279,7 @@ def spend_action(character, actions, action_name=None):
character (obj): Character spending the action
actions (int) or 'all': Number of actions to spend, or 'all' to spend all actions
Kwargs:
Keyword Args:
action_name (str or None): If a string is given, sets character's last action in
combat to provided string
"""

View file

@ -330,7 +330,7 @@ def spend_action(character, actions, action_name=None):
character (obj): Character spending the action
actions (int) or 'all': Number of actions to spend, or 'all' to spend all actions
Kwargs:
Keyword Args:
action_name (str or None): If a string is given, sets character's last action in
combat to provided string
"""

View file

@ -348,7 +348,7 @@ def spend_action(character, actions, action_name=None):
character (obj): Character spending the action
actions (int) or 'all': Number of actions to spend, or 'all' to spend all actions
Kwargs:
Keyword Args:
action_name (str or None): If a string is given, sets character's last action in
combat to provided string
"""

View file

@ -304,7 +304,7 @@ def spend_action(character, actions, action_name=None):
character (obj): Character spending the action
actions (int) or 'all': Number of actions to spend, or 'all' to spend all actions
Kwargs:
Keyword Args:
action_name (str or None): If a string is given, sets character's last action in
combat to provided string
"""

View file

@ -470,7 +470,7 @@ def spend_action(character, actions, action_name=None):
character (obj): Character spending the action
actions (int) or 'all': Number of actions to spend, or 'all' to spend all actions
Kwargs:
Keyword Args:
action_name (str or None): If a string is given, sets character's last action in
combat to provided string
"""
@ -643,7 +643,7 @@ class TBRangeTurnHandler(DefaultScript):
Args:
to_init (object): Object to initialize range field for.
Kwargs:
Keyword Args:
anchor_obj (object): Object to copy range values from, or None for a random object.
add_distance (int): Distance to put between to_init object and anchor object.

View file

@ -432,9 +432,8 @@ The web client starts out having two panes - the input-pane for entering command
and the main window.
- Use |y<Return>|n (or click the arrow on the right) to send your input.
- Use |yCtrl + <up/down-arrow>|n to step back and forth in your command-history.
- Use |yCtrl + <Return>|n to add a new line to your input without sending.
(Cmd instead of Ctrl-key on Macs)
- Use |yShift + <up/down-arrow>|n to step back and forth in your command-history.
- Use |yShift + <Return>|n to add a new line to your input without sending.
There is also some |wextra|n info to learn about customizing the webclient.

View file

@ -1158,7 +1158,8 @@ class WeaponRack(TutorialObject):
|wstab/thrust/pierce <target>|n - poke at the enemy. More damage but harder to hit.
|wslash/chop/bash <target>|n - swipe at the enemy. Less damage but easier to hit.
|wdefend/parry|n - protect yourself and make yourself harder to hit.)
""").strip()
"""
).strip()
self.db.no_more_weapons_msg = "you find nothing else of use."
self.db.available_weapons = ["knife", "dagger", "sword", "club"]

View file

@ -78,6 +78,7 @@ class CmdTutorial(Command):
helptext += "\n\n (Write 'give up' if you want to abandon your quest.)"
caller.msg(helptext)
# for the @detail command we inherit from MuxCommand, since
# we want to make use of MuxCommand's pre-parsing of '=' in the
# argument.
@ -202,22 +203,26 @@ class CmdTutorialLook(default_cmds.CmdLook):
looking_at_obj.at_desc(looker=caller)
return
class CmdTutorialGiveUp(default_cmds.MuxCommand):
"""
Give up the tutorial-world quest and return to Limbo, the start room of the
server.
"""
key = "give up"
aliases = ['abort']
aliases = ["abort"]
def func(self):
outro_room = OutroRoom.objects.all()
if outro_room:
outro_room = outro_room[0]
else:
self.caller.msg("That didn't work (seems like a bug). "
"Try to use the |wteleport|n command instead.")
self.caller.msg(
"That didn't work (seems like a bug). "
"Try to use the |wteleport|n command instead."
)
return
self.caller.move_to(outro_room)
@ -385,6 +390,7 @@ SUPERUSER_WARNING = (
#
# -------------------------------------------------------------
class CmdEvenniaIntro(Command):
"""
Start the Evennia intro wizard.
@ -393,10 +399,12 @@ class CmdEvenniaIntro(Command):
intro
"""
key = "intro"
def func(self):
from .intro_menu import init_menu
# quell also superusers
if self.caller.account:
self.caller.account.execute_cmd("quell")
@ -452,6 +460,7 @@ class IntroRoom(TutorialRoom):
character.account.execute_cmd("quell")
character.msg("(Auto-quelling while in tutorial-world)")
# -------------------------------------------------------------
#
# Bridge - unique room
@ -1165,4 +1174,3 @@ class OutroRoom(TutorialRoom):
def at_object_leave(self, character, destination):
if character.account:
character.account.execute_cmd("unquell")

View file

@ -98,7 +98,7 @@ class UnixCommandParser(argparse.ArgumentParser):
epilog (str): the epilog to show below options.
command (Command): the command calling the parser.
Kwargs:
Keyword Args:
Additional keyword arguments are directly sent to
`argparse.ArgumentParser`. You will find them on the
[parser's documentation](https://docs.python.org/2/library/argparse.html).

View file

@ -42,7 +42,7 @@ def at_search_result(matches, caller, query="", quiet=False, **kwargs):
quiet (bool, optional): If `True`, no messages will be echoed to caller
on errors.
Kwargs:
Keyword Args:
nofound_string (str): Replacement string to echo on a notfound error.
multimatch_string (str): Replacement string to echo on a multimatch error.

View file

@ -32,7 +32,6 @@ CONNECTION_SCREEN = """
If you need to create an account, type (without the <>'s):
|wcreate <username> <password>|n
If you have spaces in your username, enclose it in quotes.
Enter |whelp|n for more info. |wlook|n will re-show this screen.
|b==============================================================|n""".format(
settings.SERVERNAME, utils.get_evennia_version("short")

View file

@ -539,6 +539,8 @@ def objtag(accessing_obj, accessed_obj, *args, **kwargs):
Only true if accessed_obj has the specified tag and optional
category.
"""
if hasattr(accessed_obj, "obj"):
accessed_obj = accessed_obj.obj
tagkey = args[0] if args else None
category = args[1] if len(args) > 1 else None
return bool(accessed_obj.tags.get(tagkey, category=category))
@ -555,6 +557,8 @@ def inside(accessing_obj, accessed_obj, *args, **kwargs):
want also nested objects to pass the lock, use the `insiderecursive`
lockfunc.
"""
if hasattr(accessed_obj, "obj"):
accessed_obj = accessed_obj.obj
return accessing_obj.location == accessed_obj
@ -568,6 +572,9 @@ def inside_rec(accessing_obj, accessed_obj, *args, **kwargs):
in your inventory will also pass the lock).
"""
if hasattr(accessed_obj, "obj"):
accessed_obj = accessed_obj.obj
def _recursive_inside(obj, accessed_obj, lvl=1):
if obj.location:
if obj.location == accessed_obj:

View file

@ -333,7 +333,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
Args:
count (int): Number of objects of this type
looker (Object): Onlooker. Not used by default.
Kwargs:
Keyword Args:
key (str): Optional key to pluralize, if given, use this instead of the object's key.
Returns:
singular (str): The singular form to display.
@ -555,7 +555,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
session (Session, optional): Session to
return results to
Kwargs:
Keyword Args:
Other keyword arguments will be added to the found command
object instace as variables before it executes. This is
unused by default Evennia but may be used to set flags and
@ -601,7 +601,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
depends on the MULTISESSION_MODE.
options (dict, optional): Message-specific option-value
pairs. These will be applied at the protocol level.
Kwargs:
Keyword Args:
any (string or tuples): All kwarg keys not listed above
will be treated as send-command names and their arguments
(which can be a string or a tuple).
@ -652,7 +652,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
exclude (list, optional): A list of object not to call the
function on.
Kwargs:
Keyword Args:
Keyword arguments will be passed to the function for all objects.
"""
contents = self.contents
@ -684,7 +684,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
for every looker in contents that receives the
message. This allows for every object to potentially
get its own customized string.
Kwargs:
Keyword Args:
Keyword arguments will be passed on to `obj.msg()` for all
messaged objects.
@ -759,7 +759,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
(at_before/after_move etc) with quiet=True, this is as quiet a move
as can be done.
Kwargs:
Keyword Args:
Passed on to announce_move_to and announce_move_from hooks.
Returns:
@ -933,7 +933,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
key (str): Name of the new object.
account (Account): Account to attribute this object to.
Kwargs:
Keyword Args:
description (str): Brief description for this object.
ip (str): IP address of creator (for object auditing).
@ -1099,7 +1099,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
no_superuser_bypass (bool, optional): If `True`, don't skip
lock check for superuser (be careful with this one).
Kwargs:
Keyword Args:
Passed on to the at_access hook along with the result of the access check.
"""
@ -1258,7 +1258,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
place to do it. This is called also if the object currently
have no cmdsets.
Kwargs:
Keyword Args:
caller (Session, Object or Account): The caller requesting
this cmdset.
@ -1360,7 +1360,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
accessing_obj (Object or Account): The entity trying to gain access.
access_type (str): The type of access that was requested.
Kwargs:
Keyword Args:
Not used by default, added for possible expandability in a
game.
@ -1611,7 +1611,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
text (str, optional): The message received.
from_obj (any, optional): The object sending the message.
Kwargs:
Keyword Args:
This includes any keywords sent to the `msg` method.
Returns:
@ -1633,7 +1633,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
text (str, optional): Text to send.
to_obj (any, optional): The object to send to.
Kwargs:
Keyword Args:
Keywords passed from msg()
Notes:
@ -1869,7 +1869,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
Args:
message (str): The suggested say/whisper text spoken by self.
Kwargs:
Keyword Args:
whisper (bool): If True, this is a whisper rather than
a say. This is sent by the whisper command by default.
Other verbal commands could use this hook in similar
@ -1909,7 +1909,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
(by default only used by whispers).
msg_receivers(str): Specific message to pass to the receiver(s). This will parsed
with the {receiver} placeholder replaced with the given receiver.
Kwargs:
Keyword Args:
whisper (bool): If this is a whisper rather than a say. Kwargs
can be used by other verbal commands in a similar way.
mapping (dict): Pass an additional mapping to the message.
@ -1940,7 +1940,9 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
# whisper mode
msg_type = "whisper"
msg_self = (
'{self} whisper to {all_receivers}, "|n{speech}|n"' if msg_self is True else msg_self
'{self} whisper to {all_receivers}, "|n{speech}|n"'
if msg_self is True
else msg_self
)
msg_receivers = msg_receivers or '{object} whispers: "|n{speech}|n"'
msg_location = None
@ -2048,7 +2050,7 @@ class DefaultCharacter(DefaultObject):
If unset supplying None-- it will
change the default lockset and skip creator attribution.
Kwargs:
Keyword Args:
description (str): Brief description for this object.
ip (str): IP address of creator (for object auditing).
All other kwargs will be passed into the create_object call.
@ -2265,7 +2267,7 @@ class DefaultRoom(DefaultObject):
given, it will be given specific control/edit permissions to this
object (along with normal Admin perms). If not given, default
Kwargs:
Keyword Args:
description (str): Brief description for this object.
ip (str): IP address of creator (for object auditing).
@ -2468,7 +2470,7 @@ class DefaultExit(DefaultObject):
source (Room): The room to create this exit in.
dest (Room): The room to which this exit should go.
Kwargs:
Keyword Args:
description (str): Brief description for this object.
ip (str): IP address of creator (for object auditing).
@ -2560,7 +2562,7 @@ class DefaultExit(DefaultObject):
place to do it. This is called also if the object currently
has no cmdsets.
Kwargs:
Keyword Args:
force_init (bool): If `True`, force a re-build of the cmdset
(for example to update aliases).

View file

@ -130,7 +130,7 @@ def _set_property(caller, raw_string, **kwargs):
caller (Object, Account): The user of the wizard.
raw_string (str): Input from user on given node - the new value to set.
Kwargs:
Keyword Args:
test_parse (bool): If set (default True), parse raw_string for protfuncs and obj-refs and
try to run result through literal_eval. The parser will be run in 'testing' mode and any
parsing errors will shown to the user. Note that this is just for testing, the original
@ -297,7 +297,7 @@ def _format_list_actions(*args, **kwargs):
Args:
actions (str): Available actions. The first letter of the action name will be assumed
to be a shortcut.
Kwargs:
Keyword Args:
prefix (str): Default prefix to use.
Returns:
string (str): Formatted footer for adding to the node text.
@ -1175,7 +1175,7 @@ def _add_attr(caller, attr_string, **kwargs):
attr = value
attr;category = value
attr;category;lockstring = value
Kwargs:
Keyword Args:
delete (str): If this is set, attr_string is
considered the name of the attribute to delete and
no further parsing happens.
@ -1362,7 +1362,7 @@ def _add_tag(caller, tag_string, **kwargs):
tagname;category
tagname;category;data
Kwargs:
Keyword Args:
delete (str): If this is set, tag_string is considered
the name of the tag to delete.
@ -1911,7 +1911,7 @@ def _add_prototype_tag(caller, tag_string, **kwargs):
caller (Object): Caller of menu.
tag_string (str): Input from user - only tagname
Kwargs:
Keyword Args:
delete (str): If this is set, tag_string is considered
the name of the tag to delete.
@ -2139,7 +2139,7 @@ def _format_diff_text_and_options(diff, minimal=True, **kwargs):
diff (dict): A diff as produced by `prototype_diff`.
minimal (bool, optional): Don't show KEEPs.
Kwargs:
Keyword Args:
any (any): Forwarded into the generated options as arguments to the callable.
Returns:

View file

@ -330,7 +330,7 @@ def search_prototype(key=None, tags=None, require_single=False, return_iterators
"""
Find prototypes based on key and/or tags, or all prototypes.
Kwargs:
Keyword Args:
key (str): An exact or partial key to query for.
tags (str or list): Tag key or keys to query for. These
will always be applied with the 'db_protototype'
@ -730,7 +730,7 @@ def protfunc_parser(value, available_functions=None, testing=False, stacktrace=F
behave differently.
stacktrace (bool, optional): If set, print the stack parsing process of the protfunc-parser.
Kwargs:
Keyword Args:
session (Session): Passed to protfunc. Session of the entity spawning the prototype.
protototype (dict): Passed to protfunc. The dict this protfunc is a part of.
current_key(str): Passed to protfunc. The key in the prototype that will hold this value.

View file

@ -844,7 +844,7 @@ def spawn(*prototypes, **kwargs):
prototypes (str or dict): Each argument should either be a
prototype_key (will be used to find the prototype) or a full prototype
dictionary. These will be batched-spawned as one object each.
Kwargs:
Keyword Args:
prototype_modules (str or list): A python-path to a prototype
module, or a list of such paths. These will be used to build
the global protparents dictionary accessible by the input

View file

@ -125,7 +125,7 @@ class MonitorHandler(object):
persistent (bool, optional): If False, the monitor will survive
a server reload but not a cold restart. This is default.
Kwargs:
Keyword Args:
session (Session): If this keyword is given, the monitorhandler will
correctly analyze it and remove the monitor if after a reload/reboot
the session is no longer valid.

View file

@ -100,7 +100,7 @@ class TaskHandler(object):
callback (function or instance method): the callback itself
any (any): any additional positional arguments to send to the callback
Kwargs:
Keyword Args:
persistent (bool, optional): persist the task (store it).
any (any): additional keyword arguments to send to the callback

View file

@ -183,7 +183,7 @@ class Ticker(object):
store_key (str): Unique storage hash for this ticker subscription.
args (any, optional): Arguments to call the hook method with.
Kwargs:
Keyword Args:
_start_delay (int): If set, this will be
used to delay the start of the trigger instead of
`interval`.

View file

@ -57,7 +57,7 @@ class ConnectionWizard(object):
"""
Ask a yes/no question inline.
Kwargs:
Keyword Args:
prompt (str): The prompt to ask.
default (str): "yes" or "no", used if pressing return.
Returns:
@ -83,7 +83,7 @@ class ConnectionWizard(object):
"""
Ask multiple-choice question, get response inline.
Kwargs:
Keyword Args:
prompt (str): Input prompt.
options (list): List of options. Will be indexable by sequence number 1...
default (int): The list index+1 of the default choice, if any
@ -114,7 +114,7 @@ class ConnectionWizard(object):
"""
Get arbitrary input inline.
Kwargs:
Keyword Args:
prompt (str): The display prompt.
default (str): If empty input, use this.
validator (callable): If given, the input will be passed

View file

@ -1639,7 +1639,7 @@ def error_check_python_modules(show_warnings=False):
python source files themselves). Best they fail already here
before we get any further.
Kwargs:
Keyword Args:
show_warnings (bool): If non-fatal warning messages should be shown.
"""

View file

@ -164,7 +164,7 @@ def client_options(session, *args, **kwargs):
This allows the client an OOB way to inform us about its name and capabilities.
This will be integrated into the session settings
Kwargs:
Keyword Args:
get (bool): If this is true, return the settings as a dict
(ignore all other kwargs).
client (str): A client identifier, like "mushclient".
@ -282,7 +282,7 @@ def login(session, *args, **kwargs):
Peform a login. This only works if session is currently not logged
in. This will also automatically throttle too quick attempts.
Kwargs:
Keyword Args:
name (str): Account name
password (str): Plain-text password
@ -308,7 +308,7 @@ def get_value(session, *args, **kwargs):
Return the value of a given attribute or db_property on the
session's current account or character.
Kwargs:
Keyword Args:
name (str): Name of info value to return. Only names
in the _gettable dictionary earlier in this module
are accepted.
@ -325,7 +325,7 @@ def _testrepeat(**kwargs):
This is a test function for using with the repeat
inputfunc.
Kwargs:
Keyword Args:
session (Session): Session to return to.
"""
import time
@ -342,7 +342,7 @@ def repeat(session, *args, **kwargs):
this is meant as an example of limiting the number of
possible call functions.
Kwargs:
Keyword Args:
callback (str): The function to call. Only functions
from the _repeatable dictionary earlier in this
module are available.
@ -403,7 +403,7 @@ def monitor(session, *args, **kwargs):
"""
Adds monitoring to a given property or Attribute.
Kwargs:
Keyword Args:
name (str): The name of the property or Attribute
to report. No db_* prefix is needed. Only names
in the _monitorable dict earlier in this module
@ -485,8 +485,9 @@ def webclient_options(session, *args, **kwargs):
If kwargs is not empty, the key/values stored in there will be persisted
to the account object.
Kwargs:
Keyword Args:
<option name>: an option to save
"""
account = session.account

View file

@ -303,7 +303,7 @@ class GrapevineClient(WebSocketClientProtocol, Session):
"""
Send data grapevine -> Evennia
Kwargs:
Keyword Args:
data (dict): Converted json data.
"""

View file

@ -275,7 +275,7 @@ class IRCBot(irc.IRCClient, Session):
"""
Data IRC -> Server.
Kwargs:
Keyword Args:
text (str): Ingoing text.
kwargs (any): Other data from protocol.
@ -306,7 +306,7 @@ class IRCBot(irc.IRCClient, Session):
Args:
text (str): Outgoing text.
Kwargs:
Keyword Args:
user (str): the nick to send
privately to.
@ -375,7 +375,7 @@ class IRCBotFactory(protocol.ReconnectingClientFactory):
Args:
sessionhandler (SessionHandler): Reference to the main Sessionhandler.
Kwargs:
Keyword Args:
uid (int): Bot user id.
botname (str): Bot name (seen in IRC channel).
channel (str): IRC channel to connect to.

View file

@ -375,7 +375,7 @@ class PortalSessionHandler(SessionHandler):
Args:
session (PortalSession): Session receiving data.
Kwargs:
Keyword Args:
kwargs (any): Other data from protocol.
Notes:
@ -433,11 +433,11 @@ class PortalSessionHandler(SessionHandler):
Args:
session (Session): Session sending data.
Kwargs:
Keyword Args:
kwargs (any): Each key is a command instruction to the
protocol on the form key = [[args],{kwargs}]. This will
call a method send_<key> on the protocol. If no such
method exixts, it sends the data to a method send_default.
protocol on the form key = [[args],{kwargs}]. This will
call a method send_<key> on the protocol. If no such
method exixts, it sends the data to a method send_default.
"""
# from evennia.server.profiling.timetrace import timetrace # DEBUG

View file

@ -88,7 +88,7 @@ class RSSReader(Session):
"""
Data RSS -> Evennia.
Kwargs:
Keyword Args:
text (str): Incoming text
kwargs (any): Options from protocol.

View file

@ -263,7 +263,7 @@ class SshProtocol(Manhole, session.Session):
"""
Data Evennia -> User
Kwargs:
Keyword Args:
kwargs (any): Options to the protocol.
"""
@ -276,18 +276,19 @@ class SshProtocol(Manhole, session.Session):
Args:
text (str): The first argument is always the text string to send. No other arguments
are considered.
Kwargs:
options (dict): Send-option flags
- mxp: Enforce MXP link support.
- ansi: Enforce no ANSI colors.
- xterm256: Enforce xterm256 colors, regardless of TTYPE setting.
- nocolor: Strip all colors.
- raw: Pass string through without any ansi processing
(i.e. include Evennia ansi markers but do not
convert them into ansi tokens)
- echo: Turn on/off line echo on the client. Turn
off line echo for client, for example for password.
Note that it must be actively turned back on again!
Keyword Args:
options (dict): Send-option flags:
- mxp: Enforce MXP link support.
- ansi: Enforce no ANSI colors.
- xterm256: Enforce xterm256 colors, regardless of TTYPE setting.
- nocolor: Strip all colors.
- raw: Pass string through without any ansi processing
(i.e. include Evennia ansi markers but do not
convert them into ansi tokens)
- echo: Turn on/off line echo on the client. Turn
off line echo for client, for example for password.
Note that it must be actively turned back on again!
"""
# print "telnet.send_text", args,kwargs # DEBUG

View file

@ -360,7 +360,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
"""
Data User -> Evennia
Kwargs:
Keyword Args:
kwargs (any): Options from the protocol.
"""
@ -373,7 +373,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
"""
Data Evennia -> User
Kwargs:
Keyword Args:
kwargs (any): Options to the protocol
"""
self.sessionhandler.data_out(self, **kwargs)
@ -387,19 +387,20 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
Args:
text (str): The first argument is always the text string to send. No other arguments
are considered.
Kwargs:
options (dict): Send-option flags
- mxp: Enforce MXP link support.
- ansi: Enforce no ANSI colors.
- xterm256: Enforce xterm256 colors, regardless of TTYPE.
- noxterm256: Enforce no xterm256 color support, regardless of TTYPE.
- nocolor: Strip all Color, regardless of ansi/xterm256 setting.
- raw: Pass string through without any ansi processing
(i.e. include Evennia ansi markers but do not
convert them into ansi tokens)
- echo: Turn on/off line echo on the client. Turn
off line echo for client, for example for password.
Note that it must be actively turned back on again!
Keyword Args:
options (dict): Send-option flags:
- mxp: Enforce MXP link support.
- ansi: Enforce no ANSI colors.
- xterm256: Enforce xterm256 colors, regardless of TTYPE.
- noxterm256: Enforce no xterm256 color support, regardless of TTYPE.
- nocolor: Strip all Color, regardless of ansi/xterm256 setting.
- raw: Pass string through without any ansi processing
(i.e. include Evennia ansi markers but do not
convert them into ansi tokens)
- echo: Turn on/off line echo on the client. Turn
off line echo for client, for example for password.
Note that it must be actively turned back on again!
"""
text = args[0] if args else ""

View file

@ -11,8 +11,8 @@ instruct the client to play sounds or to update a graphical health
bar.
> Note that in Evennia's Web client, all send commands are "OOB commands",
(including the "text" one), there is no equivalence to MSDP/GMCP for the
webclient since it doesn't need it.
(including the "text" one), there is no equivalence to MSDP/GMCP for the
webclient since it doesn't need it.
This implements the following telnet OOB communication protocols:
@ -159,14 +159,15 @@ class TelnetOOB(object):
Notes:
The output of this encoding will be
MSDP structures on these forms:
```
[cmdname, [], {}] -> VAR cmdname VAL ""
[cmdname, [arg], {}] -> VAR cmdname VAL arg
[cmdname, [args],{}] -> VAR cmdname VAL ARRAYOPEN VAL arg VAL arg ... ARRAYCLOSE
[cmdname, [], {kwargs}] -> VAR cmdname VAL TABLEOPEN VAR key VAL val ... TABLECLOSE
[cmdname, [args], {kwargs}] -> VAR cmdname VAL ARRAYOPEN VAL arg VAL arg ... ARRAYCLOSE
VAR cmdname VAL TABLEOPEN VAR key VAL val ... TABLECLOSE
```
::
[cmdname, [], {}] -> VAR cmdname VAL ""
[cmdname, [arg], {}] -> VAR cmdname VAL arg
[cmdname, [args],{}] -> VAR cmdname VAL ARRAYOPEN VAL arg VAL arg ... ARRAYCLOSE
[cmdname, [], {kwargs}] -> VAR cmdname VAL TABLEOPEN VAR key VAL val ... TABLECLOSE
[cmdname, [args], {kwargs}] -> VAR cmdname VAL ARRAYOPEN VAL arg VAL arg ... ARRAYCLOSE
VAR cmdname VAL TABLEOPEN VAR key VAL val ... TABLECLOSE
Further nesting is not supported, so if an array argument consists
of an array (for example), that array will be json-converted to a
string.
@ -233,25 +234,24 @@ class TelnetOOB(object):
to have adopted). A cmdname without Package will end
up in the Core package, while Core package names will
be stripped on the Evennia side.
::
```
[cmd.name, [], {}] -> Cmd.Name
[cmd.name, [arg], {}] -> Cmd.Name arg
[cmd.name, [args],{}] -> Cmd.Name [args]
[cmd.name, [], {kwargs}] -> Cmd.Name {kwargs}
[cmdname, [args, {kwargs}] -> Core.Cmdname [[args],{kwargs}]
```
[cmd.name, [], {}] -> Cmd.Name
[cmd.name, [arg], {}] -> Cmd.Name arg
[cmd.name, [args],{}] -> Cmd.Name [args]
[cmd.name, [], {kwargs}] -> Cmd.Name {kwargs}
[cmdname, [args, {kwargs}] -> Core.Cmdname [[args],{kwargs}]
Notes:
There are also a few default mappings between evennia outputcmds and
GMCP:
```
client_options -> Core.Supports.Get
get_inputfuncs -> Core.Commands.Get
get_value -> Char.Value.Get
repeat -> Char.Repeat.Update
monitor -> Char.Monitor.Update
```
::
client_options -> Core.Supports.Get
get_inputfuncs -> Core.Commands.Get
get_value -> Char.Value.Get
repeat -> Char.Repeat.Update
monitor -> Char.Monitor.Update
"""
@ -287,14 +287,13 @@ class TelnetOOB(object):
Notes:
Clients should always send MSDP data on
one of the following forms:
::
```
cmdname '' -> [cmdname, [], {}]
cmdname val -> [cmdname, [val], {}]
cmdname array -> [cmdname, [array], {}]
cmdname table -> [cmdname, [], {table}]
cmdname array cmdname table -> [cmdname, [array], {table}]
```
cmdname '' -> [cmdname, [], {}]
cmdname val -> [cmdname, [val], {}]
cmdname array -> [cmdname, [array], {}]
cmdname table -> [cmdname, [], {table}]
cmdname array cmdname table -> [cmdname, [array], {table}]
Observe that all MSDP_VARS are used to identify cmdnames,
so if there are multiple arrays with the same cmdname
@ -388,14 +387,13 @@ class TelnetOOB(object):
We assume the structure is valid JSON.
The following is parsed into Evennia's formal structure:
::
```
Core.Name -> [name, [], {}]
Core.Name string -> [name, [string], {}]
Core.Name [arg, arg,...] -> [name, [args], {}]
Core.Name {key:arg, key:arg, ...} -> [name, [], {kwargs}]
Core.Name [[args], {kwargs}] -> [name, [args], {kwargs}]
```
Core.Name -> [name, [], {}]
Core.Name string -> [name, [string], {}]
Core.Name [arg, arg,...] -> [name, [args], {}]
Core.Name {key:arg, key:arg, ...} -> [name, [], {kwargs}]
Core.Name [[args], {kwargs}] -> [name, [args], {kwargs}]
"""
if isinstance(data, list):

View file

@ -119,6 +119,10 @@ class WebSocketClient(WebSocketServerProtocol, Session):
self.sessid = old_session.sessid
self.sessionhandler.disconnect(old_session)
self.protocol_flags["CLIENTNAME"] = "Evennia Webclient (websocket)"
self.protocol_flags["UTF-8"] = True
self.protocol_flags["OOB"] = True
# watch for dead links
self.transport.setTcpKeepAlive(1)
# actually do the connection
@ -242,7 +246,7 @@ class WebSocketClient(WebSocketServerProtocol, Session):
Args:
text (str): Text to send.
Kwargs:
Keyword Args:
options (dict): Options-dict with the following keys understood:
- raw (bool): No parsing at all (leave ansi-to-html markers unparsed).
- nocolor (bool): Clean out all color.
@ -297,7 +301,7 @@ class WebSocketClient(WebSocketServerProtocol, Session):
cmdname (str): The first argument will always be the oob cmd name.
*args (any): Remaining args will be arguments for `cmd`.
Kwargs:
Keyword Args:
options (dict): These are ignored for oob commands. Use command
arguments (which can hold dicts) to send instructions to the
client instead.

View file

@ -382,7 +382,7 @@ class AjaxWebClientSession(session.Session):
"""
Data User -> Evennia
Kwargs:
Keyword Args:
kwargs (any): Incoming data.
"""
@ -392,7 +392,7 @@ class AjaxWebClientSession(session.Session):
"""
Data Evennia -> User
Kwargs:
Keyword Args:
kwargs (any): Options to the protocol
"""
self.sessionhandler.data_out(self, **kwargs)
@ -405,7 +405,7 @@ class AjaxWebClientSession(session.Session):
Args:
text (str): Text to send.
Kwargs:
Keyword Args:
options (dict): Options-dict with the following keys understood:
- raw (bool): No parsing at all (leave ansi-to-html markers unparsed).
- nocolor (bool): Remove all color.
@ -457,7 +457,7 @@ class AjaxWebClientSession(session.Session):
cmdname (str): The first argument will always be the oob cmd name.
*args (any): Remaining args will be arguments for `cmd`.
Kwargs:
Keyword Args:
options (dict): These are ignored for oob commands. Use command
arguments (which can hold dicts) to send instructions to the
client instead.

View file

@ -399,17 +399,18 @@ class Evennia(object):
"""
Shuts down the server from inside it.
mode - sets the server restart mode.
'reload' - server restarts, no "persistent" scripts
are stopped, at_reload hooks called.
'reset' - server restarts, non-persistent scripts stopped,
at_shutdown hooks called but sessions will not
be disconnected.
'shutdown' - like reset, but server will not auto-restart.
_reactor_stopping - this is set if server is stopped by a kill
command OR this method was already called
once - in both cases the reactor is
dead/stopping already.
Keyword Args:
mode (str): Sets the server restart mode:
- 'reload': server restarts, no "persistent" scripts
are stopped, at_reload hooks called.
- 'reset' - server restarts, non-persistent scripts stopped,
at_shutdown hooks called but sessions will not
be disconnected.
-'shutdown' - like reset, but server will not auto-restart.
_reactor_stopping: This is set if server is stopped by a kill
command OR this method was already called
once - in both cases the reactor is dead/stopping already.
"""
if _reactor_stopping and hasattr(self, "shutdown_complete"):
# this means we have already passed through this method

View file

@ -343,8 +343,8 @@ class ServerSession(Session):
"""
Update the protocol_flags and sync them with Portal.
Kwargs:
key, value - A key:value pair to set in the
Keyword Args:
any: A key:value pair to set in the
protocol_flags dictionary.
Notes:
@ -361,7 +361,7 @@ class ServerSession(Session):
"""
Sending data from Evennia->Client
Kwargs:
Keyword Args:
text (str or tuple)
any (str or tuple): Send-commands identified
by their keys. Or "options", carrying options
@ -375,14 +375,15 @@ class ServerSession(Session):
Receiving data from the client, sending it off to
the respective inputfuncs.
Kwargs:
kwargs (any): Incoming data from protocol on
Keyword Args:
any: Incoming data from protocol on
the form `{"commandname": ((args), {kwargs}),...}`
Notes:
This method is here in order to give the user
a single place to catch and possibly process all incoming data from
the client. It should usually always end by sending
this data off to `self.sessionhandler.call_inputfuncs(self, **kwargs)`.
"""
self.sessionhandler.call_inputfuncs(self, **kwargs)
@ -392,9 +393,7 @@ class ServerSession(Session):
Args:
text (str): String input.
Kwargs:
any (str or tuple): Send-commands identified
kwargs (str or tuple): Send-commands identified
by their keys. Or "options", carrying options
for the protocol(s).
@ -421,7 +420,7 @@ class ServerSession(Session):
session (Session): This is here to make API consistent with
Account/Object.execute_cmd. If given, data is passed to
that Session, otherwise use self.
Kwargs:
Keyword Args:
Other keyword arguments will be added to the found command
object instace as variables before it executes. This is
unused by default Evennia but may be used to set flags and

View file

@ -177,7 +177,7 @@ class Session(object):
protocols can use this right away. Portal sessions
should overload this to format/handle the outgoing data as needed.
Kwargs:
Keyword Args:
kwargs (any): Other data to the protocol.
"""
@ -187,7 +187,7 @@ class Session(object):
"""
Hook for protocols to send incoming data to the engine.
Kwargs:
Keyword Args:
kwargs (any): Other data from the protocol.
"""

View file

@ -177,11 +177,13 @@ class SessionHandler(dict):
send-instruction, with the keyword itself being the name
of the instruction (like "text"). Suitable values for each
keyword are:
- arg -> [[arg], {}]
- [args] -> [[args], {}]
- {kwargs} -> [[], {kwargs}]
- [args, {kwargs}] -> [[arg], {kwargs}]
- [[args], {kwargs}] -> [[args], {kwargs}]
::
arg -> [[arg], {}]
[args] -> [[args], {}]
{kwargs} -> [[], {kwargs}]
[args, {kwargs}] -> [[arg], {kwargs}]
[[args], {kwargs}] -> [[args], {kwargs}]
Returns:
kwargs (dict): A cleaned dictionary of cmdname:[[args],{kwargs}] pairs,
@ -761,7 +763,7 @@ class ServerSessionHandler(SessionHandler):
Given a client identification hash (for session types that offer them)
return all sessions with a matching hash.
Args
Args:
csessid (str): The session hash.
Returns:
sessions (list): The sessions with matching .csessid, if any.
@ -827,14 +829,14 @@ class ServerSessionHandler(SessionHandler):
"""
Split incoming data into its inputfunc counterparts.
This should be called by the serversession.data_in
as sessionhandler.call_inputfunc(self, **kwargs).
as `sessionhandler.call_inputfunc(self, **kwargs)`.
We also intercept OOB communication here.
Args:
sessions (Session): Session.
Kwargs:
Keyword Args:
kwargs (any): Incoming data from protocol on
the form `{"commandname": ((args), {kwargs}),...}`

View file

@ -2,21 +2,24 @@
This module brings Django Signals into Evennia. These are events that
can be subscribed to by importing a given Signal and using the
following code.
::
THIS_SIGNAL.connect(callback, sender_object)
THIS_SIGNAL.connect(callback, sender_object`)
When other code calls THIS_SIGNAL.send(sender, **kwargs), the callback
When other code calls `THIS_SIGNAL.send(sender, **kwargs)`, the callback
will be triggered.
Callbacks must be in the following format:
::
def my_callback(sender, **kwargs):
...
def my_callback(sender, **kwargs):
...
This is used on top of hooks to make certain features easier to
add to contribs without necessitating a full takeover of hooks
that may be in high demand.
"""
from django.dispatch import Signal

View file

@ -22,7 +22,7 @@ class Throttle(object):
"""
Allows setting of throttle parameters.
Kwargs:
Keyword Args:
limit (int): Max number of failures before imposing limiter
timeout (int): number of timeout seconds after
max number of tries has been reached.

View file

@ -375,29 +375,41 @@ DUMMYRUNNER_SETTINGS_MODULE = "evennia.server.profiling.dummyrunner_settings"
# tuples mapping the exact tag (not a regex!) to the ANSI convertion, like
# `(r"%c%r", ansi.ANSI_RED)` (the evennia.utils.ansi module contains all
# ANSI escape sequences). Default is to use `|` and `|[` -prefixes.
# Note that to apply all color changes, a full `evennia reboot` is needed!
COLOR_ANSI_EXTRA_MAP = []
# Extend the available regexes for adding XTERM256 colors in-game. This is given
# as a list of regexes, where each regex must contain three anonymous groups for
# holding integers 0-5 for the red, green and blue components Default is
# is r'\|([0-5])([0-5])([0-5])', which allows e.g. |500 for red.
# Note that to apply all color changes, a full `evennia reboot` is needed!
COLOR_ANSI_EXTRA_MAP = []
# XTERM256 foreground color replacement
# Note that to apply all color changes, a full `evennia reboot` is needed!
COLOR_XTERM256_EXTRA_FG = []
# XTERM256 background color replacement. Default is \|\[([0-5])([0-5])([0-5])'
# Note that to apply all color changes, a full `evennia reboot` is needed!
COLOR_XTERM256_EXTRA_BG = []
# Extend the available regexes for adding XTERM256 grayscale values in-game. Given
# as a list of regexes, where each regex must contain one anonymous group containing
# a single letter a-z to mark the level from white to black. Default is r'\|=([a-z])',
# which allows e.g. |=k for a medium gray.
# XTERM256 grayscale foreground
# Note that to apply all color changes, a full `evennia reboot` is needed!
COLOR_XTERM256_EXTRA_GFG = []
# XTERM256 grayscale background. Default is \|\[=([a-z])'
# Note that to apply all color changes, a full `evennia reboot` is needed!
COLOR_XTERM256_EXTRA_GBG = []
# ANSI does not support bright backgrounds, so Evennia fakes this by mapping it to
# XTERM256 backgrounds where supported. This is a list of tuples that maps the wanted
# ansi tag (not a regex!) to a valid XTERM256 background tag, such as `(r'{[r', r'{[500')`.
# ansi tag (not a regex!) to a valid XTERM256 tag, such as `(r'|o', r'|531')`
# for orange. By default this is only used for bright backgrounds but
# both bright and dark colors can be mapped this way, and is a way to add
# new shortcuts to xterm colors without having to write the RGB value.
# Note that to apply all color changes, a full `evennia reboot` is needed!
COLOR_ANSI_XTERM256_BRIGHT_BG_EXTRA_MAP = []
# If set True, the above color settings *replace* the default |-style color markdown
# rather than extend it.
# Note that to apply all color changes, a full `evennia reboot` is needed!
COLOR_NO_DEFAULT = False

View file

@ -596,12 +596,13 @@ class AttributeHandler(object):
*args (tuple): Each argument should be a tuples (can be of varying
length) representing the Attribute to add to this object.
Supported tuples are
- `(key, value)`
- `(key, value, category)`
- `(key, value, category, lockstring)`
- `(key, value, category, lockstring, default_access)`
Kwargs:
Keyword Args:
strattr (bool): If `True`, value must be a string. This
will save the value without pickling which is less
flexible but faster to search (not often used except
@ -847,12 +848,12 @@ def initialize_nick_templates(in_template, out_template):
matched by the in_template.
Returns:
regex (regex): Regex to match against strings
template (str): Template with markers {arg1}, {arg2}, etc for
replacement using the standard .format method.
(regex, str): Regex to match against strings and a template
Template with markers `{arg1}`, `{arg2}`, etc for
replacement using the standard `.format` method.
Raises:
NickTemplateInvalid: If the in/out template does not have a matching
attributes.NickTemplateInvalid: If the in/out template does not have a matching
number of $args.
"""

View file

@ -35,11 +35,11 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
):
"""
Return Attribute objects by key, by category, by value, by
strvalue, by object (it is stored on) or with a combination of
`strvalue`, by object (it is stored on) or with a combination of
those criteria.
Attrs:
key (str, optional): The attribute's key to search for
Args:
key (str, optional): The attribute's key to search for.
category (str, optional): The category of the attribute(s)
to search for.
value (str, optional): The attribute value to search for.
@ -52,7 +52,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
precedence if given.
obj (Object, optional): On which object the Attribute to
search for is.
attrype (str, optional): An attribute-type to search for.
attrtype (str, optional): An attribute-type to search for.
By default this is either `None` (normal Attributes) or
`"nick"`.
kwargs (any): Currently unused. Reserved for future use.
@ -84,7 +84,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
"""
Get a nick, in parallel to `get_attribute`.
Attrs:
Args:
key (str, optional): The nicks's key to search for
category (str, optional): The category of the nicks(s) to search for.
value (str, optional): The attribute value to search for. Note that this
@ -170,7 +170,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
Return Tag objects by key, by category, by object (it is
stored on) or with a combination of those criteria.
Attrs:
Args:
key (str, optional): The Tag's key to search for
category (str, optional): The Tag of the attribute(s)
to search for.
@ -260,7 +260,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
this is either `None` (a normal Tag), `alias` or
`permission`. This always apply to all queried tags.
Kwargs:
Keyword Args:
match (str): "all" (default) or "any"; determines whether the
target object must be tagged with ALL of the provided
tags/categories or ANY single one. ANY will perform a weighted
@ -651,7 +651,7 @@ class TypeclassManager(TypedObjectManager):
Args:
args (any): These are passed on as arguments to the default
django get method.
Kwargs:
Keyword Args:
kwargs (any): These are passed on as normal arguments
to the default django get method
Returns:
@ -673,7 +673,7 @@ class TypeclassManager(TypedObjectManager):
Args:
args (any): These are passed on as arguments to the default
django filter method.
Kwargs:
Keyword Args:
kwargs (any): These are passed on as normal arguments
to the default django filter method.
Returns:
@ -796,7 +796,7 @@ class TypeclassManager(TypedObjectManager):
Variation of get that not only returns the current typeclass
but also all subclasses of that typeclass.
Kwargs:
Keyword Args:
kwargs (any): These are passed on as normal arguments
to the default django get method.
Returns:
@ -821,7 +821,7 @@ class TypeclassManager(TypedObjectManager):
Args:
args (any): These are passed on as arguments to the default
django filter method.
Kwargs:
Keyword Args:
kwargs (any): These are passed on as normal arguments
to the default django filter method.
Returns:

View file

@ -24,6 +24,8 @@ layer.
This module also contains the Managers for the respective models; inherit from
these to create custom managers.
----
"""
from django.db.models import signals
@ -161,7 +163,10 @@ class TypeclassBase(SharedMemoryModelBase):
class DbHolder(object):
"Holder for allowing property access of attributes"
"""
Holder for allowing property access of attributes.
"""
def __init__(self, obj, name, manager_name="attributes"):
_SA(self, name, _GA(obj, manager_name))
@ -309,10 +314,8 @@ class TypedObject(SharedMemoryModel):
than use the one in the model.
Args:
Passed through to parent.
Kwargs:
Passed through to parent.
*args: Passed through to parent.
**kwargs: Passed through to parent.
Notes:
The loading mechanism will attempt the following steps:
@ -635,7 +638,7 @@ class TypedObject(SharedMemoryModel):
no_superuser_bypass (bool, optional): Turn off the
superuser lock bypass (be careful with this one).
Kwargs:
Keyword Args:
kwargs (any): Ignored, but is there to make the api
consistent with the object-typeclass method access, which
use it to feed to its hook methods.
@ -725,14 +728,19 @@ class TypedObject(SharedMemoryModel):
def __db_get(self):
"""
Attribute handler wrapper. Allows for the syntax
::
obj.db.attrname = value
and
value = obj.db.attrname
and
del obj.db.attrname
and
all_attr = obj.db.all() (unless there is an attribute
named 'all', in which case that will be returned instead).
all_attr = obj.db.all()
(unless there is an attribute named 'all', in which case that will be
returned instead).
"""
try:
return self._db_holder
@ -742,14 +750,14 @@ class TypedObject(SharedMemoryModel):
# @db.setter
def __db_set(self, value):
"Stop accidentally replacing the db object"
"""Stop accidentally replacing the db object"""
string = "Cannot assign directly to db object! "
string += "Use db.attr=value instead."
raise Exception(string)
# @db.deleter
def __db_del(self):
"Stop accidental deletion."
"""Stop accidental deletion."""
raise Exception("Cannot delete the db object!")
db = property(__db_get, __db_set, __db_del)
@ -761,10 +769,23 @@ class TypedObject(SharedMemoryModel):
# @property ndb
def __ndb_get(self):
"""
A non-attr_obj store (ndb: NonDataBase). Everything stored
to this is guaranteed to be cleared when a server is shutdown.
Syntax is same as for the _get_db_holder() method and
property, e.g. obj.ndb.attr = value etc.
A non-attr_obj store (NonDataBase). Everything stored to this is
guaranteed to be cleared when a server is shutdown. Syntax is same as
for the `.db` property, e.g.
::
obj.ndb.attrname = value
and
value = obj.ndb.attrname
and
del obj.ndb.attrname
and
all_attr = obj.ndb.all()
What makes this preferable over just assigning properties directly on
the object is that Evennia can track caching for these properties and
for example avoid wiping objects with set `.ndb` data on cache flushes.
"""
try:
return self._ndb_holder
@ -869,28 +890,33 @@ class TypedObject(SharedMemoryModel):
@classmethod
def web_get_create_url(cls):
"""
Returns the URI path for a View that allows users to create new
instances of this object.
ex. Chargen = '/characters/create/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-create' would be referenced by this method.
ex.
url(r'characters/create/', ChargenView.as_view(), name='character-create')
If no View has been created and defined in urls.py, returns an
HTML anchor.
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can create new objects is the
developer's responsibility.
Returns:
path (str): URI path to object creation page, if defined.
Examples:
::
Chargen = '/characters/create/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-create' would be referenced by this method.
::
url(r'characters/create/', ChargenView.as_view(), name='character-create')
If no View has been created and defined in urls.py, returns an
HTML anchor.
Notes:
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can create new objects is the
developer's responsibility.
"""
try:
return reverse("%s-create" % slugify(cls._meta.verbose_name))
@ -902,26 +928,29 @@ class TypedObject(SharedMemoryModel):
Returns the URI path for a View that allows users to view details for
this object.
ex. Oscar (Character) = '/characters/oscar/1/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-detail' would be referenced by this method.
ex.
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$',
CharDetailView.as_view(), name='character-detail')
If no View has been created and defined in urls.py, returns an
HTML anchor.
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can view this object is the developer's
responsibility.
Returns:
path (str): URI path to object detail page, if defined.
Examples:
::
Oscar (Character) = '/characters/oscar/1/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-detail' would be referenced by this method.
::
CharDetailView.as_view(), name='character-detail')
If no View has been created and defined in urls.py, returns an
HTML anchor.
Notes:
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can view this object is the
developer's responsibility.
"""
try:
return reverse(
@ -936,26 +965,31 @@ class TypedObject(SharedMemoryModel):
Returns the URI path for a View that allows users to puppet a specific
object.
ex. Oscar (Character) = '/characters/oscar/1/puppet/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-puppet' would be referenced by this method.
ex.
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/puppet/$',
CharPuppetView.as_view(), name='character-puppet')
If no View has been created and defined in urls.py, returns an
HTML anchor.
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can view this object is the developer's
responsibility.
Returns:
path (str): URI path to object puppet page, if defined.
Examples:
::
Oscar (Character) = '/characters/oscar/1/puppet/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-puppet' would be referenced by this method.
::
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/puppet/$',
CharPuppetView.as_view(), name='character-puppet')
If no View has been created and defined in urls.py, returns an
HTML anchor.
Notes:
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can view this object is the developer's
responsibility.
"""
try:
return reverse(
@ -970,26 +1004,30 @@ class TypedObject(SharedMemoryModel):
Returns the URI path for a View that allows users to update this
object.
ex. Oscar (Character) = '/characters/oscar/1/change/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-update' would be referenced by this method.
ex.
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/change/$',
CharUpdateView.as_view(), name='character-update')
If no View has been created and defined in urls.py, returns an
HTML anchor.
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can modify objects is the developer's
responsibility.
Returns:
path (str): URI path to object update page, if defined.
Examples:
::
Oscar (Character) = '/characters/oscar/1/change/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-update' would be referenced by this method.
::
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/change/$',
CharUpdateView.as_view(), name='character-update')
If no View has been created and defined in urls.py, returns an
HTML anchor.
Notes:
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can modify objects is the developer's
responsibility.
"""
try:
return reverse(
@ -1003,26 +1041,31 @@ class TypedObject(SharedMemoryModel):
"""
Returns the URI path for a View that allows users to delete this object.
ex. Oscar (Character) = '/characters/oscar/1/delete/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-detail' would be referenced by this method.
ex.
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/delete/$',
CharDeleteView.as_view(), name='character-delete')
If no View has been created and defined in urls.py, returns an
HTML anchor.
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can delete this object is the developer's
responsibility.
Returns:
path (str): URI path to object deletion page, if defined.
Examples:
::
Oscar (Character) = '/characters/oscar/1/delete/'
For this to work, the developer must have defined a named view somewhere
in urls.py that follows the format 'modelname-action', so in this case
a named view of 'character-detail' would be referenced by this method.
::
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/delete/$',
CharDeleteView.as_view(), name='character-delete')
If no View has been created and defined in urls.py, returns an
HTML anchor.
Notes:
This method is naive and simply returns a path. Securing access to
the actual view and limiting who can delete this object is the developer's
responsibility.
"""
try:
return reverse(

View file

@ -5,9 +5,12 @@ Use the codes defined in ANSIPARSER in your text
to apply colour to text according to the ANSI standard.
Examples:
This is |rRed text|n and this is normal again.
Mostly you should not need to call parse_ansi() explicitly;
```python
"This is |rRed text|n and this is normal again."
```
Mostly you should not need to call `parse_ansi()` explicitly;
it is run by Evennia just before returning data to/from the
user. Depreciated example forms are available by extending
the ansi mapping.
@ -523,6 +526,13 @@ def raw(string):
return string.replace("{", "{{").replace("|", "||")
# ------------------------------------------------------------
#
# ANSIString - ANSI-aware string class
#
# ------------------------------------------------------------
def _spacing_preflight(func):
"""
This wrapper function is used to do some preflight checks on
@ -822,7 +832,7 @@ class ANSIString(str, metaclass=ANSIMeta):
"""
if not offset:
return []
return iterable
return [i + offset for i in iterable]
@classmethod
@ -893,9 +903,23 @@ class ANSIString(str, metaclass=ANSIMeta):
replayed.
"""
slice_indexes = self._char_indexes[slc]
char_indexes = self._char_indexes
slice_indexes = char_indexes[slc]
# If it's the end of the string, we need to append final color codes.
if not slice_indexes:
# if we find no characters it may be because we are just outside
# of the interval, using an open-ended slice. We must replay all
# of the escape characters until/after this point.
if char_indexes:
if slc.start is None and slc.stop is None:
# a [:] slice of only escape characters
return ANSIString(self._raw_string[slc])
if slc.start is None:
# this is a [:x] slice
return ANSIString(self._raw_string[:char_indexes[0]])
if slc.stop is None:
# a [x:] slice
return ANSIString(self._raw_string[char_indexes[-1] + 1:])
return ANSIString("")
try:
string = self[slc.start or 0]._raw_string
@ -915,7 +939,7 @@ class ANSIString(str, metaclass=ANSIMeta):
# raw_string not long enough
pass
if i is not None:
append_tail = self._get_interleving(self._char_indexes.index(i) + 1)
append_tail = self._get_interleving(char_indexes.index(i) + 1)
else:
append_tail = ""
return ANSIString(string + append_tail, decoded=True)
@ -982,13 +1006,11 @@ class ANSIString(str, metaclass=ANSIMeta):
sep (str): The separator to split the string on.
reverse (boolean): Whether to split the string on the last
occurrence of the separator rather than the first.
Returns:
result (tuple):
prefix (ANSIString): The part of the string before the
separator
sep (ANSIString): The separator itself
postfix (ANSIString): The part of the string after the
separator.
ANSIString: The part of the string before the separator
ANSIString: The separator itself
ANSIString: The part of the string after the separator.
"""
if hasattr(sep, "_clean_string"):
@ -1288,18 +1310,21 @@ class ANSIString(str, metaclass=ANSIMeta):
one.
NOTE: This should always be used for joining strings when ANSIStrings
are involved. Otherwise color information will be discarded by
python, due to details in the C implementation of strings.
are involved. Otherwise color information will be discarded by python,
due to details in the C implementation of strings.
Args:
iterable (list of strings): A list of strings to join together
Returns:
result (ANSIString): A single string with all of the iterable's
contents concatenated, with this string between each. For
example:
ANSIString(', ').join(['up', 'right', 'left', 'down'])
...Would return:
ANSIString('up, right, left, down')
ANSIString: A single string with all of the iterable's
contents concatenated, with this string between each.
Examples:
::
>>> ANSIString(', ').join(['up', 'right', 'left', 'down'])
ANSIString('up, right, left, down')
"""
result = ANSIString("")

View file

@ -31,7 +31,8 @@ etc. You also need to know Python and Evennia's API. Hence it's
recommended that the batch-code processor is limited only to
superusers or highly trusted staff.
Batch-command processor file syntax
Batch-Command processor file syntax
-----------------------------------
The batch-command processor accepts 'batchcommand files' e.g
`batch.ev`, containing a sequence of valid Evennia commands in a
@ -39,66 +40,61 @@ simple format. The engine runs each command in sequence, as if they
had been run at the game prompt.
Each Evennia command must be delimited by a line comment to mark its
end.
```
#INSERT path.batchcmdfile - this as the first entry on a line will
import and run a batch.ev file in this position, as if it was
written in this file.
```
This way entire game worlds can be created and planned offline; it is
end. This way entire game worlds can be created and planned offline; it is
especially useful in order to create long room descriptions where a
real offline text editor is often much better than any online text
editor or prompt.
There is only one batchcommand-specific entry to use in a batch-command
files (all others are just like in-game commands):
- `#INSERT path.batchcmdfile` - this as the first entry on a line will
import and run a batch.ev file in this position, as if it was
written in this file.
Example of batch.ev file:
----------------------------
::
```
# batch file
# all lines starting with # are comments; they also indicate
# that a command definition is over.
# batch file
# all lines starting with # are comments; they also indicate
# that a command definition is over.
@create box
@create box
# this comment ends the @create command.
# this comment ends the @create command.
@set box/desc = A large box.
@set box/desc = A large box.
Inside are some scattered piles of clothing.
Inside are some scattered piles of clothing.
It seems the bottom of the box is a bit loose.
It seems the bottom of the box is a bit loose.
# Again, this comment indicates the @set command is over. Note how
# the description could be freely added. Excess whitespace on a line
# is ignored. An empty line in the command definition is parsed as a \n
# (so two empty lines becomes a new paragraph).
# Again, this comment indicates the @set command is over. Note how
# the description could be freely added. Excess whitespace on a line
# is ignored. An empty line in the command definition is parsed as a \n
# (so two empty lines becomes a new paragraph).
@teleport #221
@teleport #221
# (Assuming #221 is a warehouse or something.)
# (remember, this comment ends the @teleport command! Don'f forget it)
# (Assuming #221 is a warehouse or something.)
# (remember, this comment ends the @teleport command! Don'f forget it)
# Example of importing another file at this point.
#IMPORT examples.batch
# Example of importing another file at this point.
#INSERT examples.batch
@drop box
@drop box
# Done, the box is in the warehouse! (this last comment is not necessary to
# close the @drop command since it's the end of the file)
```
# Done, the box is in the warehouse! (this last comment is not necessary to
# close the @drop command since it's the end of the file)
-------------------------
An example batch file is `contrib/examples/batch_example.ev`.
==========================================================================
Batch-code processor file syntax
Batch-Code processor file syntax
--------------------------------
The Batch-code processor accepts full python modules (e.g. `batch.py`)
that looks identical to normal Python files. The difference from
@ -113,62 +109,62 @@ the code and re-run sections of it easily during development.
Code blocks are marked by commented tokens alone on a line:
#HEADER - This denotes code that should be pasted at the top of all
other code. Multiple HEADER statements - regardless of where
it exists in the file - is the same as one big block.
Observe that changes to variables made in one block is not
preserved between blocks!
#CODE - This designates a code block that will be executed like a
stand-alone piece of code together with any HEADER(s)
defined. It is mainly used as a way to mark stop points for
the interactive mode of the batchprocessor. If no CODE block
is defined in the module, the entire module (including HEADERS)
is assumed to be a CODE block.
#INSERT path.filename - This imports another batch_code.py file and
runs it in the given position. The inserted file will retain
its own HEADERs which will not be mixed with the headers of
this file.
- `#HEADER` - This denotes code that should be pasted at the top of all
other code. Multiple HEADER statements - regardless of where
it exists in the file - is the same as one big block.
Observe that changes to variables made in one block is not
preserved between blocks!
- `#CODE` - This designates a code block that will be executed like a
stand-alone piece of code together with any HEADER(s)
defined. It is mainly used as a way to mark stop points for
the interactive mode of the batchprocessor. If no CODE block
is defined in the module, the entire module (including HEADERS)
is assumed to be a CODE block.
- `#INSERT path.filename` - This imports another batch_code.py file and
runs it in the given position. The inserted file will retain
its own HEADERs which will not be mixed with the headers of
this file.
Importing works as normal. The following variables are automatically
made available in the script namespace.
- `caller` - The object executing the batchscript
- `DEBUG` - This is a boolean marking if the batchprocessor is running
in debug mode. It can be checked to e.g. delete created objects
when running a CODE block multiple times during testing.
(avoids creating a slew of same-named db objects)
in debug mode. It can be checked to e.g. delete created objects
when running a CODE block multiple times during testing.
(avoids creating a slew of same-named db objects)
Example batch.py file:
::
Example batch.py file
-----------------------------------
#HEADER
```
#HEADER
from django.conf import settings
from evennia.utils import create
from types import basetypes
from django.conf import settings
from evennia.utils import create
from types import basetypes
GOLD = 10
GOLD = 10
#CODE
#CODE
obj = create.create_object(basetypes.Object)
obj2 = create.create_object(basetypes.Object)
obj.location = caller.location
obj.db.gold = GOLD
caller.msg("The object was created!")
obj = create.create_object(basetypes.Object)
obj2 = create.create_object(basetypes.Object)
obj.location = caller.location
obj.db.gold = GOLD
caller.msg("The object was created!")
if DEBUG:
obj.delete()
obj2.delete()
if DEBUG:
obj.delete()
obj2.delete()
#INSERT another_batch_file
#INSERT another_batch_file
#CODE
#CODE
script = create.create_script()
----
script = create.create_script()
```
"""
import re
import codecs
@ -206,7 +202,7 @@ def read_batchfile(pythonpath, file_ending=".py"):
file_ending (str): The file ending of this file (.ev or .py)
Returns:
text (str): The text content of the batch file.
str: The text content of the batch file.
Raises:
IOError: If problems reading file.
@ -255,19 +251,20 @@ class BatchCommandProcessor(object):
"""
This parses the lines of a batchfile according to the following
rules:
1) # at the beginning of a line marks the end of the command before
it. It is also a comment and any number of # can exist on
subsequent lines (but not inside comments).
2) #INSERT at the beginning of a line imports another
batch-cmd file file and pastes it into the batch file as if
it was written there.
3) Commands are placed alone at the beginning of a line and their
arguments are considered to be everything following (on any
number of lines) until the next comment line beginning with #.
4) Newlines are ignored in command definitions
5) A completely empty line in a command line definition is condered
a newline (so two empty lines is a paragraph).
6) Excess spaces and indents inside arguments are stripped.
1. `#` at the beginning of a line marks the end of the command before
it. It is also a comment and any number of # can exist on
subsequent lines (but not inside comments).
2. `#INSERT` at the beginning of a line imports another
batch-cmd file file and pastes it into the batch file as if
it was written there.
3. Commands are placed alone at the beginning of a line and their
arguments are considered to be everything following (on any
number of lines) until the next comment line beginning with #.
4. Newlines are ignored in command definitions
5. A completely empty line in a command line definition is condered
a newline (so two empty lines is a paragraph).
6. Excess spaces and indents inside arguments are stripped.
"""

View file

@ -78,7 +78,7 @@ def create_object(
Create a new in-game object.
Kwargs:
Keyword Args:
typeclass (class or str): Class or python path to a typeclass.
key (str): Name of the new object. If not set, a name of
#dbref will be set.
@ -96,7 +96,7 @@ def create_object(
location itself or during unittests.
attributes (list): Tuples on the form (key, value) or (key, value, category),
(key, value, lockstring) or (key, value, lockstring, default_access).
to set as Attributes on the new object.
to set as Attributes on the new object.
nattributes (list): Non-persistent tuples on the form (key, value). Note that
adding this rarely makes sense since this data will not survive a reload.
@ -206,7 +206,7 @@ def create_script(
scripts. It's behaviour is similar to the game objects except
scripts has a time component and are more limited in scope.
Kwargs:
Keyword Args:
typeclass (class or str): Class or python path to a typeclass.
key (str): Name of the new object. If not set, a name of
#dbref will be set.
@ -439,7 +439,7 @@ def create_channel(
Args:
key (str): This must be unique.
Kwargs:
Keyword Args:
aliases (list of str): List of alternative (likely shorter) keynames.
desc (str): A description of the channel, for use in listings.
locks (str): Lockstring.
@ -505,7 +505,7 @@ def create_account(
the empty string, will be set to None.
password (str): Password in cleartext.
Kwargs:
Keyword Args:
typeclass (str): The typeclass to use for the account.
is_superuser (bool): Wether or not this account is to be a superuser
locks (str): Lockstring.

View file

@ -604,7 +604,7 @@ def from_pickle(data, db_obj=None):
object was removed (or changed in-place) in the database, None will be
returned.
Args_
Args:
data (any): Pickled data to unpickle.
db_obj (Atribute, any): This is the model instance (normally
an Attribute) that _Saver*-type iterables (_SaverList etc)
@ -612,7 +612,7 @@ def from_pickle(data, db_obj=None):
that saves assigned data to the database. Skip if not
serializing onto a given object. If db_obj is given, this
function will convert lists, dicts and sets to their
_SaverList, _SaverDict and _SaverSet counterparts.
`_SaverList`, `_SaverDict` and `_SaverSet` counterparts.
Returns:
data (any): Unpickled data.

View file

@ -15,32 +15,34 @@ Features of the editor:
To use the editor, just import EvEditor from this module
and initialize it:
::
from evennia.utils.eveditor import EvEditor
EvEditor(caller, loadfunc=None, savefunc=None, quitfunc=None, key="", persistent=True)
- caller is the user of the editor, the one to see all feedback.
- loadfunc(caller) is called when the editor is first launched; the
return from this function is loaded as the starting buffer in the
editor.
- safefunc(caller, buffer) is called with the current buffer when
saving in the editor. The function should return True/False depending
on if the saving was successful or not.
- quitfunc(caller) is called when the editor exits. If this is given,
no automatic quit messages will be given.
- key is an optional identifier for the editing session, to be
displayed in the editor.
- persistent means the editor state will be saved to the database making it
survive a server reload. Note that using this mode, the load- save-
and quit-funcs must all be possible to pickle - notable unusable
callables are class methods and functions defined inside other
functions. With persistent=False, no such restriction exists.
- code set to True activates features on the EvEditor to enter Python code.
- `caller` is the user of the editor, the one to see all feedback.
- `loadfunc(caller)` is called when the editor is first launched; the
return from this function is loaded as the starting buffer in the
editor.
- `safefunc(caller, buffer)` is called with the current buffer when
saving in the editor. The function should return True/False depending
on if the saving was successful or not.
- `quitfunc(caller)` is called when the editor exits. If this is given,
no automatic quit messages will be given.
- `key` is an optional identifier for the editing session, to be
displayed in the editor.
- `persistent` means the editor state will be saved to the database making it
survive a server reload. Note that using this mode, the load- save-
and quit-funcs must all be possible to pickle - notable unusable
callables are class methods and functions defined inside other
functions. With persistent=False, no such restriction exists.
- `code` set to True activates features on the EvEditor to enter Python code.
In addition, the EvEditor can be used to enter Python source code,
and offers basic handling of indentation.
----
"""
import re
@ -229,14 +231,16 @@ class CmdEditorBase(Command):
"""
Handles pre-parsing
Editor commands are on the form
Usage:
:cmd [li] [w] [txt]
Where all arguments are optional.
li - line number (int), starting from 1. This could also
be a range given as <l>:<l>.
w - word(s) (string), could be encased in quotes.
txt - extra text (string), could be encased in quotes.
- li - line number (int), starting from 1. This could also
be a range given as <l>:<l>.
- w - word(s) (string), could be encased in quotes.
- txt - extra text (string), could be encased in quotes.
"""
editor = self.caller.ndb._eveditor

View file

@ -13,34 +13,32 @@ absolute size of the field and will be filled with an `evtable.EvCell`
object when displaying the form.
Example of input file `testform.py`:
::
```python
FORMCHAR = "x"
TABLECHAR = "c"
FORMCHAR = "x"
TABLECHAR = "c"
FORM = '''
.------------------------------------------------.
| |
| Name: xxxxx1xxxxx Player: xxxxxxx2xxxxxxx |
| xxxxxxxxxxx |
| |
>----------------------------------------------<
| |
| Desc: xxxxxxxxxxx STR: x4x DEX: x5x |
| xxxxx3xxxxx INT: x6x STA: x7x |
| xxxxxxxxxxx LUC: x8x MAG: x9x |
| |
>----------------------------------------------<
| | |
| cccccccc | ccccccccccccccccccccccccccccccccccc |
| cccccccc | ccccccccccccccccccccccccccccccccccc |
| cccAcccc | ccccccccccccccccccccccccccccccccccc |
| cccccccc | ccccccccccccccccccccccccccccccccccc |
| cccccccc | cccccccccccccccccBccccccccccccccccc |
| | |
-------------------------------------------------
'''
```
FORM = '''
.------------------------------------------------.
| |
| Name: xxxxx1xxxxx Player: xxxxxxx2xxxxxxx |
| xxxxxxxxxxx |
| |
>----------------------------------------------<
| |
| Desc: xxxxxxxxxxx STR: x4x DEX: x5x |
| xxxxx3xxxxx INT: x6x STA: x7x |
| xxxxxxxxxxx LUC: x8x MAG: x9x |
| |
>----------------------------------------------<
| | |
| cccccccc | ccccccccccccccccccccccccccccccccccc |
| cccccccc | ccccccccccccccccccccccccccccccccccc |
| cccAcccc | ccccccccccccccccccccccccccccccccccc |
| cccccccc | ccccccccccccccccccccccccccccccccccc |
| cccccccc | cccccccccccccccccBccccccccccccccccc |
| | |
-------------------------------------------------
The first line of the `FORM` string is ignored. The forms and table
markers must mark out complete, unbroken rectangles, each containing
@ -54,8 +52,8 @@ character's width.
Use as follows:
::
```python
from evennia import EvForm, EvTable
# create a new form from the template
@ -87,32 +85,32 @@ Use as follows:
"B": tableB})
print(form)
```
This produces the following result:
::
.------------------------------------------------.
| |
| Name: Tom the Player: Griatch |
| Bouncer |
| |
>----------------------------------------------<
| |
| Desc: A sturdy STR: 12 DEX: 10 |
| fellow INT: 5 STA: 18 |
| LUC: 10 MAG: 3 |
| |
>----------------------------------------------<
| | |
| HP|MV|MP | Skill |Value |Exp |
| ~~+~~+~~ | ~~~~~~~~~~~+~~~~~~~~~~~+~~~~~~~~~~~ |
| **|**|** | Shooting |12 |550/1200 |
| |**|* | Herbalism |14 |990/1400 |
| |* | | Smithing |9 |205/900 |
| | |
------------------------------------------------
```
.------------------------------------------------.
| |
| Name: Tom the Player: Griatch |
| Bouncer |
| |
>----------------------------------------------<
| |
| Desc: A sturdy STR: 12 DEX: 10 |
| fellow INT: 5 STA: 18 |
| LUC: 10 MAG: 3 |
| |
>----------------------------------------------<
| | |
| HP|MV|MP | Skill |Value |Exp |
| ~~+~~+~~ | ~~~~~~~~~~~+~~~~~~~~~~~+~~~~~~~~~~~ |
| **|**|** | Shooting |12 |550/1200 |
| |**|* | Herbalism |14 |990/1400 |
| |* | | Smithing |9 |205/900 |
| | |
------------------------------------------------
```
The marked forms have been replaced with EvCells of text and with
EvTables. The form can be updated by simply re-applying `form.map()`
@ -127,6 +125,8 @@ small for it). If you try to fit a table into an area it cannot fit
into (when including its borders and at least one line of text), the
form will raise an error.
----
"""
import re
@ -163,9 +163,12 @@ def _to_rect(lines):
def _to_ansi(obj, regexable=False):
"convert to ANSIString"
if isinstance(obj, str):
if isinstance(obj, ANSIString):
return obj
elif isinstance(obj, str):
# since ansi will be parsed twice (here and in the normal ansi send), we have to
# escape the |-structure twice.
# escape the |-structure twice. TODO: This is tied to the default color-tag syntax
# which is not ideal for those wanting to replace/extend it ...
obj = _ANSI_ESCAPE.sub(r"||||", obj)
if isinstance(obj, dict):
return dict((key, _to_ansi(value, regexable=regexable)) for key, value in obj.items())
@ -175,7 +178,7 @@ def _to_ansi(obj, regexable=False):
return ANSIString(obj, regexable=regexable)
class EvForm(object):
class EvForm:
"""
This object is instantiated with a text file and parses
it for rectangular form fields. It can then be fed a
@ -186,16 +189,15 @@ class EvForm(object):
def __init__(self, filename=None, cells=None, tables=None, form=None, **kwargs):
"""
Initiate the form
Initiate the form.
Kwargs:
Keyword Args:
filename (str): Path to template file.
cells (dict): A dictionary mapping of {id:text}
tables (dict): A dictionary mapping of {id:EvTable}.
form (dict): A dictionary of {"FORMCHAR":char,
"TABLECHAR":char,
"FORM":templatestring}
if this is given, filename is not read.
cells (dict): A dictionary mapping of `{id:text}`.
tables (dict): A dictionary mapping of `{id:EvTable}`.
form (dict): A dictionary of
`{"FORMCHAR":char, "TABLECHAR":char, "FORM":templatestring}`.
if this is given, filename is not read.
Notes:
Other kwargs are fed as options to the EvCells and EvTables
(see `evtable.EvCell` and `evtable.EvTable` for more info).
@ -461,40 +463,3 @@ class EvForm(object):
def __str__(self):
"Prints the form"
return str(ANSIString("\n").join([line for line in self.form]))
def _test():
"test evform. This is used by the unittest system."
form = EvForm("evennia.utils.tests.data.evform_example")
# add data to each tagged form cell
form.map(
cells={
"AA": "|gTom the Bouncer",
2: "|yGriatch",
3: "A sturdy fellow",
4: 12,
5: 10,
6: 5,
7: 18,
8: 10,
9: 3,
"F": "rev 1",
}
)
# create the EvTables
tableA = EvTable("HP", "MV", "MP", table=[["**"], ["*****"], ["***"]], border="incols")
tableB = EvTable(
"Skill",
"Value",
"Exp",
table=[
["Shooting", "Herbalism", "Smithing"],
[12, 14, 9],
["550/1200", "990/1400", "205/900"],
],
border="incols",
)
# add the tables to the proper ids in the form
form.map(tables={"A": tableA, "B": tableB})
return str(form)

View file

@ -1,12 +1,10 @@
"""
EvMenu
This implements a full menu system for Evennia.
The EvMenu is a full in-game menu system for Evennia.
To start the menu, just import the EvMenu class from this module.
Example usage:
```python
Example usage:
::
from evennia.utils.evmenu import EvMenu
@ -14,11 +12,10 @@ Example usage:
startnode="node1",
cmdset_mergetype="Replace", cmdset_priority=1,
auto_quit=True, cmd_on_exit="look", persistent=True)
```
Where `caller` is the Object to use the menu on - it will get a new
cmdset while using the Menu. The menu_module_path is the python path
to a python module containing function definitions. By adjusting the
cmdset while using the Menu. The `menu_module_path` is the python path
to a python module containing function definitions. By adjusting the
keyword options of the Menu() initialization call you can start the
menu at different places in the menu definition file, adjust if the
menu command should overload the normal commands or not, etc.
@ -32,8 +29,7 @@ no such restrictions exist.
The menu is defined in a module (this can be the same module as the
command definition too) with function definitions:
```python
::
def node1(caller):
# (this is the start node if called like above)
@ -47,14 +43,13 @@ command definition too) with function definitions:
def another_node(caller, input_string, **kwargs):
# code
return text, options
```
Where caller is the object using the menu and input_string is the
Where `caller` is the object using the menu and input_string is the
command entered by the user on the *previous* node (the command
entered to get to this node). The node function code will only be
executed once per node-visit and the system will accept nodes with
both one or two arguments interchangeably. It also accepts nodes
that takes **kwargs.
that takes `**kwargs`.
The menu tree itself is available on the caller as
`caller.ndb._evmenu`. This makes it a convenient place to store
@ -65,43 +60,43 @@ The return values must be given in the above order, but each can be
returned as None as well. If the options are returned as None, the
menu is immediately exited and the default "look" command is called.
text (str, tuple or None): Text shown at this node. If a tuple, the
second element in the tuple is a help text to display at this
node when the user enters the menu help command there.
options (tuple, dict or None): If `None`, this exits the menu.
If a single dict, this is a single-option node. If a tuple,
it should be a tuple of option dictionaries. Option dicts have
the following keys:
- `key` (str or tuple, optional): What to enter to choose this option.
If a tuple, it must be a tuple of strings, where the first string is the
key which will be shown to the user and the others are aliases.
If unset, the options' number will be used. The special key `_default`
marks this option as the default fallback when no other option matches
the user input. There can only be one `_default` option per node. It
will not be displayed in the list.
- `desc` (str, optional): This describes what choosing the option will do.
- `goto` (str, tuple or callable): If string, should be the name of node to go to
when this option is selected. If a callable, it has the signature
`callable(caller[,raw_input][,**kwargs]). If a tuple, the first element
is the callable and the second is a dict with the **kwargs to pass to
the callable. Those kwargs will also be passed into the next node if possible.
Such a callable should return either a str or a (str, dict), where the
string is the name of the next node to go to and the dict is the new,
(possibly modified) kwarg to pass into the next node. If the callable returns
None or the empty string, the current node will be revisited.
- `exec` (str, callable or tuple, optional): This takes the same input as `goto` above
and runs before it. If given a node name, the node will be executed but will not
be considered the next node. If node/callback returns str or (str, dict), these will
replace the `goto` step (`goto` callbacks will not fire), with the string being the
next node name and the optional dict acting as the kwargs-input for the next node.
If an exec callable returns the empty string (only), the current node is re-run.
- `text` (str, tuple or None): Text shown at this node. If a tuple, the
second element in the tuple is a help text to display at this
node when the user enters the menu help command there.
- `options` (tuple, dict or None): If `None`, this exits the menu.
If a single dict, this is a single-option node. If a tuple,
it should be a tuple of option dictionaries. Option dicts have
the following keys:
- `key` (str or tuple, optional): What to enter to choose this option.
If a tuple, it must be a tuple of strings, where the first string is the
key which will be shown to the user and the others are aliases.
If unset, the options' number will be used. The special key `_default`
marks this option as the default fallback when no other option matches
the user input. There can only be one `_default` option per node. It
will not be displayed in the list.
- `desc` (str, optional): This describes what choosing the option will do.
- `goto` (str, tuple or callable): If string, should be the name of node to go to
when this option is selected. If a callable, it has the signature
`callable(caller[,raw_input][,**kwargs])`. If a tuple, the first element
is the callable and the second is a dict with the kwargs to pass to
the callable. Those kwargs will also be passed into the next node if possible.
Such a callable should return either a str or a (str, dict), where the
string is the name of the next node to go to and the dict is the new,
(possibly modified) kwarg to pass into the next node. If the callable returns
None or the empty string, the current node will be revisited.
- `exec` (str, callable or tuple, optional): This takes the same input as `goto` above
and runs before it. If given a node name, the node will be executed but will not
be considered the next node. If node/callback returns str or (str, dict), these will
replace the `goto` step (`goto` callbacks will not fire), with the string being the
next node name and the optional dict acting as the kwargs-input for the next node.
If an exec callable returns `None`, the current node is re-run.
If key is not given, the option will automatically be identified by
its number 1..N.
Example:
```python
::
# in menu_module.py
@ -137,10 +132,9 @@ Example:
text = "This ends the menu since there are no options."
return text, None
```
When starting this menu with `Menu(caller, "path.to.menu_module")`,
the first node will look something like this:
::
This is a node text
______________________________________
@ -257,10 +251,11 @@ strings is only needed if wanting to pass strippable spaces, otherwise the
key:values will be converted to strings/numbers with literal_eval before passed
into the callable.
The `> ` option takes a glob or regex to perform different actions depending on user
The "> " option takes a glob or regex to perform different actions depending on user
input. Make sure to sort these in increasing order of generality since they
will be tested in sequence.
----
"""
@ -538,7 +533,7 @@ class EvMenu:
the menu. Deactivate for production use! When the debug flag is active, the
`persistent` flag is deactivated.
Kwargs:
Keyword Args:
any (any): All kwargs will become initialization variables on `caller.ndb._evmenu`,
to be available at run.
@ -739,6 +734,30 @@ class EvMenu:
Call a node-like callable, with a variable number of raw_string, *args, **kwargs, all of
which should work also if not present (only `caller` is always required). Return its result.
Viable node-like callable forms:
::
_callname(caller)
_callname(caller, raw_string)
_callname(caller, **kwargs)
_callname(caller, raw_string, **kwargs)
If this is a node:
- `caller` is the one using the menu.
- `raw_string` is the users exact input on the *previous* node.
- `**kwargs` is either passed through the previous node or returned
along with the node name from the goto-callable leading to this node.
If this is a goto-callable:
- `caller` is the one using the menu.
- `raw_string` is the user's exact input when chosing the option that triggered
this goto-callable.
- `**kwargs` is any extra dict passed to the callable in the option
definition, or (if no explit kwarg was given to the callable) the
previous node's kwarg, if any.
"""
try:
try:
@ -943,7 +962,7 @@ class EvMenu:
raw_string (str): The raw default string entered on the
previous node (only used if the node accepts it as an
argument)
Kwargs:
Keyword Args:
any: Extra arguments to goto callables.
"""
@ -1317,24 +1336,32 @@ def list_node(option_generator, select=None, pagesize=10):
option_generator (callable or list): A list of strings indicating the options, or a callable
that is called as option_generator(caller) to produce such a list.
select (callable or str, optional): Node to redirect a selection to. Its `**kwargs` will
contain the `available_choices` list and `selection` will hold one of the elements in
that list. If a callable, it will be called as
select(caller, menuchoice, **kwargs) where menuchoice is the chosen option as a
string and `available_choices` is a kwarg mapping the option keys to the choices
offered by the option_generator. The callable whould return the name of the target node
to goto after this selection (or None to repeat the list-node). Note that if this is not
given, the decorated node must itself provide a way to continue from the node!
contain the `available_choices` list and `selection` will hold one
of the elements in that list. If a callable, it will be called as
`select(caller, menuchoice, **kwargs)` where menuchoice is the
chosen option as a string and `available_choices` is the list of available
options offered by the option_generator. The callable whould return
the name of the target node to goto after this selection (or None to repeat the
list-node). Note that if this is not given, the decorated node
must itself provide a way to continue from the node!
pagesize (int): How many options to show per page.
Example:
@list_node(['foo', 'bar'], select)
def node_index(caller):
text = "describing the list"
return text, []
::
def _selectfunc(caller, menuchoice, **kwargs):
# menuchoice would be either 'foo' or 'bar' here
# kwargs['available_choices'] would be the list ['foo', 'bar']
return "the_next_node_to_go_to"
@list_node(['foo', 'bar'], _selectfunc)
def node_index(caller):
text = "describing the list"
return text, []
Notes:
All normal `goto` or `exec` callables returned from the decorated nodes will, if they accept
**kwargs, get a new kwarg 'available_choices' injected. These are the ordered list of named
`**kwargs`, get a new kwarg `available_choices` injected. This is the ordered list of named
options (descs) visible on the current node page.
"""
@ -1579,13 +1606,13 @@ def get_input(caller, prompt, callback, session=None, *args, **kwargs):
greater than 2. The session is then updated by the
command and is available (for example in callbacks)
through `caller.ndb.getinput._session`.
*args, **kwargs (optional): Extra arguments will be
args, kwargs (optional): Extra arguments will be
passed to the fall back function as a list 'args'
and all keyword arguments as a dictionary 'kwargs'.
To utilise *args and **kwargs, a value for the
To utilise `*args` and `**kwargs`, a value for the
session argument must be provided (None by default)
and the callback function must take *args and
**kwargs as arguments.
and the callback function must take `*args` and
`**kwargs` as arguments.
Raises:
RuntimeError: If the given callback is not callable.
@ -1633,9 +1660,7 @@ def get_input(caller, prompt, callback, session=None, *args, **kwargs):
_RE_NODE = re.compile(r"##\s*?NODE\s+?(?P<nodename>\S[\S\s]*?)$", re.I + re.M)
_RE_OPTIONS_SEP = re.compile(r"##\s*?OPTIONS\s*?$", re.I + re.M)
_RE_CALLABLE = re.compile(r"\S+?\(\)", re.I + re.M)
_RE_CALLABLE = re.compile(
r"(?P<funcname>\S+?)(?:\((?P<kwargs>[\S\s]+?)\)|\(\))", re.I + re.M
)
_RE_CALLABLE = re.compile(r"(?P<funcname>\S+?)(?:\((?P<kwargs>[\S\s]+?)\)|\(\))", re.I + re.M)
_HELP_NO_OPTION_MATCH = _("Choose an option or try 'help'.")
@ -1649,8 +1674,8 @@ _OPTION_COMMENT_START = "#"
# Input/option/goto handler functions that allows for dynamically generated
# nodes read from the menu template.
def _process_callable(caller, goto, goto_callables, raw_string,
current_nodename, kwargs):
def _process_callable(caller, goto, goto_callables, raw_string, current_nodename, kwargs):
"""
Central helper for parsing a goto-callable (`funcname(**kwargs)`) out of
the right-hand-side of the template options and map this to an actual
@ -1666,12 +1691,18 @@ def _process_callable(caller, goto, goto_callables, raw_string,
for kwarg in gotokwargs.split(","):
if kwarg and "=" in kwarg:
key, value = [part.strip() for part in kwarg.split("=", 1)]
if key in ("evmenu_goto", "evmenu_gotomap", "_current_nodename",
"evmenu_current_nodename", "evmenu_goto_callables"):
if key in (
"evmenu_goto",
"evmenu_gotomap",
"_current_nodename",
"evmenu_current_nodename",
"evmenu_goto_callables",
):
raise RuntimeError(
f"EvMenu template error: goto-callable '{goto}' uses a "
f"kwarg ({kwarg}) that is reserved for the EvMenu templating "
"system. Rename the kwarg.")
"system. Rename the kwarg."
)
try:
key = literal_eval(key)
except ValueError:
@ -1698,8 +1729,7 @@ def _generated_goto_func(caller, raw_string, **kwargs):
goto = kwargs["evmenu_goto"]
goto_callables = kwargs["evmenu_goto_callables"]
current_nodename = kwargs["evmenu_current_nodename"]
return _process_callable(caller, goto, goto_callables, raw_string,
current_nodename, kwargs)
return _process_callable(caller, goto, goto_callables, raw_string, current_nodename, kwargs)
def _generated_input_goto_func(caller, raw_string, **kwargs):
@ -1719,13 +1749,15 @@ def _generated_input_goto_func(caller, raw_string, **kwargs):
# start with glob patterns
for pattern, goto in gotomap.items():
if fnmatch(raw_string.lower(), pattern):
return _process_callable(caller, goto, goto_callables, raw_string,
current_nodename, kwargs)
return _process_callable(
caller, goto, goto_callables, raw_string, current_nodename, kwargs
)
# no glob pattern match; try regex
for pattern, goto in gotomap.items():
if pattern and re.match(pattern, raw_string.lower(), flags=re.I + re.M):
return _process_callable(caller, goto, goto_callables, raw_string,
current_nodename, kwargs)
return _process_callable(
caller, goto, goto_callables, raw_string, current_nodename, kwargs
)
# no match, show error
raise EvMenuGotoAbortMessage(_HELP_NO_OPTION_MATCH)
@ -1756,6 +1788,7 @@ def parse_menu_template(caller, menu_template, goto_callables=None):
dict: A `{"node": nodefunc}` menutree suitable to pass into EvMenu.
"""
def _validate_kwarg(goto, kwarg):
"""
Validate goto-callable kwarg is on correct form.
@ -1765,14 +1798,21 @@ def parse_menu_template(caller, menu_template, goto_callables=None):
f"EvMenu template error: goto-callable '{goto}' has a "
f"non-kwarg argument ({kwarg}). All callables in the "
"template must have only keyword-arguments, or no "
"args at all.")
"args at all."
)
key, _ = [part.strip() for part in kwarg.split("=", 1)]
if key in ("evmenu_goto", "evmenu_gotomap", "_current_nodename",
"evmenu_current_nodename", "evmenu_goto_callables"):
if key in (
"evmenu_goto",
"evmenu_gotomap",
"_current_nodename",
"evmenu_current_nodename",
"evmenu_goto_callables",
):
raise RuntimeError(
f"EvMenu template error: goto-callable '{goto}' uses a "
f"kwarg ({kwarg}) that is reserved for the EvMenu templating "
"system. Rename the kwarg.")
"system. Rename the kwarg."
)
def _parse_options(nodename, optiontxt, goto_callables):
"""
@ -1802,7 +1842,7 @@ def parse_menu_template(caller, menu_template, goto_callables=None):
if match:
kwargs = match.group("kwargs")
if kwargs:
for kwarg in kwargs.split(','):
for kwarg in kwargs.split(","):
_validate_kwarg(goto, kwarg)
# parse key [;aliases|pattern]
@ -1814,7 +1854,7 @@ def parse_menu_template(caller, menu_template, goto_callables=None):
if main_key.startswith(_OPTION_INPUT_MARKER):
# if we have a pattern, build the arguments for _default later
pattern = main_key[len(_OPTION_INPUT_MARKER):].strip()
pattern = main_key[len(_OPTION_INPUT_MARKER) :].strip()
inputparsemap[pattern] = goto
else:
# a regular goto string/callable target
@ -1875,12 +1915,7 @@ def parse_menu_template(caller, menu_template, goto_callables=None):
def template2menu(
caller,
menu_template,
goto_callables=None,
startnode="start",
persistent=False,
**kwargs,
caller, menu_template, goto_callables=None, startnode="start", persistent=False, **kwargs,
):
"""
Helper function to generate and start an EvMenu based on a menu template
@ -1905,9 +1940,4 @@ def template2menu(
"""
goto_callables = goto_callables or {}
menu_tree = parse_menu_template(caller, menu_template, goto_callables)
return EvMenu(
caller,
menu_tree,
persistent=persistent,
**kwargs,
)
return EvMenu(caller, menu_tree, persistent=persistent, **kwargs,)

View file

@ -7,6 +7,7 @@ down in the text (the name comes from the traditional 'more' unix
command).
To use, simply pass the text through the EvMore object:
::
from evennia.utils.evmore import EvMore
@ -14,17 +15,20 @@ To use, simply pass the text through the EvMore object:
EvMore(caller, text, always_page=False, session=None, justify_kwargs=None, **kwargs)
One can also use the convenience function msg from this module:
::
from evennia.utils import evmore
text = some_long_text_output()
evmore.msg(caller, text, always_page=False, session=None, justify_kwargs=None, **kwargs)
Where always_page decides if the pager is used also if the text is not
long enough to need to scroll, session is used to determine which session to relay to
and justify_kwargs are kwargs to pass to utils.utils.justify in order to change the formatting
of the text. The remaining **kwargs will be passed on to the
caller.msg() construct every time the page is updated.
Where always_page decides if the pager is used also if the text is not long
enough to need to scroll, session is used to determine which session to relay
to and `justify_kwargs` are kwargs to pass to `utils.utils.justify` in order to
change the formatting of the text. The remaining `**kwargs` will be passed on to
the `caller.msg()` construct every time the page is updated.
----
"""
from django.conf import settings
@ -124,9 +128,10 @@ def queryset_maxsize(qs):
return qs.count()
class EvMore(object):
class EvMore:
"""
The main pager object
The main pager object.
"""
def __init__(
@ -144,23 +149,25 @@ class EvMore(object):
):
"""
Initialization of the inp handler.
Initialization of the Evmore input handler.
Args:
caller (Object or Account): Entity reading the text.
inp (str, EvTable, Paginator or iterator): The text or data to put under paging.
- If a string, paginage normally. If this text contains
one or more `\f` format symbol, automatic pagination and justification
are force-disabled and page-breaks will only happen after each `\f`.
one or more \\\\f (backslash + f) format symbols, automatic
pagination and justification are force-disabled and
page-breaks will only happen after each \\\\f.
- If `EvTable`, the EvTable will be paginated with the same
setting on each page if it is too long. The table
decorations will be considered in the size of the page.
setting on each page if it is too long. The table
decorations will be considered in the size of the page.
- Otherwise `inp` is converted to an iterator, where each step is
expected to be a line in the final display. Each line
will be run through `iter_callable`.
always_page (bool, optional): If `False`, the
pager will only kick in if `inp` is too big
to fit the screen.
expected to be a line in the final display. Each line
will be run through `iter_callable`.
always_page (bool, optional): If `False`, the pager will only kick
in if `inp` is too big to fit the screen.
session (Session, optional): If given, this session will be used
to determine the screen width and will receive all output.
justify (bool, optional): If set, auto-justify long lines. This must be turned
@ -176,30 +183,51 @@ class EvMore(object):
the caller when the more page exits. Note that this will be using whatever
cmdset the user had *before* the evmore pager was activated (so none of
the evmore commands will be available when this is run).
kwargs (any, optional): These will be passed on to the `caller.msg` method.
kwargs (any, any): These will be passed on to the `caller.msg` method.
Examples:
Basic use:
```
super_long_text = " ... "
EvMore(caller, super_long_text)
```
Paginator
```
from django.core.paginator import Paginator
query = ObjectDB.objects.all()
pages = Paginator(query, 10) # 10 objs per page
EvMore(caller, pages)
```
Every page an EvTable
```
from evennia import EvTable
def _to_evtable(page):
table = ... # convert page to a table
return EvTable(*headers, table=table, ...)
EvMore(caller, pages, page_formatter=_to_evtable)
```
::
super_long_text = " ... "
EvMore(caller, super_long_text)
Paginated query data - this is an optimization to avoid fetching
database data until it's actually paged to.
::
from django.core.paginator import Paginator
query = ObjectDB.objects.all()
pages = Paginator(query, 10) # 10 objs per page
EvMore(caller, pages)
Automatic split EvTable over multiple EvMore pages
::
table = EvMore(*header, table=tabledata)
EvMore(caller, table)
Every page a separate EvTable (optimization for very large data sets)
::
from evennia import EvTable, EvMore
class TableEvMore(EvMore):
def init_pages(self, data):
pages = # depends on data type
super().init_pages(pages)
def page_formatter(self, page):
table = EvTable()
for line in page:
cols = # split raw line into columns
table.add_row(*cols)
return str(table)
TableEvMore(caller, pages)
"""
self._caller = caller
@ -386,9 +414,10 @@ class EvMore(object):
def init_f_str(self, text):
"""
The input contains \f markers. We use \f to indicate the user wants to
enforce their line breaks on their own. If so, we do no automatic
line-breaking/justification at all.
The input contains \\\\f (backslash + f) markers. We use \\\\f to indicate
the user wants to enforce their line breaks on their own. If so, we do
no automatic line-breaking/justification at all.
"""
self._data = text.split("\f")
self._npages = len(self._data)
@ -433,14 +462,17 @@ class EvMore(object):
Notes:
If overridden, this method must perform the following actions:
- read and re-store `self._data` (the incoming data set) if needed for pagination to work.
- read and re-store `self._data` (the incoming data set) if needed
for pagination to work.
- set `self._npages` to the total number of pages. Default is 1.
- set `self._paginator` to a callable that will take a page number 1...N and return
the data to display on that page (not any decorations or next/prev buttons). If only
wanting to change the paginator, override `self.paginator` instead.
- set `self._page_formatter` to a callable that will receive the page from `self._paginator`
and format it with one element per line. Default is `str`. Or override `self.page_formatter`
directly instead.
the data to display on that page (not any decorations or next/prev buttons). If only
wanting to change the paginator, override `self.paginator` instead.
- set `self._page_formatter` to a callable that will receive the
page from `self._paginator` and format it with one element per
line. Default is `str`. Or override `self.page_formatter`
directly instead.
By default, helper methods are called that perform these actions
depending on supported inputs.
@ -520,15 +552,17 @@ def msg(
Args:
caller (Object or Account): Entity reading the text.
text (str, EvTable or iterator): The text or data to put under paging.
- If a string, paginage normally. If this text contains
one or more `\f` format symbol, automatic pagination is disabled
and page-breaks will only happen after each `\f`.
one or more \\\\f (backslash + f) format symbol, automatic pagination is disabled
and page-breaks will only happen after each \\\\f.
- If `EvTable`, the EvTable will be paginated with the same
setting on each page if it is too long. The table
decorations will be considered in the size of the page.
setting on each page if it is too long. The table
decorations will be considered in the size of the page.
- Otherwise `text` is converted to an iterator, where each step is
is expected to be a line in the final display, and each line
will be run through repr().
always_page (bool, optional): If `False`, the
pager will only kick in if `text` is too big
to fit the screen.

View file

@ -1,66 +1,61 @@
"""
This is an advanced ASCII table creator. It was inspired by
[prettytable](https://code.google.com/p/prettytable/) but shares no
code.
[prettytable](https://code.google.com/p/prettytable/) but shares no code.
Example usage:
::
```python
from evennia.utils import evtable
from evennia.utils import evtable
table = evtable.EvTable("Heading1", "Heading2",
table = evtable.EvTable("Heading1", "Heading2",
table=[[1,2,3],[4,5,6],[7,8,9]], border="cells")
table.add_column("This is long data", "This is even longer data")
table.add_row("This is a single row")
print table
```
table.add_column("This is long data", "This is even longer data")
table.add_row("This is a single row")
print table
Result:
::
```
+----------------------+----------+---+--------------------------+
| Heading1 | Heading2 | | |
+~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~+~~~+~~~~~~~~~~~~~~~~~~~~~~~~~~+
| 1 | 4 | 7 | This is long data |
+----------------------+----------+---+--------------------------+
| 2 | 5 | 8 | This is even longer data |
+----------------------+----------+---+--------------------------+
| 3 | 6 | 9 | |
+----------------------+----------+---+--------------------------+
| This is a single row | | | |
+----------------------+----------+---+--------------------------+
```
+----------------------+----------+---+--------------------------+
| Heading1 | Heading2 | | |
+~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~+~~~+~~~~~~~~~~~~~~~~~~~~~~~~~~+
| 1 | 4 | 7 | This is long data |
+----------------------+----------+---+--------------------------+
| 2 | 5 | 8 | This is even longer data |
+----------------------+----------+---+--------------------------+
| 3 | 6 | 9 | |
+----------------------+----------+---+--------------------------+
| This is a single row | | | |
+----------------------+----------+---+--------------------------+
As seen, the table will automatically expand with empty cells to make
the table symmetric. Tables can be restricted to a given width:
::
```python
table.reformat(width=50, align="l")
```
table.reformat(width=50, align="l")
(We could just have added these keywords to the table creation call)
This yields the following result:
::
```
+-----------+------------+-----------+-----------+
| Heading1 | Heading2 | | |
+~~~~~~~~~~~+~~~~~~~~~~~~+~~~~~~~~~~~+~~~~~~~~~~~+
| 1 | 4 | 7 | This is |
| | | | long data |
+-----------+------------+-----------+-----------+
| | | | This is |
| 2 | 5 | 8 | even |
| | | | longer |
| | | | data |
+-----------+------------+-----------+-----------+
| 3 | 6 | 9 | |
+-----------+------------+-----------+-----------+
| This is a | | | |
| single | | | |
| row | | | |
+-----------+------------+-----------+-----------+
```
+-----------+------------+-----------+-----------+
| Heading1 | Heading2 | | |
+~~~~~~~~~~~+~~~~~~~~~~~~+~~~~~~~~~~~+~~~~~~~~~~~+
| 1 | 4 | 7 | This is |
| | | | long data |
+-----------+------------+-----------+-----------+
| | | | This is |
| 2 | 5 | 8 | even |
| | | | longer |
| | | | data |
+-----------+------------+-----------+-----------+
| 3 | 6 | 9 | |
+-----------+------------+-----------+-----------+
| This is a | | | |
| single | | | |
| row | | | |
+-----------+------------+-----------+-----------+
Table-columns can be individually formatted. Note that if an
individual column is set with a specific width, table auto-balancing
@ -68,29 +63,25 @@ will not affect this column (this may lead to the full table being too
wide, so be careful mixing fixed-width columns with auto- balancing).
Here we change the width and alignment of the column at index 3
(Python starts from 0):
::
```python
table.reformat_column(3, width=30, align="r")
print table
table.reformat_column(3, width=30, align="r")
print table
```
```
+-----------+-------+-----+-----------------------------+---------+
| Heading1 | Headi | | | |
| | ng2 | | | |
+~~~~~~~~~~~+~~~~~~~+~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~+
| 1 | 4 | 7 | This is long data | Test1 |
+-----------+-------+-----+-----------------------------+---------+
| 2 | 5 | 8 | This is even longer data | Test3 |
+-----------+-------+-----+-----------------------------+---------+
| 3 | 6 | 9 | | Test4 |
+-----------+-------+-----+-----------------------------+---------+
| This is a | | | | |
| single | | | | |
| row | | | | |
+-----------+-------+-----+-----------------------------+---------+
```
+-----------+-------+-----+-----------------------------+---------+
| Heading1 | Headi | | | |
| | ng2 | | | |
+~~~~~~~~~~~+~~~~~~~+~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~+
| 1 | 4 | 7 | This is long data | Test1 |
+-----------+-------+-----+-----------------------------+---------+
| 2 | 5 | 8 | This is even longer data | Test3 |
+-----------+-------+-----+-----------------------------+---------+
| 3 | 6 | 9 | | Test4 |
+-----------+-------+-----+-----------------------------+---------+
| This is a | | | | |
| single | | | | |
| row | | | | |
+-----------+-------+-----+-----------------------------+---------+
When adding new rows/columns their data can have its own alignments
(left/center/right, top/center/bottom).
@ -109,6 +100,8 @@ eventual colour outside the table will not transfer "across" a table,
you need to re-set the color to have it appear on both sides of the
table string.
----
"""
from django.conf import settings
@ -282,7 +275,7 @@ def wrap(text, width=_DEFAULT_WIDTH, **kwargs):
text (str): Text to wrap.
width (int, optional): Width to wrap `text` to.
Kwargs:
Keyword Args:
See TextWrapper class for available keyword args to customize
wrapping behaviour.
@ -303,7 +296,7 @@ def fill(text, width=_DEFAULT_WIDTH, **kwargs):
text (str): Text to fill.
width (int, optional): Width of fill area.
Kwargs:
Keyword Args:
See TextWrapper class for available keyword args to customize
filling behaviour.
@ -328,7 +321,7 @@ class EvCell(object):
Args:
data (str): The un-padded data of the entry.
Kwargs:
Keyword Args:
width (int): Desired width of cell. It will pad
to this size.
height (int): Desired height of cell. it will pad
@ -773,7 +766,7 @@ class EvCell(object):
"""
Reformat the EvCell with new options
Kwargs:
Keyword Args:
The available keyword arguments are the same as for `EvCell.__init__`.
Raises:
@ -932,7 +925,7 @@ class EvColumn(object):
Args:
Text for each row in the column
Kwargs:
Keyword Args:
All `EvCell.__init_` keywords are available, these
settings will be persistently applied to every Cell in the
column.
@ -947,7 +940,7 @@ class EvColumn(object):
coherent and lined-up column. Will enforce column-specific
options to cells.
Kwargs:
Keyword Args:
Extra keywords to modify the column setting. Same keywords
as in `EvCell.__init__`.
@ -979,7 +972,7 @@ class EvColumn(object):
use `ypos=0`. If not given, data will be inserted at the end
of the column.
Kwargs:
Keyword Args:
Available keywods as per `EvCell.__init__`.
"""
@ -998,7 +991,7 @@ class EvColumn(object):
"""
Change the options for the column.
Kwargs:
Keyword Args:
Keywords as per `EvCell.__init__`.
"""
@ -1013,7 +1006,7 @@ class EvColumn(object):
index (int): Index location of the cell in the column,
starting from 0 for the first row to Nrows-1.
Kwargs:
Keyword Args:
Keywords as per `EvCell.__init__`.
"""
@ -1053,7 +1046,7 @@ class EvTable(object):
Args:
Header texts for the table.
Kwargs:
Keyword Args:
table (list of lists or list of `EvColumns`, optional):
This is used to build the table in a quick way. If not
given, the table will start out empty and `add_` methods
@ -1207,7 +1200,7 @@ class EvTable(object):
nx (int): x size of table.
ny (int): y size of table.
Kwargs:
Keyword Args:
Keywords as per `EvTable.__init__`.
Returns:
@ -1534,7 +1527,7 @@ class EvTable(object):
Args:
args (str): These strings will be used as the header texts.
Kwargs:
Keyword Args:
Same keywords as per `EvTable.__init__`. Will be applied
to the new header's cells.
@ -1558,7 +1551,7 @@ class EvTable(object):
to input new column. If not given, column will be added to the end
of the table. Uses Python indexing (so first column is `xpos=0`)
Kwargs:
Keyword Args:
Other keywords as per `Cell.__init__`.
"""
@ -1622,7 +1615,7 @@ class EvTable(object):
input new row. If not given, will be added to the end of the table.
Uses Python indexing (so first row is `ypos=0`)
Kwargs:
Keyword Args:
Other keywords are as per `EvCell.__init__`.
"""
@ -1661,7 +1654,7 @@ class EvTable(object):
"""
Force a re-shape of the entire table.
Kwargs:
Keyword Args:
Table options as per `EvTable.__init__`.
"""
@ -1697,7 +1690,7 @@ class EvTable(object):
index (int): Which column to reformat. The column index is
given from 0 to Ncolumns-1.
Kwargs:
Keyword Args:
Column options as per `EvCell.__init__`.
Raises:

View file

@ -2,20 +2,18 @@
Inline functions (nested form).
This parser accepts nested inlinefunctions on the form
::
```
$funcname(arg, arg, ...)
```
$funcname(arg, arg, ...)
embedded in any text where any arg can be another $funcname{} call.
embedded in any text where any arg can be another `$funcname{}` call.
This functionality is turned off by default - to activate,
`settings.INLINEFUNC_ENABLED` must be set to `True`.
Each token starts with "$funcname(" where there must be no space
between the $funcname and (. It ends with a matched ending parentesis.
")".
Each token starts with `$funcname(` where there must be no space between the
$funcname and "(". It ends with a matched ending parentesis ")".
Inside the inlinefunc definition, one can use `\` to escape. This is
Inside the inlinefunc definition, one can use \\\\ to escape. This is
mainly needed for escaping commas in flowing text (which would
otherwise be interpreted as an argument separator), or to escape `}`
when not intended to close the function block. Enclosing text in
@ -27,11 +25,10 @@ The available inlinefuncs are defined as global-level functions in
modules defined by `settings.INLINEFUNC_MODULES`. They are identified
by their function name (and ignored if this name starts with `_`). They
should be on the following form:
::
```python
def funcname (*args, **kwargs):
def funcname (*args, **kwargs):
# ...
```
Here, the arguments given to `$funcname(arg1,arg2)` will appear as the
`*args` tuple. This will be populated by the arguments given to the
@ -44,19 +41,21 @@ the string is sent to a non-puppetable object. The inlinefunc should
never raise an exception.
There are two reserved function names:
- "nomatch": This is called if the user uses a functionname that is
not registered. The nomatch function will get the name of the
not-found function as its first argument followed by the normal
arguments to the given function. If not defined the default effect is
to print `<UNKNOWN>` to replace the unknown function.
not registered. The nomatch function will get the name of the
not-found function as its first argument followed by the normal
arguments to the given function. If not defined the default effect is
to print `<UNKNOWN>` to replace the unknown function.
- "stackfull": This is called when the maximum nested function stack is reached.
When this happens, the original parsed string is returned and the result of
the `stackfull` inlinefunc is appended to the end. By default this is an
error message.
Error handling:
Syntax errors, notably not completely closing all inlinefunc
blocks, will lead to the entire string remaining unparsed.
Syntax errors, notably not completely closing all inlinefunc blocks, will lead
to the entire string remaining unparsed.
----
"""
@ -92,9 +91,10 @@ def random(*args, **kwargs):
given range.
Example:
`$random()`
`$random(5)`
`$random(5, 10)`
- `$random()`
- `$random(5)`
- `$random(5, 10)`
"""
nargs = len(args)
@ -134,7 +134,7 @@ def pad(*args, **kwargs):
fillchar (str, optional): Character used for padding. Defaults to a
space.
Kwargs:
Keyword Args:
session (Session): Session performing the pad.
Example:
@ -164,7 +164,7 @@ def crop(*args, **kwargs):
crop in characters.
suffix (str, optional): End string to mark the fact that a part
of the string was cropped. Defaults to `[...]`.
Kwargs:
Keyword Args:
session (Session): Session performing the crop.
Example:
@ -189,7 +189,7 @@ def space(*args, **kwargs):
Args:
spaces (int, optional): The number of spaces to insert.
Kwargs:
Keyword Args:
session (Session): Session performing the crop.
Example:
@ -212,7 +212,7 @@ def clr(*args, **kwargs):
text (str, optional): Text
endclr (str, optional): The color to use at the end of the string. Defaults
to `|n` (reset-color).
Kwargs:
Keyword Args:
session (Session): Session object triggering inlinefunc.
Example:
@ -369,7 +369,7 @@ def parse_inlinefunc(string, strip=False, available_funcs=None, stacktrace=False
available_funcs (dict, optional): Define an alternative source of functions to parse for.
If unset, use the functions found through `settings.INLINEFUNC_MODULES`.
stacktrace (bool, optional): If set, print the stacktrace to log.
Kwargs:
Keyword Args:
session (Session): This is sent to this function by Evennia when triggering
it. It is passed to the inlinefunc.
kwargs (any): All other kwargs are also passed on to the inlinefunc.
@ -574,15 +574,15 @@ def initialize_nick_templates(in_template, out_template):
Args:
in_template (str): The template to be used for nick recognition.
out_template (str): The template to be used to replace the string
matched by the in_template.
matched by the `in_template`.
Returns:
regex (regex): Regex to match against strings
template (str): Template with markers {arg1}, {arg2}, etc for
replacement using the standard .format method.
regex, template (regex, str): Regex to match against strings and a
template with markers `{arg1}`, `{arg2}`, etc for replacement using the
standard `.format` method.
Raises:
NickTemplateInvalid: If the in/out template does not have a matching
inlinefuncs.NickTemplateInvalid: If the in/out template does not have a matching
number of $args.
"""

View file

@ -1,3 +1,9 @@
"""
Option classes store user- or server Options in a generic way
while also providing validation.
"""
import datetime
from evennia import logger
from evennia.utils.ansi import strip_ansi
@ -6,7 +12,7 @@ from evennia.utils.utils import crop
from evennia.utils import validatorfuncs
class BaseOption(object):
class BaseOption:
"""
Abstract Class to deal with encapsulating individual Options. An Option has
a name/key, a description to display in relevant commands and menus, and a
@ -109,11 +115,11 @@ class BaseOption(object):
def save(self, **kwargs):
"""
Stores the current value using .handler.save_handler(self.key, value, **kwargs)
Stores the current value using `.handler.save_handler(self.key, value, **kwargs)`
where kwargs are a combination of those passed into this function and the
ones specified by the OptionHandler.
Kwargs:
Keyword Args:
any (any): Not used by default. These are passed in from self.set
and allows the option to let the caller customize saving by
overriding or extend the default save kwargs
@ -173,7 +179,7 @@ class BaseOption(object):
"""
Renders the Option's value as something pretty to look at.
Kwargs:
Keyword Args:
any (any): These are options passed by the caller to potentially
customize display dynamically.

View file

@ -36,7 +36,6 @@ __all__ = (
"search_message",
"search_channel",
"search_help_entry",
"search_object_tag",
"search_script_tag",
"search_account_tag",
"search_channel_tag",

View file

@ -38,17 +38,19 @@ def unload_module(module):
an object, the module in which that object sits will be unloaded. A string
should directly give the module pathname to unload.
Example:
# (in a test method)
unload_module(foo)
with mock.patch("foo.GLOBALTHING", "mockval"):
import foo
... # test code using foo.GLOBALTHING, now set to 'mockval'
Example:
::
# (in a test method)
unload_module(foo)
with mock.patch("foo.GLOBALTHING", "mockval"):
import foo
... # test code using foo.GLOBALTHING, now set to 'mockval'
This allows for mocking constants global to the module, since
otherwise those would not be mocked (since a module is only
loaded once).
Notes:
This allows for mocking constants global to the module, since
otherwise those would not be mocked (since a module is only
loaded once).
"""
if isinstance(module, str):

View file

@ -3,60 +3,165 @@ Unit tests for the EvForm text form generator
"""
from django.test import TestCase
from evennia.utils import evform
from evennia.utils import evform, ansi, evtable
class TestEvForm(TestCase):
def test_form(self):
self.maxDiff = None
form1 = evform._test()
form2 = evform._test()
maxDiff = None
def _parse_form(self):
"test evform. This is used by the unittest system."
form = evform.EvForm("evennia.utils.tests.data.evform_example")
# add data to each tagged form cell
form.map(
cells={
"AA": "|gTom the Bouncer",
2: "|yGriatch",
3: "A sturdy fellow",
4: 12,
5: 10,
6: 5,
7: 18,
8: 10,
9: 3,
"F": "rev 1",
}
)
# create the EvTables
tableA = evtable.EvTable("HP", "MV", "MP",
table=[["**"], ["*****"], ["***"]],
border="incols")
tableB = evtable.EvTable(
"Skill",
"Value",
"Exp",
table=[
["Shooting", "Herbalism", "Smithing"],
[12, 14, 9],
["550/1200", "990/1400", "205/900"],
],
border="incols",
)
# add the tables to the proper ids in the form
form.map(tables={"A": tableA, "B": tableB})
return str(form)
def _simple_form(self, form):
cellsdict = {1: "Apple", 2: "Banana", 3: "Citrus", 4: "Durian"}
formdict = {"FORMCHAR": 'x', "TABLECHAR": 'c', "FORM": form}
form = evform.EvForm(form=formdict)
form.map(cellsdict)
form = ansi.strip_ansi(str(form))
# this is necessary since editors/black tend to strip lines spaces
# from the end of lines for the comparison strings.
form = "\n".join(line.rstrip() for line in form.split("\n"))
return form
def test_form_consistency(self):
"""
Make sure form looks the same every time.
"""
form1 = self._parse_form()
form2 = self._parse_form()
self.assertEqual(form1, form2)
# self.assertEqual(form1, "")
# '.------------------------------------------------.\n'
# '| |\n'
# '| Name: \x1b[0m\x1b[1m\x1b[32mTom\x1b[1m\x1b[32m \x1b'
# '[1m\x1b[32mthe\x1b[1m\x1b[32m \x1b[0m \x1b[0m '
# 'Account: \x1b[0m\x1b[1m\x1b[33mGriatch '
# '\x1b[0m\x1b[0m\x1b[1m\x1b[32m\x1b[1m\x1b[32m\x1b[1m\x1b[32m\x1b[1m\x1b[32m\x1b[0m\x1b[0m '
# '|\n'
# '| \x1b[0m\x1b[1m\x1b[32mBouncer\x1b[0m \x1b[0m |\n'
# '| |\n'
# ' >----------------------------------------------< \n'
# '| |\n'
# '| Desc: \x1b[0mA sturdy \x1b[0m \x1b[0m'
# ' STR: \x1b[0m12 \x1b[0m\x1b[0m\x1b[0m\x1b[0m'
# ' DEX: \x1b[0m10 \x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
# '| \x1b[0mfellow\x1b[0m \x1b[0m'
# ' INT: \x1b[0m5 \x1b[0m\x1b[0m\x1b[0m\x1b[0m'
# ' STA: \x1b[0m18 \x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
# '| \x1b[0m \x1b[0m'
# ' LUC: \x1b[0m10 \x1b[0m\x1b[0m\x1b[0m'
# ' MAG: \x1b[0m3 \x1b[0m\x1b[0m\x1b[0m |\n'
# '| |\n'
# ' >----------.-----------------------------------< \n'
# '| | |\n'
# '| \x1b[0mHP\x1b[0m|\x1b[0mMV \x1b[0m|\x1b[0mMP\x1b[0m '
# '| \x1b[0mSkill \x1b[0m|\x1b[0mValue \x1b[0m'
# '|\x1b[0mExp \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
# '| ~~+~~~+~~ | ~~~~~~~~~~~+~~~~~~~~~~+~~~~~~~~~~~ |\n'
# '| \x1b[0m**\x1b[0m|\x1b[0m***\x1b[0m\x1b[0m|\x1b[0m**\x1b[0m\x1b[0m '
# '| \x1b[0mShooting \x1b[0m|\x1b[0m12 \x1b[0m'
# '|\x1b[0m550/1200 \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
# '| \x1b[0m \x1b[0m|\x1b[0m**\x1b[0m \x1b[0m|\x1b[0m*\x1b[0m \x1b[0m '
# '| \x1b[0mHerbalism \x1b[0m|\x1b[0m14 \x1b[0m'
# '|\x1b[0m990/1400 \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
# '| \x1b[0m \x1b[0m|\x1b[0m \x1b[0m|\x1b[0m \x1b[0m '
# '| \x1b[0mSmithing \x1b[0m|\x1b[0m9 \x1b[0m'
# '|\x1b[0m205/900 \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
# '| | |\n'
# ' -----------`-------------------------------------\n'
# ' Footer: \x1b[0mrev 1 \x1b[0m \n'
# ' info \n'
# ' ')
def test_form_output(self):
"""
Check the result of the form. We strip ansi for readability.
"""
form = self._parse_form()
form_noansi = ansi.strip_ansi(form)
# we must strip extra space at the end of output simply
# because editors tend to strip it when creating
# the comparison string ...
form_noansi = "\n".join(line.rstrip() for line in form_noansi.split("\n"))
self.assertNotEqual(form, form_noansi)
expected = """
.------------------------------------------------.
| |
| Name: Tom the Account: Griatch |
| Bouncer |
| |
>----------------------------------------------<
| |
| Desc: A sturdy STR: 12 DEX: 10 |
| fellow INT: 5 STA: 18 |
| LUC: 10 MAG: 3 |
| |
>----------.-----------------------------------<
| | |
| HP|MV |MP | Skill |Value |Exp |
| ~~+~~~+~~ | ~~~~~~~~~~~+~~~~~~~~~~+~~~~~~~~~~~ |
| **|***|** | Shooting |12 |550/1200 |
| |** |* | Herbalism |14 |990/1400 |
| | | | Smithing |9 |205/900 |
| | |
-----------`-------------------------------------
Footer: rev 1
info
""".lstrip()
self.assertEqual(expected, form_noansi)
def test_ansi_escape(self):
# note that in a msg() call, the result would be the correct |-----,
# in a print, ansi only gets called once, so ||----- is the result
self.assertEqual(str(evform.EvForm(form={"FORM": "\n||-----"})), "||-----")
def test_stacked_form(self):
"""
Test simple stacked form.
"""
form = """
xxxx1xxxx
xxxx2xxxx
xxxx3xxxx
xxxx4xxxx
"""
expected = """
Apple
Banana
Citrus
Durian
""".lstrip()
result = self._simple_form(form)
self.assertEqual(expected, result)
def test_side_by_side_two_column(self):
"""
Side-by-side 2-column form (bug #2205)
"""
form = """
xxxx1xxxx xxxx2xxxx
xxxx3xxxx xxxx4xxxx
"""
expected = """
Apple Banana
Citrus Durian
""".lstrip()
result = self._simple_form(form)
self.assertEqual(expected, result)
def test_side_by_side_three_column(self):
"""
Side-by-side 3-column form (bug #2205)
"""
form = """
xxxx1xxxx xxxx2xxxx xxxx3xxxx
xxxx4xxxx
"""
expected = """
Apple Banana Citrus
Durian
""".lstrip()
result = self._simple_form(form)
self.assertEqual(expected, result)

View file

@ -321,8 +321,7 @@ class TestMenuTemplateParse(EvenniaTest):
def test_parse_menu_template(self):
"""EvMenu template testing"""
menutree = evmenu.parse_menu_template(self.char1, self.menu_template,
self.goto_callables)
menutree = evmenu.parse_menu_template(self.char1, self.menu_template, self.goto_callables)
self.assertEqual(menutree, {"start": Anything, "node1": Anything, "node2": Anything})
def test_template2menu(self):

View file

@ -177,6 +177,59 @@ class ANSIStringTestCase(TestCase):
self.assertEqual(a.rstrip(), ANSIString(" |r Test of stuff |b with spaces|n"))
self.assertEqual(b.strip(), b)
def test_regex_search(self):
"""
Test regex-search in ANSIString - the found position should ignore any ansi-markers
"""
string = ANSIString(" |r|[b Test ")
match = re.search(r"Test", string)
self.assertTrue(match)
self.assertEqual(match.span(), (3, 7))
def test_regex_replace(self):
"""
Inserting text into an ansistring at an index position should ignore
the ansi markers but not remove them!
"""
string = ANSIString("A |rTest|n string")
match = re.search(r"Test", string)
ix1, ix2 = match.span()
self.assertEqual((ix1, ix2), (2, 6))
result = string[:ix1] + "Replacement" + string[ix2:]
expected = ANSIString("A |rReplacement|n string")
self.assertEqual(expected, result)
def test_slice_insert(self):
"""
Inserting a slice should not remove ansi markup (issue #2205)
"""
string = ANSIString("|rTest|n")
split_string = string[:0] + "Test" + string[4:]
self.assertEqual(string.raw(), split_string.raw())
def test_slice_insert_longer(self):
"""
The ANSIString replays the color code before the split in order to
produce a *visually* identical result. The result is a longer string in
raw characters, but one which correctly represents the color output.
"""
string = ANSIString("A bigger |rTest|n of things |bwith more color|n")
# from evennia import set_trace;set_trace()
split_string = string[:9] + "Test" + string[13:]
self.assertEqual(
repr((ANSIString("A bigger ")
+ ANSIString("|rTest") # note that the |r|n is replayed together on next line
+ ANSIString("|r|n of things |bwith more color|n")).raw()),
repr(split_string.raw()))
def test_slice_full(self):
string = ANSIString("A bigger |rTest|n of things |bwith more color|n")
split_string = string[:]
self.assertEqual(string.raw(), split_string.raw())
class TestTextToHTMLparser(TestCase):
def setUp(self):

View file

@ -1029,7 +1029,7 @@ def delay(timedelay, callback, *args, **kwargs):
callback (callable): Will be called as `callback(*args, **kwargs)`
after `timedelay` seconds.
args (any, optional): Will be used as arguments to callback
Kwargs:
Keyword Args:
persistent (bool, optional): should make the delay persistent
over a reboot or reload
any (any): Will be used as keyword arguments to callback.
@ -1069,11 +1069,11 @@ def run_async(to_execute, *args, **kwargs):
Args:
to_execute (callable): If this is a callable, it will be
executed with *args and non-reserved *kwargs as arguments.
executed with `*args` and non-reserved `**kwargs` as arguments.
The callable will be executed using ProcPool, or in a thread
if ProcPool is not available.
Kwargs:
Keyword Args:
at_return (callable): Should point to a callable with one
argument. It will be called with the return value from
to_execute.
@ -1168,7 +1168,7 @@ def check_evennia_dependencies():
def has_parent(basepath, obj):
"""
Checks if `basepath` is somewhere in `obj`s parent tree.
Checks if `basepath` is somewhere in `obj`'s parent tree.
Args:
basepath (str): Python dotpath to compare against obj path.
@ -1495,8 +1495,8 @@ def init_new_account(account):
def string_similarity(string1, string2):
"""
This implements a "cosine-similarity" algorithm as described for example in
*Proceedings of the 22nd International Conference on Computation
Linguistics* (Coling 2008), pages 593-600, Manchester, August 2008.
*Proceedings of the 22nd International Conference on Computation
Linguistics* (Coling 2008), pages 593-600, Manchester, August 2008.
The measure-vectors used is simply a "bag of words" type histogram
(but for letters).
@ -1605,9 +1605,9 @@ def string_partial_matching(alternatives, inp, ret_index=True):
def format_table(table, extra_space=1):
"""
Note: `evennia.utils.evtable` is more powerful than this, but this
function can be useful when the number of columns and rows are
unknown and must be calculated on the fly.
Note: `evennia.utils.evtable` is more powerful than this, but this function
can be useful when the number of columns and rows are unknown and must be
calculated on the fly.
Args.
table (list): A list of lists to represent columns in the
@ -1626,18 +1626,18 @@ def format_table(table, extra_space=1):
The function formats the columns to be as wide as the widest member
of each column.
Examples:
Example:
::
ftable = format_table([[...], [...], ...])
for ir, row in enumarate(ftable):
if ir == 0:
# make first row white
string += "\\\\n|w" + ""join(row) + "|n"
else:
string += "\\\\n" + "".join(row)
print(string)
```python
ftable = format_table([[...], [...], ...])
for ir, row in enumarate(ftable):
if ir == 0:
# make first row white
string += "\n|w" + ""join(row) + "|n"
else:
string += "\n" + "".join(row)
print string
```
"""
if not table:
return [[]]
@ -1888,7 +1888,7 @@ def at_search_result(matches, caller, query="", quiet=False, **kwargs):
quiet (bool, optional): If `True`, no messages will be echoed to caller
on errors.
Kwargs:
Keyword Args:
nofound_string (str): Replacement string to echo on a notfound error.
multimatch_string (str): Replacement string to echo on a multimatch error.
@ -1953,7 +1953,7 @@ class LimitedSizeOrderedDict(OrderedDict):
"""
Limited-size ordered dict.
Kwargs:
Keyword Args:
size_limit (int): Use this to limit the number of elements
alloweds to be in this list. By default the overshooting elements
will be removed in FIFO order.
@ -2051,26 +2051,28 @@ def get_all_typeclasses(parent=None):
def interactive(func):
"""
Decorator to make a method pausable with yield(seconds)
and able to ask for user-input with response=yield(question).
For the question-asking to work, 'caller' must the name
of an argument or kwarg to the decorated function.
Decorator to make a method pausable with yield(seconds) and able to ask for
user-input with `response=yield(question)`. For the question-asking to
work, 'caller' must the name of an argument or kwarg to the decorated
function.
Note that this turns the method into a generator.
Example:
::
Example usage:
@interactive
def myfunc(caller):
caller.msg("This is a test")
# wait five seconds
yield(5)
# ask user (caller) a question
response = yield("Do you want to continue waiting?")
if response == "yes":
@interactive
def myfunc(caller):
caller.msg("This is a test")
# wait five seconds
yield(5)
else:
# ...
# ask user (caller) a question
response = yield("Do you want to continue waiting?")
if response == "yes":
yield(5)
else:
# ...
Notes:
This turns the method into a generator!
"""
from evennia.utils.evmenu import get_input

View file

@ -39,8 +39,8 @@ def color(entry, option_key="Color", **kwargs):
def datetime(entry, option_key="Datetime", account=None, from_tz=None, **kwargs):
"""
Process a datetime string in standard forms while accounting for the inputer's timezone. Always
returns a result in UTC.
Process a datetime string in standard forms while accounting for the
inputer's timezone. Always returns a result in UTC.
Args:
entry (str): A date string from a user.
@ -48,10 +48,12 @@ def datetime(entry, option_key="Datetime", account=None, from_tz=None, **kwargs)
account (AccountDB): The Account performing this lookup. Unless `from_tz` is provided,
the account's timezone option will be used.
from_tz (pytz.timezone): An instance of a pytz timezone object from the
user. If not provided, tries to use the timezone option of the `account'.
user. If not provided, tries to use the timezone option of the `account`.
If neither one is provided, defaults to UTC.
Returns:
datetime in UTC.
Raises:
ValueError: If encountering a malformed timezone, date string or other format error.

View file

@ -145,7 +145,7 @@ class EvenniaIndexView(TemplateView):
You can do whatever you want to it, but it must be returned at the end
of this method.
Kwargs:
Keyword Args:
any (any): Passed through.
Returns: