mirror of
https://github.com/evennia/evennia.git
synced 2025-10-29 19:35:56 +00:00
Add help entry to project_rename utility.
Also merge in latest master changes to devel branch.
This commit is contained in:
commit
6d846c6c82
@ -25,6 +25,25 @@ FAKE_MODE = False
|
||||
# if these words are longer than output word, retain given case
|
||||
CASE_WORD_EXCEPTIONS = ('an', )
|
||||
|
||||
_HELP_TEXT = """This program interactively renames words in all files of your project. It's
|
||||
currently renaming {sources} to {targets}.
|
||||
|
||||
If it wants to replace text in a file, it will show all lines (and line numbers) it wants to
|
||||
replace, each directly followed by the suggested replacement.
|
||||
|
||||
If a rename is not okay, you can de-select it by entering 'i' followed by one or more
|
||||
comma-separated line numbers. You cannot ignore partial lines, those you need to remember to change
|
||||
manually later.
|
||||
|
||||
[q]uit - exits the program immediately.
|
||||
[h]elp - this help.
|
||||
[s]kip file - make no changes at all in this file, continue on to the next.
|
||||
[i]ignore lines - specify line numbers to not change.
|
||||
[c]lear ignores - this reverts all your ignores if you make a mistake.
|
||||
[a]accept/save file - apply all accepted renames and continue on to the next file.
|
||||
|
||||
(return to continue)
|
||||
"""
|
||||
|
||||
# Helper functions
|
||||
|
||||
@ -227,10 +246,11 @@ def rename_in_file(path, in_list, out_list, is_interactive):
|
||||
|
||||
ret = raw_input(_green("Choose: "
|
||||
"[q]uit, "
|
||||
"[h]elp, "
|
||||
"[s]kip file, "
|
||||
"[i]gnore lines, "
|
||||
"[c]lear ignores, "
|
||||
"[a]ccept/save file: "))
|
||||
"[a]ccept/save file: ".lower()))
|
||||
|
||||
if ret == "s":
|
||||
# skip file entirely
|
||||
@ -252,8 +272,10 @@ def rename_in_file(path, in_list, out_list, is_interactive):
|
||||
print(" ... Saved file %s" % path)
|
||||
return
|
||||
elif ret == "q":
|
||||
print("Quit renaming.")
|
||||
print("Quit renaming program.")
|
||||
sys.exit()
|
||||
elif ret == "h":
|
||||
raw_input(_HELP_TEXT.format(sources=in_list, targets=out_list))
|
||||
elif ret.startswith("i"):
|
||||
# ignore one or more lines
|
||||
ignores = [int(ind)-1 for ind in ret[1:].split(',') if ind.strip().isdigit()]
|
||||
|
||||
@ -75,6 +75,9 @@ class CommandTest(EvenniaTest):
|
||||
cmdobj.parse()
|
||||
cmdobj.func()
|
||||
cmdobj.at_post_cmd()
|
||||
except InterruptCommand:
|
||||
pass
|
||||
finally:
|
||||
# clean out evtable sugar. We only operate on text-type
|
||||
stored_msg = [args[0] if args and args[0] else kwargs.get("text",utils.to_str(kwargs, force_string=True))
|
||||
for name, args, kwargs in receiver.msg.mock_calls]
|
||||
@ -90,11 +93,8 @@ class CommandTest(EvenniaTest):
|
||||
retval = sep1 + msg.strip() + sep2 + returned_msg + sep3
|
||||
raise AssertionError(retval)
|
||||
else:
|
||||
returned_msg = "\n".join(stored_msg)
|
||||
returned_msg = "\n".join(str(msg) for msg in stored_msg)
|
||||
returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip()
|
||||
except InterruptCommand:
|
||||
pass
|
||||
finally:
|
||||
receiver.msg = old_msg
|
||||
|
||||
return returned_msg
|
||||
|
||||
@ -30,6 +30,8 @@ things you want from here into your game folder and change them there.
|
||||
multiple descriptions for time and season as well as details.
|
||||
* GenderSub (Griatch 2015) - Simple example (only) of storing gender
|
||||
on a character and access it in an emote with a custom marker.
|
||||
* In-game Python (Vincent Le Geoff 2017) - Allow trusted builders to script
|
||||
objects and events using Python from in-game.
|
||||
* Mail (grungies1138 2016) - An in-game mail system for communication.
|
||||
* Menu login (Griatch 2011) - A login system using menus asking
|
||||
for name/password rather than giving them as one command
|
||||
@ -51,6 +53,7 @@ things you want from here into your game folder and change them there.
|
||||
as a start to build from. Has attack/disengage and turn timeouts.
|
||||
* Wilderness (titeuf87 2017) - Make infinitely large wilderness areas
|
||||
with dynamically created locations.
|
||||
* UnixCommand (Vincent Le Geoff 2017) - Add commands with UNIX-style syntax.
|
||||
|
||||
## Contrib packages
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# Evennia event system
|
||||
# Evennia in-game Python system
|
||||
|
||||
Vincent Le Goff 2017
|
||||
|
||||
This contrib adds the system of events in Evennia, allowing immortals (or other trusted builders) to
|
||||
dynamically add features to individual objects. Using events, every immortal or privileged users
|
||||
This contrib adds the system of in-game Python in Evennia, allowing immortals (or other trusted builders) to
|
||||
dynamically add features to individual objects. Using custom Python set in-game, every immortal or privileged users
|
||||
could have a specific room, exit, character, object or something else behave differently from its
|
||||
"cousins". For these familiar with the use of softcode in MU`*`, like SMAUG MudProgs, the ability to
|
||||
add arbitrary behavior to individual objects is a step toward freedom. Keep in mind, however, the
|
||||
@ -11,26 +11,26 @@ warning below, and read it carefully before the rest of the documentation.
|
||||
|
||||
## A WARNING REGARDING SECURITY
|
||||
|
||||
Evennia's event system will run arbitrary Python code without much restriction. Such a system is as
|
||||
Evennia's in-game Python system will run arbitrary Python code without much restriction. Such a system is as
|
||||
powerful as potentially dangerous, and you will have to keep in mind these points before deciding to
|
||||
install it:
|
||||
|
||||
1. Untrusted people can run Python code on your game server with this system. Be careful about who
|
||||
can use this system (see the permissions below).
|
||||
2. You can do all of this in Python outside the game. The event system is not to replace all your
|
||||
2. You can do all of this in Python outside the game. The in-game Python system is not to replace all your
|
||||
game feature.
|
||||
|
||||
## Basic structure and vocabulary
|
||||
|
||||
- At the basis of the event system are **events**. An **event** defines the context in which we
|
||||
would like to call some arbitrary code. For instance, one event is defined on exits and will fire
|
||||
every time a character traverses through this exit. Events are described on a
|
||||
[typeclass](https://github.com/evennia/evennia/wiki/Typeclasses) (like
|
||||
[exits](https://github.com/evennia/evennia/wiki/Objects#exits) in our example). All objects
|
||||
inheriting from this typeclass will have access to this event.
|
||||
- At the basis of the in-game Python system are **events**. An **event** defines the context in which we
|
||||
would like to call some arbitrary code. For instance, one event is
|
||||
defined on exits and will fire every time a character traverses through this exit. Events are described
|
||||
on a [typeclass](https://github.com/evennia/evennia/wiki/Typeclasses) (like
|
||||
[exits](https://github.com/evennia/evennia/wiki/Objects#exits) in our example). All objects inheriting
|
||||
from this typeclass will have access to this event.
|
||||
- **Callbacks** can be set on individual objects, on events defined in code. These **callbacks**
|
||||
can contain arbitrary code and describe a specific behavior for an object. When the event fires,
|
||||
all callbacks connected to this object's event are executed.
|
||||
all callbacks connected to this object's event are executed.
|
||||
|
||||
To see the system in context, when an object is picked up (using the default `get` command), a
|
||||
specific event is fired:
|
||||
@ -41,10 +41,10 @@ specific event is fired:
|
||||
the "get" event on this object.
|
||||
4. All callbacks tied to this object's "get" event will be executed in order. These callbacks act
|
||||
as functions containing Python code that you can write in-game, using specific variables that
|
||||
will be listed when you edit the callback itself.
|
||||
will be listed when you edit the callback itself.
|
||||
5. In individual callbacks, you can add multiple lines of Python code that will be fired at this
|
||||
point. In this example, the `character` variable will contain the character who has picked up
|
||||
the object, while `obj` will contain the object that was picked up.
|
||||
the object, while `obj` will contain the object that was picked up.
|
||||
|
||||
Following this example, if you create a callback "get" on the object "a sword", and put in it:
|
||||
|
||||
@ -59,11 +59,11 @@ When you pick up this object you should see something like:
|
||||
|
||||
## Installation
|
||||
|
||||
Being in a separate contrib, the event system isn't installed by default. You need to do it
|
||||
Being in a separate contrib, the in-game Python system isn't installed by default. You need to do it
|
||||
manually, following these steps:
|
||||
|
||||
1. Launch the main script (important!):
|
||||
```@py evennia.create_script("evennia.contrib.events.scripts.EventHandler")```
|
||||
```@py evennia.create_script("evennia.contrib.ingame_python.scripts.EventHandler")```
|
||||
2. Set the permissions (optional):
|
||||
- `EVENTS_WITH_VALIDATION`: a group that can edit callbacks, but will need approval (default to
|
||||
`None`).
|
||||
@ -73,23 +73,23 @@ manually, following these steps:
|
||||
- `EVENTS_CALENDAR`: type of the calendar to be used (either `None`, `"standard"` or `"custom"`,
|
||||
default to `None`).
|
||||
3. Add the `@call` command.
|
||||
4. Inherit from the custom typeclasses of the event system.
|
||||
- `evennia.contrib.events.typeclasses.EventCharacter`: to replace `DefaultCharacter`.
|
||||
- `evennia.contrib.events.typeclasses.EventExit`: to replace `DefaultExit`.
|
||||
- `evennia.contrib.events.typeclasses.EventObject`: to replace `DefaultObject`.
|
||||
- `evennia.contrib.events.typeclasses.EventRoom`: to replace `DefaultRoom`.
|
||||
4. Inherit from the custom typeclasses of the in-game Python system.
|
||||
- `evennia.contrib.ingame_python.typeclasses.EventCharacter`: to replace `DefaultCharacter`.
|
||||
- `evennia.contrib.ingame_python.typeclasses.EventExit`: to replace `DefaultExit`.
|
||||
- `evennia.contrib.ingame_python.typeclasses.EventObject`: to replace `DefaultObject`.
|
||||
- `evennia.contrib.ingame_python.typeclasses.EventRoom`: to replace `DefaultRoom`.
|
||||
|
||||
The following sections describe in details each step of the installation.
|
||||
|
||||
> Note: If you were to start the game without having started the main script (such as when
|
||||
> Note: If you were to start the game without having started the main script (such as when
|
||||
resetting your database) you will most likely face a traceback when logging in, telling you
|
||||
that a 'callback' property is not defined. After performing step `1` the error will go away.
|
||||
that a 'callback' property is not defined. After performing step `1` the error will go away.
|
||||
|
||||
### Starting the event script
|
||||
|
||||
To start the event script, you only need a single command, using `@py`.
|
||||
|
||||
@py evennia.create_script("evennia.contrib.events.scripts.EventHandler")
|
||||
@py evennia.create_script("evennia.contrib.ingame_python.scripts.EventHandler")
|
||||
|
||||
This command will create a global script (that is, a script independent from any object). This
|
||||
script will hold basic configuration, individual callbacks and so on. You may access it directly,
|
||||
@ -174,7 +174,7 @@ this:
|
||||
|
||||
```python
|
||||
from evennia import default_cmds
|
||||
from evennia.contrib.events.commands import CmdCallback
|
||||
from evennia.contrib.ingame_python.commands import CmdCallback
|
||||
|
||||
class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||
"""
|
||||
@ -194,25 +194,25 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||
|
||||
### Changing parent classes of typeclasses
|
||||
|
||||
Finally, to use the event system, you need to have your typeclasses inherit from the modified event
|
||||
Finally, to use the in-game Python system, you need to have your typeclasses inherit from the modified event
|
||||
classes. For instance, in your `typeclasses/characters.py` module, you should change inheritance
|
||||
like this:
|
||||
|
||||
```python
|
||||
from evennia.contrib.events.typeclasses import EventCharacter
|
||||
from evennia.contrib.ingame_python.typeclasses import EventCharacter
|
||||
|
||||
class Character(EventCharacter):
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
You should do the same thing for your rooms, exits and objects. Note that the event system works by
|
||||
You should do the same thing for your rooms, exits and objects. Note that the in-game Python system works by
|
||||
overriding some hooks. Some of these features might not be accessible in your game if you don't
|
||||
call the parent methods when overriding hooks.
|
||||
|
||||
## Using the `@call` command
|
||||
|
||||
The event system relies, to a great extent, on its `@call` command. Who can execute this command,
|
||||
The in-game Python system relies, to a great extent, on its `@call` command. Who can execute this command,
|
||||
and who can do what with it, will depend on your set of permissions.
|
||||
|
||||
The `@call` command allows to add, edit and delete callbacks on specific objects' events. The event
|
||||
@ -383,7 +383,7 @@ most complex.
|
||||
|
||||
### The eventfuncs
|
||||
|
||||
In order to make development a little easier, the event system provides eventfuncs to be used in
|
||||
In order to make development a little easier, the in-game Python system provides eventfuncs to be used in
|
||||
callbacks themselves. You don't have to use them, they are just shortcuts. An eventfunc is just a
|
||||
simple function that can be used inside of your callback code.
|
||||
|
||||
@ -473,7 +473,7 @@ And if the character Wilfred takes this exit, others in the room will see:
|
||||
|
||||
Wildred falls into a hole in the ground!
|
||||
|
||||
In this case, the event system placed the variable "message" in the callback locals, but will read
|
||||
In this case, the in-game Python system placed the variable "message" in the callback locals, but will read
|
||||
from it when the event has been executed.
|
||||
|
||||
### Callbacks with parameters
|
||||
@ -661,15 +661,15 @@ specific events fired.
|
||||
|
||||
Adding new events should be done in your typeclasses. Events are contained in the `_events` class
|
||||
variable, a dictionary of event names as keys, and tuples to describe these events as values. You
|
||||
also need to register this class, to tell the event system that it contains events to be added to
|
||||
also need to register this class, to tell the in-game Python system that it contains events to be added to
|
||||
this typeclass.
|
||||
|
||||
Here, we want to add a "push" event on objects. In your `typeclasses/objects.py` file, you should
|
||||
write something like:
|
||||
|
||||
```python
|
||||
from evennia.contrib.events.utils import register_events
|
||||
from evennia.contrib.events.typeclasses import EventObject
|
||||
from evennia.contrib.ingame_python.utils import register_events
|
||||
from evennia.contrib.ingame_python.typeclasses import EventObject
|
||||
|
||||
EVENT_PUSH = """
|
||||
A character push the object.
|
||||
@ -692,7 +692,7 @@ class Object(EventObject):
|
||||
}
|
||||
```
|
||||
|
||||
- Line 1-2: we import several things we will need from the event system. Note that we use
|
||||
- Line 1-2: we import several things we will need from the in-game Python system. Note that we use
|
||||
`EventObject` as a parent instead of `DefaultObject`, as explained in the installation.
|
||||
- Line 4-12: we usually define the help of the event in a separate variable, this is more readable,
|
||||
though there's no rule against doing it another way. Usually, the help should contain a short
|
||||
@ -714,7 +714,7 @@ fired.
|
||||
|
||||
### Calling an event in code
|
||||
|
||||
The event system is accessible through a handler on all objects. This handler is named `callbacks`
|
||||
The in-game Python system is accessible through a handler on all objects. This handler is named `callbacks`
|
||||
and can be accessed from any typeclassed object (your character, a room, an exit...). This handler
|
||||
offers several methods to examine and call an event or callback on this object.
|
||||
|
||||
@ -825,7 +825,7 @@ this is out of the scope of this documentation).
|
||||
The "say" command uses phrase parameters (you can set a "say" callback to fires if a phrase
|
||||
contains one specific word).
|
||||
|
||||
In both cases, you need to import a function from `evennia.contrib.events.utils` and use it as third
|
||||
In both cases, you need to import a function from `evennia.contrib.ingame_python.utils` and use it as third
|
||||
parameter in your event definition.
|
||||
|
||||
- `keyword_event` should be used for keyword parameters.
|
||||
@ -834,7 +834,7 @@ parameter in your event definition.
|
||||
For example, here is the definition of the "say" event:
|
||||
|
||||
```python
|
||||
from evennia.contrib.events.utils import register_events, phrase_event
|
||||
from evennia.contrib.ingame_python.utils import register_events, phrase_event
|
||||
# ...
|
||||
@register_events
|
||||
class SomeTypeclass:
|
||||
@ -865,5 +865,5 @@ The best way to do this is to use a custom setting, in your setting file
|
||||
EVENTS_DISABLED = True
|
||||
```
|
||||
|
||||
The event system will still be accessible (you will have access to the `@call` command, to debug),
|
||||
The in-game Python system will still be accessible (you will have access to the `@call` command, to debug),
|
||||
but no event will be called automatically.
|
||||
@ -7,9 +7,9 @@ from collections import namedtuple
|
||||
class CallbackHandler(object):
|
||||
|
||||
"""
|
||||
The event handler for a specific object.
|
||||
The callback handler for a specific object.
|
||||
|
||||
The script that contains all events will be reached through this
|
||||
The script that contains all callbacks will be reached through this
|
||||
handler. This handler is therefore a shortcut to be used by
|
||||
developers. This handler (accessible through `obj.callbacks`) is a
|
||||
shortcut to manipulating callbacks within this object, getting,
|
||||
@ -1,5 +1,5 @@
|
||||
"""
|
||||
Module containing the commands of the callback system.
|
||||
Module containing the commands of the in-game Python system.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
@ -10,7 +10,7 @@ from evennia.utils.ansi import raw
|
||||
from evennia.utils.eveditor import EvEditor
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.utils.utils import class_from_module, time_format
|
||||
from evennia.contrib.events.utils import get_event_handler
|
||||
from evennia.contrib.ingame_python.utils import get_event_handler
|
||||
|
||||
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||
|
||||
@ -358,9 +358,6 @@ class CmdCallback(COMMAND_DEFAULT_CLASS):
|
||||
|
||||
# Open the editor
|
||||
callback = dict(callback)
|
||||
callback["obj"] = obj
|
||||
callback["name"] = callback_name
|
||||
callback["number"] = number
|
||||
self.caller.db._callback = callback
|
||||
EvEditor(self.caller, loadfunc=_ev_load, savefunc=_ev_save,
|
||||
quitfunc=_ev_quit, key="Callback {} of {}".format(
|
||||
@ -6,14 +6,14 @@ Eventfuncs are just Python functions that can be used inside of calllbacks.
|
||||
"""
|
||||
|
||||
from evennia import ObjectDB, ScriptDB
|
||||
from evennia.contrib.events.utils import InterruptEvent
|
||||
from evennia.contrib.ingame_python.utils import InterruptEvent
|
||||
|
||||
def deny():
|
||||
"""
|
||||
Deny, that is stop, the event here.
|
||||
Deny, that is stop, the callback here.
|
||||
|
||||
Notes:
|
||||
This function will raise an exception to terminate the event
|
||||
This function will raise an exception to terminate the callback
|
||||
in a controlled way. If you use this function in an event called
|
||||
prior to a command, the command will be cancelled as well. Good
|
||||
situations to use the `deny()` function are in events that begins
|
||||
@ -1,5 +1,5 @@
|
||||
"""
|
||||
Scripts for the event system.
|
||||
Scripts for the in-game Python system.
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
@ -15,8 +15,8 @@ from evennia.utils.ansi import raw
|
||||
from evennia.utils.create import create_channel
|
||||
from evennia.utils.dbserialize import dbserialize
|
||||
from evennia.utils.utils import all_from_module, delay, pypath_to_realpath
|
||||
from evennia.contrib.events.callbackhandler import CallbackHandler
|
||||
from evennia.contrib.events.utils import get_next_wait, EVENTS, InterruptEvent
|
||||
from evennia.contrib.ingame_python.callbackhandler import CallbackHandler
|
||||
from evennia.contrib.ingame_python.utils import get_next_wait, EVENTS, InterruptEvent
|
||||
|
||||
# Constants
|
||||
RE_LINE_ERROR = re.compile(r'^ File "\<string\>", line (\d+)')
|
||||
@ -29,7 +29,7 @@ class EventHandler(DefaultScript):
|
||||
This script shouldn't be created more than once. It contains
|
||||
event (in a non-persistent attribute) and callbacks (in a
|
||||
persistent attribute). The script method would help adding,
|
||||
editing and deleting these events.
|
||||
editing and deleting these events and callbacks.
|
||||
|
||||
"""
|
||||
|
||||
@ -68,7 +68,7 @@ class EventHandler(DefaultScript):
|
||||
# Generate locals
|
||||
self.ndb.current_locals = {}
|
||||
self.ndb.fresh_locals = {}
|
||||
addresses = ["evennia.contrib.events.eventfuncs"]
|
||||
addresses = ["evennia.contrib.ingame_python.eventfuncs"]
|
||||
addresses.extend(getattr(settings, "EVENTFUNCS_LOCATIONS", ["world.eventfuncs"]))
|
||||
for address in addresses:
|
||||
if pypath_to_realpath(address):
|
||||
@ -85,7 +85,7 @@ class EventHandler(DefaultScript):
|
||||
delay(seconds, complete_task, task_id)
|
||||
|
||||
# Place the script in the CallbackHandler
|
||||
from evennia.contrib.events import typeclasses
|
||||
from evennia.contrib.ingame_python import typeclasses
|
||||
CallbackHandler.script = self
|
||||
DefaultObject.callbacks = typeclasses.EventObject.callbacks
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
"""
|
||||
Module containing the test cases for the event system.
|
||||
Module containing the test cases for the in-game Python system.
|
||||
"""
|
||||
|
||||
from mock import Mock
|
||||
@ -12,8 +12,8 @@ from evennia.objects.objects import ExitCommand
|
||||
from evennia.utils import ansi, utils
|
||||
from evennia.utils.create import create_object, create_script
|
||||
from evennia.utils.test_resources import EvenniaTest
|
||||
from evennia.contrib.events.commands import CmdCallback
|
||||
from evennia.contrib.events.callbackhandler import CallbackHandler
|
||||
from evennia.contrib.ingame_python.commands import CmdCallback
|
||||
from evennia.contrib.ingame_python.callbackhandler import CallbackHandler
|
||||
|
||||
# Force settings
|
||||
settings.EVENTS_CALENDAR = "standard"
|
||||
@ -31,18 +31,18 @@ class TestEventHandler(EvenniaTest):
|
||||
"""Create the event handler."""
|
||||
super(TestEventHandler, self).setUp()
|
||||
self.handler = create_script(
|
||||
"evennia.contrib.events.scripts.EventHandler")
|
||||
"evennia.contrib.ingame_python.scripts.EventHandler")
|
||||
|
||||
# Copy old events if necessary
|
||||
if OLD_EVENTS:
|
||||
self.handler.ndb.events = dict(OLD_EVENTS)
|
||||
|
||||
# Alter typeclasses
|
||||
self.char1.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
||||
self.char2.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
||||
self.room1.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
||||
self.room2.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
||||
self.exit.swap_typeclass("evennia.contrib.events.typeclasses.EventExit")
|
||||
self.char1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||
self.char2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||
self.room1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||
self.room2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||
self.exit.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventExit")
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop the event handler."""
|
||||
@ -249,18 +249,18 @@ class TestCmdCallback(CommandTest):
|
||||
"""Create the callback handler."""
|
||||
super(TestCmdCallback, self).setUp()
|
||||
self.handler = create_script(
|
||||
"evennia.contrib.events.scripts.EventHandler")
|
||||
"evennia.contrib.ingame_python.scripts.EventHandler")
|
||||
|
||||
# Copy old events if necessary
|
||||
if OLD_EVENTS:
|
||||
self.handler.ndb.events = dict(OLD_EVENTS)
|
||||
|
||||
# Alter typeclasses
|
||||
self.char1.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
||||
self.char2.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
||||
self.room1.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
||||
self.room2.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
||||
self.exit.swap_typeclass("evennia.contrib.events.typeclasses.EventExit")
|
||||
self.char1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||
self.char2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||
self.room1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||
self.room2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||
self.exit.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventExit")
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop the callback handler."""
|
||||
@ -268,7 +268,7 @@ class TestCmdCallback(CommandTest):
|
||||
OLD_EVENTS.update(self.handler.ndb.events)
|
||||
self.handler.stop()
|
||||
for script in ScriptDB.objects.filter(
|
||||
db_typeclass_path="evennia.contrib.events.scripts.TimeEventScript"):
|
||||
db_typeclass_path="evennia.contrib.ingame_python.scripts.TimeEventScript"):
|
||||
script.stop()
|
||||
|
||||
CallbackHandler.script = None
|
||||
@ -414,18 +414,18 @@ class TestDefaultCallbacks(CommandTest):
|
||||
"""Create the callback handler."""
|
||||
super(TestDefaultCallbacks, self).setUp()
|
||||
self.handler = create_script(
|
||||
"evennia.contrib.events.scripts.EventHandler")
|
||||
"evennia.contrib.ingame_python.scripts.EventHandler")
|
||||
|
||||
# Copy old events if necessary
|
||||
if OLD_EVENTS:
|
||||
self.handler.ndb.events = dict(OLD_EVENTS)
|
||||
|
||||
# Alter typeclasses
|
||||
self.char1.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
||||
self.char2.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
||||
self.room1.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
||||
self.room2.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
||||
self.exit.swap_typeclass("evennia.contrib.events.typeclasses.EventExit")
|
||||
self.char1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||
self.char2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||
self.room1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||
self.room2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||
self.exit.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventExit")
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop the callback handler."""
|
||||
@ -1,5 +1,5 @@
|
||||
"""
|
||||
Typeclasses for the event system.
|
||||
Typeclasses for the in-game Python system.
|
||||
|
||||
To use thm, one should inherit from these classes (EventObject,
|
||||
EventRoom, EventCharacter and EventExit).
|
||||
@ -9,8 +9,8 @@ EventRoom, EventCharacter and EventExit).
|
||||
from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
|
||||
from evennia import ScriptDB
|
||||
from evennia.utils.utils import delay, inherits_from, lazy_property
|
||||
from evennia.contrib.events.callbackhandler import CallbackHandler
|
||||
from evennia.contrib.events.utils import register_events, time_event, phrase_event
|
||||
from evennia.contrib.ingame_python.callbackhandler import CallbackHandler
|
||||
from evennia.contrib.ingame_python.utils import register_events, time_event, phrase_event
|
||||
|
||||
# Character help
|
||||
CHARACTER_CAN_DELETE = """
|
||||
@ -121,7 +121,7 @@ parameters that should be present, as separate words, in the
|
||||
spoken phrase. For instance, you can set an event tthat would
|
||||
fire if the phrase spoken by the character contains "menu" or
|
||||
"dinner" or "lunch":
|
||||
@event/add ... = say menu, dinner, lunch
|
||||
@call/add ... = say menu, dinner, lunch
|
||||
Then if one of the words is present in what the character says,
|
||||
this event will fire.
|
||||
|
||||
@ -135,12 +135,12 @@ CHARACTER_TIME = """
|
||||
A repeated event to be called regularly.
|
||||
This event is scheduled to repeat at different times, specified
|
||||
as parameters. You can set it to run every day at 8:00 AM (game
|
||||
time). You have to specify the time as an argument to @event/add, like:
|
||||
@event/add here = time 8:00
|
||||
time). You have to specify the time as an argument to @call/add, like:
|
||||
@call/add here = time 8:00
|
||||
The parameter (8:00 here) must be a suite of digits separated by
|
||||
spaces, colons or dashes. Keep it as close from a recognizable
|
||||
date format, like this:
|
||||
@event/add here = time 06-15 12:20
|
||||
@call/add here = time 06-15 12:20
|
||||
This event will fire every year on June the 15th at 12 PM (still
|
||||
game time). Units have to be specified depending on your set calendar
|
||||
(ask a developer for more details).
|
||||
@ -461,12 +461,12 @@ EXIT_TIME = """
|
||||
A repeated event to be called regularly.
|
||||
This event is scheduled to repeat at different times, specified
|
||||
as parameters. You can set it to run every day at 8:00 AM (game
|
||||
time). You have to specify the time as an argument to @event/add, like:
|
||||
@event/add north = time 8:00
|
||||
time). You have to specify the time as an argument to @call/add, like:
|
||||
@call/add north = time 8:00
|
||||
The parameter (8:00 here) must be a suite of digits separated by
|
||||
spaces, colons or dashes. Keep it as close from a recognizable
|
||||
date format, like this:
|
||||
@event/add south = time 06-15 12:20
|
||||
@call/add south = time 06-15 12:20
|
||||
This event will fire every year on June the 15th at 12 PM (still
|
||||
game time). Units have to be specified depending on your set calendar
|
||||
(ask a developer for more details).
|
||||
@ -559,12 +559,12 @@ OBJECT_TIME = """
|
||||
A repeated event to be called regularly.
|
||||
This event is scheduled to repeat at different times, specified
|
||||
as parameters. You can set it to run every day at 8:00 AM (game
|
||||
time). You have to specify the time as an argument to @event/add, like:
|
||||
@event/add here = time 8:00
|
||||
time). You have to specify the time as an argument to @call/add, like:
|
||||
@call/add here = time 8:00
|
||||
The parameter (8:00 here) must be a suite of digits separated by
|
||||
spaces, colons or dashes. Keep it as close from a recognizable
|
||||
date format, like this:
|
||||
@event/add here = time 06-15 12:20
|
||||
@call/add here = time 06-15 12:20
|
||||
This event will fire every year on June the 15th at 12 PM (still
|
||||
game time). Units have to be specified depending on your set calendar
|
||||
(ask a developer for more details).
|
||||
@ -702,7 +702,7 @@ specify a list of keywords as parameters that should be present,
|
||||
as separate words, in the spoken phrase. For instance, you can
|
||||
set an event tthat would fire if the phrase spoken by the character
|
||||
contains "menu" or "dinner" or "lunch":
|
||||
@event/add ... = say menu, dinner, lunch
|
||||
@call/add ... = say menu, dinner, lunch
|
||||
Then if one of the words is present in what the character says,
|
||||
this event will fire.
|
||||
|
||||
@ -716,12 +716,12 @@ ROOM_TIME = """
|
||||
A repeated event to be called regularly.
|
||||
This event is scheduled to repeat at different times, specified
|
||||
as parameters. You can set it to run every day at 8:00 AM (game
|
||||
time). You have to specify the time as an argument to @event/add, like:
|
||||
@event/add here = time 8:00
|
||||
time). You have to specify the time as an argument to @call/add, like:
|
||||
@call/add here = time 8:00
|
||||
The parameter (8:00 here) must be a suite of digits separated by
|
||||
spaces, colons or dashes. Keep it as close from a recognizable
|
||||
date format, like this:
|
||||
@event/add here = time 06-15 12:20
|
||||
@call/add here = time 06-15 12:20
|
||||
This event will fire every year on June the 15th at 12 PM (still
|
||||
game time). Units have to be specified depending on your set calendar
|
||||
(ask a developer for more details).
|
||||
@ -166,7 +166,7 @@ def time_event(obj, event_name, number, parameters):
|
||||
|
||||
"""
|
||||
seconds, usual, key = get_next_wait(parameters)
|
||||
script = create_script("evennia.contrib.events.scripts.TimeEventScript", interval=seconds, obj=obj)
|
||||
script = create_script("evennia.contrib.ingame_python.scripts.TimeEventScript", interval=seconds, obj=obj)
|
||||
script.key = key
|
||||
script.desc = "event on {}".format(key)
|
||||
script.db.time_format = parameters
|
||||
@ -451,13 +451,13 @@ class TestChargen(CommandTest):
|
||||
self.assertTrue(self.account.db._character_dbrefs)
|
||||
self.call(chargen.CmdOOCLook(), "", "You, TestAccount, are an OOC ghost without form.",caller=self.account)
|
||||
self.call(chargen.CmdOOCLook(), "testchar", "testchar(", caller=self.account)
|
||||
|
||||
|
||||
# Testing clothing contrib
|
||||
from evennia.contrib import clothing
|
||||
from evennia.objects.objects import DefaultRoom
|
||||
|
||||
class TestClothingCmd(CommandTest):
|
||||
|
||||
|
||||
def test_clothingcommands(self):
|
||||
wearer = create_object(clothing.ClothedCharacter, key="Wearer")
|
||||
friend = create_object(clothing.ClothedCharacter, key="Friend")
|
||||
@ -500,7 +500,7 @@ class TestClothingCmd(CommandTest):
|
||||
self.call(clothing.CmdInventory(), "", "You are not carrying or wearing anything.", caller=wearer)
|
||||
|
||||
class TestClothingFunc(EvenniaTest):
|
||||
|
||||
|
||||
def test_clothingfunctions(self):
|
||||
wearer = create_object(clothing.ClothedCharacter, key="Wearer")
|
||||
room = create_object(DefaultRoom, key="room")
|
||||
@ -520,28 +520,28 @@ class TestClothingFunc(EvenniaTest):
|
||||
|
||||
test_hat.wear(wearer, 'on the head')
|
||||
self.assertEqual(test_hat.db.worn, 'on the head')
|
||||
|
||||
|
||||
test_hat.remove(wearer)
|
||||
self.assertEqual(test_hat.db.worn, False)
|
||||
|
||||
|
||||
test_hat.worn = True
|
||||
test_hat.at_get(wearer)
|
||||
self.assertEqual(test_hat.db.worn, False)
|
||||
|
||||
|
||||
clothes_list = [test_shirt, test_hat, test_pants]
|
||||
self.assertEqual(clothing.order_clothes_list(clothes_list), [test_hat, test_shirt, test_pants])
|
||||
|
||||
|
||||
test_hat.wear(wearer, True)
|
||||
test_pants.wear(wearer, True)
|
||||
self.assertEqual(clothing.get_worn_clothes(wearer), [test_hat, test_pants])
|
||||
|
||||
self.assertEqual(clothing.clothing_type_count(clothes_list), {'hat':1, 'top':1, 'bottom':1})
|
||||
|
||||
self.assertEqual(clothing.single_type_count(clothes_list, 'hat'), 1)
|
||||
|
||||
|
||||
|
||||
|
||||
self.assertEqual(clothing.clothing_type_count(clothes_list), {'hat':1, 'top':1, 'bottom':1})
|
||||
|
||||
self.assertEqual(clothing.single_type_count(clothes_list, 'hat'), 1)
|
||||
|
||||
|
||||
|
||||
|
||||
# Testing custom_gametime
|
||||
from evennia.contrib import custom_gametime
|
||||
|
||||
@ -849,7 +849,7 @@ from evennia.contrib import turnbattle
|
||||
from evennia.objects.objects import DefaultRoom
|
||||
|
||||
class TestTurnBattleCmd(CommandTest):
|
||||
|
||||
|
||||
# Test combat commands
|
||||
def test_turnbattlecmd(self):
|
||||
self.call(turnbattle.CmdFight(), "", "You can't start a fight if you've been defeated!")
|
||||
@ -857,9 +857,9 @@ class TestTurnBattleCmd(CommandTest):
|
||||
self.call(turnbattle.CmdPass(), "", "You can only do that in combat. (see: help fight)")
|
||||
self.call(turnbattle.CmdDisengage(), "", "You can only do that in combat. (see: help fight)")
|
||||
self.call(turnbattle.CmdRest(), "", "Char rests to recover HP.")
|
||||
|
||||
|
||||
class TestTurnBattleFunc(EvenniaTest):
|
||||
|
||||
|
||||
# Test combat functions
|
||||
def test_turnbattlefunc(self):
|
||||
attacker = create_object(turnbattle.BattleCharacter, key="Attacker")
|
||||
@ -936,3 +936,51 @@ class TestTurnBattleFunc(EvenniaTest):
|
||||
self.assertTrue(turnhandler.db.fighters == [joiner, attacker, defender])
|
||||
# Remove the script at the end
|
||||
turnhandler.stop()
|
||||
|
||||
|
||||
# Test of the unixcommand module
|
||||
|
||||
from evennia.contrib.unixcommand import UnixCommand
|
||||
|
||||
class CmdDummy(UnixCommand):
|
||||
|
||||
"""A dummy UnixCommand."""
|
||||
|
||||
key = "dummy"
|
||||
|
||||
def init_parser(self):
|
||||
"""Fill out options."""
|
||||
self.parser.add_argument("nb1", type=int, help="the first number")
|
||||
self.parser.add_argument("nb2", type=int, help="the second number")
|
||||
self.parser.add_argument("-v", "--verbose", action="store_true")
|
||||
|
||||
def func(self):
|
||||
nb1 = self.opts.nb1
|
||||
nb2 = self.opts.nb2
|
||||
result = nb1 * nb2
|
||||
verbose = self.opts.verbose
|
||||
if verbose:
|
||||
self.msg("{} times {} is {}".format(nb1, nb2, result))
|
||||
else:
|
||||
self.msg("{} * {} = {}".format(nb1, nb2, result))
|
||||
|
||||
|
||||
class TestUnixCommand(CommandTest):
|
||||
|
||||
def test_success(self):
|
||||
"""See the command parsing succeed."""
|
||||
self.call(CmdDummy(), "5 10", "5 * 10 = 50")
|
||||
self.call(CmdDummy(), "5 10 -v", "5 times 10 is 50")
|
||||
|
||||
def test_failure(self):
|
||||
"""If not provided with the right info, should fail."""
|
||||
ret = self.call(CmdDummy(), "5")
|
||||
lines = ret.splitlines()
|
||||
self.assertTrue(any(l.startswith("usage:") for l in lines))
|
||||
self.assertTrue(any(l.startswith("dummy: error:") for l in lines))
|
||||
|
||||
# If we specify an incorrect number as parameter
|
||||
ret = self.call(CmdDummy(), "five ten")
|
||||
lines = ret.splitlines()
|
||||
self.assertTrue(any(l.startswith("usage:") for l in lines))
|
||||
self.assertTrue(any(l.startswith("dummy: error:") for l in lines))
|
||||
|
||||
294
evennia/contrib/unixcommand.py
Normal file
294
evennia/contrib/unixcommand.py
Normal file
@ -0,0 +1,294 @@
|
||||
"""
|
||||
Unix-like Command style parent
|
||||
|
||||
Evennia contribution, Vincent Le Geoff 2017
|
||||
|
||||
This module contains a command class that allows for unix-style command syntax in-game, using
|
||||
--options, positional arguments and stuff like -n 10 etc similarly to a unix command. It might not
|
||||
the best syntax for the average player but can be really useful for builders when they need to have
|
||||
a single command do many things with many options. It uses the ArgumentParser from Python's standard
|
||||
library under the hood.
|
||||
|
||||
To use, inherit `UnixCommand` from this module from your own commands. You need
|
||||
to override two methods:
|
||||
|
||||
- The `init_parser` method, which adds options to the parser. Note that you should normally
|
||||
*not* override the normal `parse` method when inheriting from `UnixCommand`.
|
||||
- The `func` method, called to execute the command once parsed (like any Command).
|
||||
|
||||
Here's a short example:
|
||||
|
||||
```python
|
||||
class CmdPlant(UnixCommand):
|
||||
|
||||
'''
|
||||
Plant a tree or plant.
|
||||
|
||||
This command is used to plant something in the room you are in.
|
||||
|
||||
Examples:
|
||||
plant orange -a 8
|
||||
plant strawberry --hidden
|
||||
plant potato --hidden --age 5
|
||||
|
||||
'''
|
||||
|
||||
key = "plant"
|
||||
|
||||
def init_parser(self):
|
||||
"Add the arguments to the parser."
|
||||
# 'self.parser' inherits `argparse.ArgumentParser`
|
||||
self.parser.add_argument("key",
|
||||
help="the key of the plant to be planted here")
|
||||
self.parser.add_argument("-a", "--age", type=int,
|
||||
default=1, help="the age of the plant to be planted")
|
||||
self.parser.add_argument("--hidden", action="store_true",
|
||||
help="should the newly-planted plant be hidden to players?")
|
||||
|
||||
def func(self):
|
||||
"func is called only if the parser succeeded."
|
||||
# 'self.opts' contains the parsed options
|
||||
key = self.opts.key
|
||||
age = self.opts.age
|
||||
hidden = self.opts.hidden
|
||||
self.msg("Going to plant '{}', age={}, hidden={}.".format(
|
||||
key, age, hidden))
|
||||
```
|
||||
|
||||
To see the full power of argparse and the types of supported options, visit
|
||||
[the documentation of argparse](https://docs.python.org/2/library/argparse.html).
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import shlex
|
||||
from textwrap import dedent
|
||||
|
||||
from evennia import Command, InterruptCommand
|
||||
from evennia.utils.ansi import raw
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
|
||||
"""An error occurred during parsing."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UnixCommandParser(argparse.ArgumentParser):
|
||||
|
||||
"""A modifier command parser for unix commands.
|
||||
|
||||
This parser is used to replace `argparse.ArgumentParser`. It
|
||||
is aware of the command calling it, and can more easily report to
|
||||
the caller. Some features (like the "brutal exit" of the original
|
||||
parser) are disabled or replaced. This parser is used by UnixCommand
|
||||
and creating one directly isn't recommended nor necessary. Even
|
||||
adding a sub-command will use this replaced parser automatically.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, prog, description="", epilog="", command=None, **kwargs):
|
||||
"""
|
||||
Build a UnixCommandParser with a link to the command using it.
|
||||
|
||||
Args:
|
||||
prog (str): the program name (usually the command key).
|
||||
description (str): a very brief line to show in the usage text.
|
||||
epilog (str): the epilog to show below options.
|
||||
command (Command): the command calling the parser.
|
||||
|
||||
Kwargs:
|
||||
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).
|
||||
|
||||
Note:
|
||||
It's doubtful you would need to create this parser manually.
|
||||
The `UnixCommand` does that automatically. If you create
|
||||
sub-commands, this class will be used.
|
||||
|
||||
"""
|
||||
prog = prog or command.key
|
||||
super(UnixCommandParser, self).__init__(
|
||||
prog=prog, description=description,
|
||||
conflict_handler='resolve', add_help=False, **kwargs)
|
||||
self.command = command
|
||||
self.post_help = epilog
|
||||
|
||||
def n_exit(code=None, msg=None):
|
||||
raise ParseError(msg)
|
||||
|
||||
self.exit = n_exit
|
||||
|
||||
# Replace the -h/--help
|
||||
self.add_argument("-h", "--hel", nargs=0, action=HelpAction,
|
||||
help="display the command help")
|
||||
|
||||
def format_usage(self):
|
||||
"""Return the usage line.
|
||||
|
||||
Note:
|
||||
This method is present to return the raw-escaped usage line,
|
||||
in order to avoid unintentional color codes.
|
||||
|
||||
"""
|
||||
return raw(super(UnixCommandParser, self).format_usage())
|
||||
|
||||
def format_help(self):
|
||||
"""Return the parser help, including its epilog.
|
||||
|
||||
Note:
|
||||
This method is present to return the raw-escaped help,
|
||||
in order to avoid unintentional color codes. Color codes
|
||||
in the epilog (the command docstring) are supported.
|
||||
|
||||
"""
|
||||
autohelp = raw(super(UnixCommandParser, self).format_help())
|
||||
return "\n" + autohelp + "\n" + self.post_help
|
||||
|
||||
def print_usage(self, file=None):
|
||||
"""Print the usage to the caller.
|
||||
|
||||
Args:
|
||||
file (file-object): not used here, the caller is used.
|
||||
|
||||
Note:
|
||||
This method will override `argparse.ArgumentParser`'s in order
|
||||
to not display the help on stdout or stderr, but to the
|
||||
command's caller.
|
||||
|
||||
"""
|
||||
if self.command:
|
||||
self.command.msg(self.format_usage().strip())
|
||||
|
||||
def print_help(self, file=None):
|
||||
"""Print the help to the caller.
|
||||
|
||||
Args:
|
||||
file (file-object): not used here, the caller is used.
|
||||
|
||||
Note:
|
||||
This method will override `argparse.ArgumentParser`'s in order
|
||||
to not display the help on stdout or stderr, but to the
|
||||
command's caller.
|
||||
|
||||
"""
|
||||
if self.command:
|
||||
self.command.msg(self.format_help().strip())
|
||||
|
||||
|
||||
class HelpAction(argparse.Action):
|
||||
|
||||
"""Override the -h/--help action in the default parser.
|
||||
|
||||
Using the default -h/--help will call the exit function in different
|
||||
ways, preventing the entire help message to be provided. Hence
|
||||
this override.
|
||||
|
||||
"""
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
"""If asked for help, display to the caller."""
|
||||
if parser.command:
|
||||
parser.command.msg(parser.format_help().strip())
|
||||
parser.exit(0, "")
|
||||
|
||||
|
||||
class UnixCommand(Command):
|
||||
"""
|
||||
Unix-type commands, supporting short and long options.
|
||||
|
||||
This command syntax uses the Unix-style commands with short options
|
||||
(-X) and long options (--something). The `argparse` module is
|
||||
used to parse the command.
|
||||
|
||||
In order to use it, you should override two methods:
|
||||
- `init_parser`: this method is called when the command is created.
|
||||
It can be used to set options in the parser. `self.parser`
|
||||
contains the `argparse.ArgumentParser`, so you can add arguments
|
||||
here.
|
||||
- `func`: this method is called to execute the command, but after
|
||||
the parser has checked the arguments given to it are valid.
|
||||
You can access the namespace of valid arguments in `self.opts`
|
||||
at this point.
|
||||
|
||||
The help of UnixCommands is derived from the docstring, in a
|
||||
slightly different way than usual: the first line of the docstring
|
||||
is used to represent the program description (the very short
|
||||
line at the top of the help message). The other lines below are
|
||||
used as the program's "epilog", displayed below the options. It
|
||||
means in your docstring, you don't have to write the options.
|
||||
They will be automatically provided by the parser and displayed
|
||||
accordingly. The `argparse` module provides a default '-h' or
|
||||
'--help' option on the command. Typing |whelp commandname|n will
|
||||
display the same as |wcommandname -h|n, though this behavior can
|
||||
be changed.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
The lockhandler works the same as for objects.
|
||||
optional kwargs will be set as properties on the Command at runtime,
|
||||
overloading evential same-named class properties.
|
||||
|
||||
"""
|
||||
super(UnixCommand, self).__init__(**kwargs)
|
||||
|
||||
# Create the empty UnixCommandParser, inheriting argparse.ArgumentParser
|
||||
lines = dedent(self.__doc__.strip("\n")).splitlines()
|
||||
description = lines[0].strip()
|
||||
epilog = "\n".join(lines[1:]).strip()
|
||||
self.parser = UnixCommandParser(None, description, epilog, command=self)
|
||||
|
||||
# Fill the argument parser
|
||||
self.init_parser()
|
||||
|
||||
def init_parser(self):
|
||||
"""
|
||||
Configure the argument parser, adding in options.
|
||||
|
||||
Note:
|
||||
This method is to be overridden in order to add options
|
||||
to the argument parser. Use `self.parser`, which contains
|
||||
the `argparse.ArgumentParser`. You can, for instance,
|
||||
use its `add_argument` method.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def func(self):
|
||||
"""Override to handle the command execution."""
|
||||
pass
|
||||
|
||||
def get_help(self, caller, cmdset):
|
||||
"""
|
||||
Return the help message for this command and this caller.
|
||||
|
||||
Args:
|
||||
caller (Object or Player): the caller asking for help on the command.
|
||||
cmdset (CmdSet): the command set (if you need additional commands).
|
||||
|
||||
Returns:
|
||||
docstring (str): the help text to provide the caller for this command.
|
||||
|
||||
"""
|
||||
return self.parser.format_help()
|
||||
|
||||
def parse(self):
|
||||
"""
|
||||
Process arguments provided in `self.args`.
|
||||
|
||||
Note:
|
||||
You should not override this method. Consider overriding
|
||||
`init_parser` instead.
|
||||
|
||||
"""
|
||||
try:
|
||||
self.opts = self.parser.parse_args(shlex.split(self.args))
|
||||
except ParseError as err:
|
||||
msg = str(err)
|
||||
if msg:
|
||||
self.msg(msg)
|
||||
raise InterruptCommand
|
||||
@ -613,7 +613,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||
obj.msg(text=(outmessage, outkwargs), from_obj=from_obj, **kwargs)
|
||||
|
||||
def move_to(self, destination, quiet=False,
|
||||
emit_to_obj=None, use_destination=True, to_none=False, move_hooks=True):
|
||||
emit_to_obj=None, use_destination=True, to_none=False, move_hooks=True,
|
||||
**kwargs):
|
||||
"""
|
||||
Moves this object to a new location.
|
||||
|
||||
@ -634,6 +635,9 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||
(at_before/after_move etc) with quiet=True, this is as quiet a move
|
||||
as can be done.
|
||||
|
||||
Kwargs:
|
||||
Passed on to announce_move_to and announce_move_from hooks.
|
||||
|
||||
Returns:
|
||||
result (bool): True/False depending on if there were problems with the move.
|
||||
This method may also return various error messages to the
|
||||
@ -699,7 +703,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||
if not quiet:
|
||||
# tell the old room we are leaving
|
||||
try:
|
||||
self.announce_move_from(destination)
|
||||
self.announce_move_from(destination, **kwargs)
|
||||
except Exception as err:
|
||||
logerr(errtxt % "at_announce_move()", err)
|
||||
return False
|
||||
@ -714,7 +718,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||
if not quiet:
|
||||
# Tell the new room we are there.
|
||||
try:
|
||||
self.announce_move_to(source_location)
|
||||
self.announce_move_to(source_location, **kwargs)
|
||||
except Exception as err:
|
||||
logerr(errtxt % "announce_move_to()", err)
|
||||
return False
|
||||
|
||||
@ -220,7 +220,7 @@ class AttributeHandler(object):
|
||||
def _fullcache(self):
|
||||
"""Cache all attributes of this object"""
|
||||
query = {"%s__id" % self._model: self._objid,
|
||||
"attribute__db_model": self._model,
|
||||
"attribute__db_model__iexact": self._model,
|
||||
"attribute__db_attrtype": self._attrtype}
|
||||
attrs = [
|
||||
conn.attribute for conn in getattr(
|
||||
@ -278,7 +278,7 @@ class AttributeHandler(object):
|
||||
return [] # no such attribute: return an empty list
|
||||
else:
|
||||
query = {"%s__id" % self._model: self._objid,
|
||||
"attribute__db_model": self._model,
|
||||
"attribute__db_model__iexact": self._model,
|
||||
"attribute__db_attrtype": self._attrtype,
|
||||
"attribute__db_key__iexact": key.lower(),
|
||||
"attribute__db_category__iexact": category.lower() if category else None}
|
||||
@ -303,7 +303,7 @@ class AttributeHandler(object):
|
||||
else:
|
||||
# we have to query to make this category up-date in the cache
|
||||
query = {"%s__id" % self._model: self._objid,
|
||||
"attribute__db_model": self._model,
|
||||
"attribute__db_model__iexact": self._model,
|
||||
"attribute__db_attrtype": self._attrtype,
|
||||
"attribute__db_category__iexact": category.lower() if category else None}
|
||||
attrs = [conn.attribute for conn
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user