Add help entry to project_rename utility.
Also merge in latest master changes to devel branch.
This commit is contained in:
commit
6d846c6c82
16 changed files with 492 additions and 124 deletions
|
|
@ -25,6 +25,25 @@ FAKE_MODE = False
|
||||||
# if these words are longer than output word, retain given case
|
# if these words are longer than output word, retain given case
|
||||||
CASE_WORD_EXCEPTIONS = ('an', )
|
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
|
# Helper functions
|
||||||
|
|
||||||
|
|
@ -227,10 +246,11 @@ def rename_in_file(path, in_list, out_list, is_interactive):
|
||||||
|
|
||||||
ret = raw_input(_green("Choose: "
|
ret = raw_input(_green("Choose: "
|
||||||
"[q]uit, "
|
"[q]uit, "
|
||||||
|
"[h]elp, "
|
||||||
"[s]kip file, "
|
"[s]kip file, "
|
||||||
"[i]gnore lines, "
|
"[i]gnore lines, "
|
||||||
"[c]lear ignores, "
|
"[c]lear ignores, "
|
||||||
"[a]ccept/save file: "))
|
"[a]ccept/save file: ".lower()))
|
||||||
|
|
||||||
if ret == "s":
|
if ret == "s":
|
||||||
# skip file entirely
|
# skip file entirely
|
||||||
|
|
@ -252,8 +272,10 @@ def rename_in_file(path, in_list, out_list, is_interactive):
|
||||||
print(" ... Saved file %s" % path)
|
print(" ... Saved file %s" % path)
|
||||||
return
|
return
|
||||||
elif ret == "q":
|
elif ret == "q":
|
||||||
print("Quit renaming.")
|
print("Quit renaming program.")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
elif ret == "h":
|
||||||
|
raw_input(_HELP_TEXT.format(sources=in_list, targets=out_list))
|
||||||
elif ret.startswith("i"):
|
elif ret.startswith("i"):
|
||||||
# ignore one or more lines
|
# ignore one or more lines
|
||||||
ignores = [int(ind)-1 for ind in ret[1:].split(',') if ind.strip().isdigit()]
|
ignores = [int(ind)-1 for ind in ret[1:].split(',') if ind.strip().isdigit()]
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,9 @@ class CommandTest(EvenniaTest):
|
||||||
cmdobj.parse()
|
cmdobj.parse()
|
||||||
cmdobj.func()
|
cmdobj.func()
|
||||||
cmdobj.at_post_cmd()
|
cmdobj.at_post_cmd()
|
||||||
|
except InterruptCommand:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
# clean out evtable sugar. We only operate on text-type
|
# 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))
|
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]
|
for name, args, kwargs in receiver.msg.mock_calls]
|
||||||
|
|
@ -90,11 +93,8 @@ class CommandTest(EvenniaTest):
|
||||||
retval = sep1 + msg.strip() + sep2 + returned_msg + sep3
|
retval = sep1 + msg.strip() + sep2 + returned_msg + sep3
|
||||||
raise AssertionError(retval)
|
raise AssertionError(retval)
|
||||||
else:
|
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()
|
returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip()
|
||||||
except InterruptCommand:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
receiver.msg = old_msg
|
receiver.msg = old_msg
|
||||||
|
|
||||||
return returned_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.
|
multiple descriptions for time and season as well as details.
|
||||||
* GenderSub (Griatch 2015) - Simple example (only) of storing gender
|
* GenderSub (Griatch 2015) - Simple example (only) of storing gender
|
||||||
on a character and access it in an emote with a custom marker.
|
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.
|
* Mail (grungies1138 2016) - An in-game mail system for communication.
|
||||||
* Menu login (Griatch 2011) - A login system using menus asking
|
* Menu login (Griatch 2011) - A login system using menus asking
|
||||||
for name/password rather than giving them as one command
|
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.
|
as a start to build from. Has attack/disengage and turn timeouts.
|
||||||
* Wilderness (titeuf87 2017) - Make infinitely large wilderness areas
|
* Wilderness (titeuf87 2017) - Make infinitely large wilderness areas
|
||||||
with dynamically created locations.
|
with dynamically created locations.
|
||||||
|
* UnixCommand (Vincent Le Geoff 2017) - Add commands with UNIX-style syntax.
|
||||||
|
|
||||||
## Contrib packages
|
## Contrib packages
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
# Evennia event system
|
# Evennia in-game Python system
|
||||||
|
|
||||||
Vincent Le Goff 2017
|
Vincent Le Goff 2017
|
||||||
|
|
||||||
This contrib adds the system of events in Evennia, allowing immortals (or other trusted builders) to
|
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 events, every immortal or privileged users
|
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
|
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
|
"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
|
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
|
## 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
|
powerful as potentially dangerous, and you will have to keep in mind these points before deciding to
|
||||||
install it:
|
install it:
|
||||||
|
|
||||||
1. Untrusted people can run Python code on your game server with this system. Be careful about who
|
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).
|
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.
|
game feature.
|
||||||
|
|
||||||
## Basic structure and vocabulary
|
## Basic structure and vocabulary
|
||||||
|
|
||||||
- At the basis of the event system are **events**. An **event** defines the context in which we
|
- 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
|
would like to call some arbitrary code. For instance, one event is
|
||||||
every time a character traverses through this exit. Events are described on a
|
defined on exits and will fire every time a character traverses through this exit. Events are described
|
||||||
[typeclass](https://github.com/evennia/evennia/wiki/Typeclasses) (like
|
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
|
[exits](https://github.com/evennia/evennia/wiki/Objects#exits) in our example). All objects inheriting
|
||||||
inheriting from this typeclass will have access to this event.
|
from this typeclass will have access to this event.
|
||||||
- **Callbacks** can be set on individual objects, on events defined in code. These **callbacks**
|
- **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,
|
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
|
To see the system in context, when an object is picked up (using the default `get` command), a
|
||||||
specific event is fired:
|
specific event is fired:
|
||||||
|
|
@ -41,10 +41,10 @@ specific event is fired:
|
||||||
the "get" event on this object.
|
the "get" event on this object.
|
||||||
4. All callbacks tied to this object's "get" event will be executed in order. These callbacks act
|
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
|
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
|
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
|
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:
|
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
|
## 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:
|
manually, following these steps:
|
||||||
|
|
||||||
1. Launch the main script (important!):
|
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):
|
2. Set the permissions (optional):
|
||||||
- `EVENTS_WITH_VALIDATION`: a group that can edit callbacks, but will need approval (default to
|
- `EVENTS_WITH_VALIDATION`: a group that can edit callbacks, but will need approval (default to
|
||||||
`None`).
|
`None`).
|
||||||
|
|
@ -73,11 +73,11 @@ manually, following these steps:
|
||||||
- `EVENTS_CALENDAR`: type of the calendar to be used (either `None`, `"standard"` or `"custom"`,
|
- `EVENTS_CALENDAR`: type of the calendar to be used (either `None`, `"standard"` or `"custom"`,
|
||||||
default to `None`).
|
default to `None`).
|
||||||
3. Add the `@call` command.
|
3. Add the `@call` command.
|
||||||
4. Inherit from the custom typeclasses of the event system.
|
4. Inherit from the custom typeclasses of the in-game Python system.
|
||||||
- `evennia.contrib.events.typeclasses.EventCharacter`: to replace `DefaultCharacter`.
|
- `evennia.contrib.ingame_python.typeclasses.EventCharacter`: to replace `DefaultCharacter`.
|
||||||
- `evennia.contrib.events.typeclasses.EventExit`: to replace `DefaultExit`.
|
- `evennia.contrib.ingame_python.typeclasses.EventExit`: to replace `DefaultExit`.
|
||||||
- `evennia.contrib.events.typeclasses.EventObject`: to replace `DefaultObject`.
|
- `evennia.contrib.ingame_python.typeclasses.EventObject`: to replace `DefaultObject`.
|
||||||
- `evennia.contrib.events.typeclasses.EventRoom`: to replace `DefaultRoom`.
|
- `evennia.contrib.ingame_python.typeclasses.EventRoom`: to replace `DefaultRoom`.
|
||||||
|
|
||||||
The following sections describe in details each step of the installation.
|
The following sections describe in details each step of the installation.
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ that a 'callback' property is not defined. After performing step `1` the error w
|
||||||
|
|
||||||
To start the event script, you only need a single command, using `@py`.
|
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
|
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,
|
script will hold basic configuration, individual callbacks and so on. You may access it directly,
|
||||||
|
|
@ -174,7 +174,7 @@ this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia import default_cmds
|
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):
|
class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
"""
|
"""
|
||||||
|
|
@ -194,25 +194,25 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
|
|
||||||
### Changing parent classes of typeclasses
|
### 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
|
classes. For instance, in your `typeclasses/characters.py` module, you should change inheritance
|
||||||
like this:
|
like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia.contrib.events.typeclasses import EventCharacter
|
from evennia.contrib.ingame_python.typeclasses import EventCharacter
|
||||||
|
|
||||||
class Character(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
|
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.
|
call the parent methods when overriding hooks.
|
||||||
|
|
||||||
## Using the `@call` command
|
## 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.
|
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
|
The `@call` command allows to add, edit and delete callbacks on specific objects' events. The event
|
||||||
|
|
@ -383,7 +383,7 @@ most complex.
|
||||||
|
|
||||||
### The eventfuncs
|
### 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
|
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.
|
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!
|
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.
|
from it when the event has been executed.
|
||||||
|
|
||||||
### Callbacks with parameters
|
### 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
|
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
|
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.
|
this typeclass.
|
||||||
|
|
||||||
Here, we want to add a "push" event on objects. In your `typeclasses/objects.py` file, you should
|
Here, we want to add a "push" event on objects. In your `typeclasses/objects.py` file, you should
|
||||||
write something like:
|
write something like:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia.contrib.events.utils import register_events
|
from evennia.contrib.ingame_python.utils import register_events
|
||||||
from evennia.contrib.events.typeclasses import EventObject
|
from evennia.contrib.ingame_python.typeclasses import EventObject
|
||||||
|
|
||||||
EVENT_PUSH = """
|
EVENT_PUSH = """
|
||||||
A character push the object.
|
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.
|
`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,
|
- 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
|
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
|
### 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
|
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.
|
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
|
The "say" command uses phrase parameters (you can set a "say" callback to fires if a phrase
|
||||||
contains one specific word).
|
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.
|
parameter in your event definition.
|
||||||
|
|
||||||
- `keyword_event` should be used for keyword parameters.
|
- `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:
|
For example, here is the definition of the "say" event:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia.contrib.events.utils import register_events, phrase_event
|
from evennia.contrib.ingame_python.utils import register_events, phrase_event
|
||||||
# ...
|
# ...
|
||||||
@register_events
|
@register_events
|
||||||
class SomeTypeclass:
|
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
|
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.
|
but no event will be called automatically.
|
||||||
|
|
@ -7,9 +7,9 @@ from collections import namedtuple
|
||||||
class CallbackHandler(object):
|
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
|
handler. This handler is therefore a shortcut to be used by
|
||||||
developers. This handler (accessible through `obj.callbacks`) is a
|
developers. This handler (accessible through `obj.callbacks`) is a
|
||||||
shortcut to manipulating callbacks within this object, getting,
|
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
|
from datetime import datetime
|
||||||
|
|
@ -10,7 +10,7 @@ from evennia.utils.ansi import raw
|
||||||
from evennia.utils.eveditor import EvEditor
|
from evennia.utils.eveditor import EvEditor
|
||||||
from evennia.utils.evtable import EvTable
|
from evennia.utils.evtable import EvTable
|
||||||
from evennia.utils.utils import class_from_module, time_format
|
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)
|
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||||
|
|
||||||
|
|
@ -358,9 +358,6 @@ class CmdCallback(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
# Open the editor
|
# Open the editor
|
||||||
callback = dict(callback)
|
callback = dict(callback)
|
||||||
callback["obj"] = obj
|
|
||||||
callback["name"] = callback_name
|
|
||||||
callback["number"] = number
|
|
||||||
self.caller.db._callback = callback
|
self.caller.db._callback = callback
|
||||||
EvEditor(self.caller, loadfunc=_ev_load, savefunc=_ev_save,
|
EvEditor(self.caller, loadfunc=_ev_load, savefunc=_ev_save,
|
||||||
quitfunc=_ev_quit, key="Callback {} of {}".format(
|
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 import ObjectDB, ScriptDB
|
||||||
from evennia.contrib.events.utils import InterruptEvent
|
from evennia.contrib.ingame_python.utils import InterruptEvent
|
||||||
|
|
||||||
def deny():
|
def deny():
|
||||||
"""
|
"""
|
||||||
Deny, that is stop, the event here.
|
Deny, that is stop, the callback here.
|
||||||
|
|
||||||
Notes:
|
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
|
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
|
prior to a command, the command will be cancelled as well. Good
|
||||||
situations to use the `deny()` function are in events that begins
|
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
|
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.create import create_channel
|
||||||
from evennia.utils.dbserialize import dbserialize
|
from evennia.utils.dbserialize import dbserialize
|
||||||
from evennia.utils.utils import all_from_module, delay, pypath_to_realpath
|
from evennia.utils.utils import all_from_module, delay, pypath_to_realpath
|
||||||
from evennia.contrib.events.callbackhandler import CallbackHandler
|
from evennia.contrib.ingame_python.callbackhandler import CallbackHandler
|
||||||
from evennia.contrib.events.utils import get_next_wait, EVENTS, InterruptEvent
|
from evennia.contrib.ingame_python.utils import get_next_wait, EVENTS, InterruptEvent
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
RE_LINE_ERROR = re.compile(r'^ File "\<string\>", line (\d+)')
|
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
|
This script shouldn't be created more than once. It contains
|
||||||
event (in a non-persistent attribute) and callbacks (in a
|
event (in a non-persistent attribute) and callbacks (in a
|
||||||
persistent attribute). The script method would help adding,
|
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
|
# Generate locals
|
||||||
self.ndb.current_locals = {}
|
self.ndb.current_locals = {}
|
||||||
self.ndb.fresh_locals = {}
|
self.ndb.fresh_locals = {}
|
||||||
addresses = ["evennia.contrib.events.eventfuncs"]
|
addresses = ["evennia.contrib.ingame_python.eventfuncs"]
|
||||||
addresses.extend(getattr(settings, "EVENTFUNCS_LOCATIONS", ["world.eventfuncs"]))
|
addresses.extend(getattr(settings, "EVENTFUNCS_LOCATIONS", ["world.eventfuncs"]))
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
if pypath_to_realpath(address):
|
if pypath_to_realpath(address):
|
||||||
|
|
@ -85,7 +85,7 @@ class EventHandler(DefaultScript):
|
||||||
delay(seconds, complete_task, task_id)
|
delay(seconds, complete_task, task_id)
|
||||||
|
|
||||||
# Place the script in the CallbackHandler
|
# Place the script in the CallbackHandler
|
||||||
from evennia.contrib.events import typeclasses
|
from evennia.contrib.ingame_python import typeclasses
|
||||||
CallbackHandler.script = self
|
CallbackHandler.script = self
|
||||||
DefaultObject.callbacks = typeclasses.EventObject.callbacks
|
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
|
from mock import Mock
|
||||||
|
|
@ -12,8 +12,8 @@ from evennia.objects.objects import ExitCommand
|
||||||
from evennia.utils import ansi, utils
|
from evennia.utils import ansi, utils
|
||||||
from evennia.utils.create import create_object, create_script
|
from evennia.utils.create import create_object, create_script
|
||||||
from evennia.utils.test_resources import EvenniaTest
|
from evennia.utils.test_resources import EvenniaTest
|
||||||
from evennia.contrib.events.commands import CmdCallback
|
from evennia.contrib.ingame_python.commands import CmdCallback
|
||||||
from evennia.contrib.events.callbackhandler import CallbackHandler
|
from evennia.contrib.ingame_python.callbackhandler import CallbackHandler
|
||||||
|
|
||||||
# Force settings
|
# Force settings
|
||||||
settings.EVENTS_CALENDAR = "standard"
|
settings.EVENTS_CALENDAR = "standard"
|
||||||
|
|
@ -31,18 +31,18 @@ class TestEventHandler(EvenniaTest):
|
||||||
"""Create the event handler."""
|
"""Create the event handler."""
|
||||||
super(TestEventHandler, self).setUp()
|
super(TestEventHandler, self).setUp()
|
||||||
self.handler = create_script(
|
self.handler = create_script(
|
||||||
"evennia.contrib.events.scripts.EventHandler")
|
"evennia.contrib.ingame_python.scripts.EventHandler")
|
||||||
|
|
||||||
# Copy old events if necessary
|
# Copy old events if necessary
|
||||||
if OLD_EVENTS:
|
if OLD_EVENTS:
|
||||||
self.handler.ndb.events = dict(OLD_EVENTS)
|
self.handler.ndb.events = dict(OLD_EVENTS)
|
||||||
|
|
||||||
# Alter typeclasses
|
# Alter typeclasses
|
||||||
self.char1.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
self.char1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||||
self.char2.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
self.char2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||||
self.room1.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
self.room1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||||
self.room2.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
self.room2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||||
self.exit.swap_typeclass("evennia.contrib.events.typeclasses.EventExit")
|
self.exit.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventExit")
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Stop the event handler."""
|
"""Stop the event handler."""
|
||||||
|
|
@ -249,18 +249,18 @@ class TestCmdCallback(CommandTest):
|
||||||
"""Create the callback handler."""
|
"""Create the callback handler."""
|
||||||
super(TestCmdCallback, self).setUp()
|
super(TestCmdCallback, self).setUp()
|
||||||
self.handler = create_script(
|
self.handler = create_script(
|
||||||
"evennia.contrib.events.scripts.EventHandler")
|
"evennia.contrib.ingame_python.scripts.EventHandler")
|
||||||
|
|
||||||
# Copy old events if necessary
|
# Copy old events if necessary
|
||||||
if OLD_EVENTS:
|
if OLD_EVENTS:
|
||||||
self.handler.ndb.events = dict(OLD_EVENTS)
|
self.handler.ndb.events = dict(OLD_EVENTS)
|
||||||
|
|
||||||
# Alter typeclasses
|
# Alter typeclasses
|
||||||
self.char1.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
self.char1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||||
self.char2.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
self.char2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||||
self.room1.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
self.room1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||||
self.room2.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
self.room2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||||
self.exit.swap_typeclass("evennia.contrib.events.typeclasses.EventExit")
|
self.exit.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventExit")
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Stop the callback handler."""
|
"""Stop the callback handler."""
|
||||||
|
|
@ -268,7 +268,7 @@ class TestCmdCallback(CommandTest):
|
||||||
OLD_EVENTS.update(self.handler.ndb.events)
|
OLD_EVENTS.update(self.handler.ndb.events)
|
||||||
self.handler.stop()
|
self.handler.stop()
|
||||||
for script in ScriptDB.objects.filter(
|
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()
|
script.stop()
|
||||||
|
|
||||||
CallbackHandler.script = None
|
CallbackHandler.script = None
|
||||||
|
|
@ -414,18 +414,18 @@ class TestDefaultCallbacks(CommandTest):
|
||||||
"""Create the callback handler."""
|
"""Create the callback handler."""
|
||||||
super(TestDefaultCallbacks, self).setUp()
|
super(TestDefaultCallbacks, self).setUp()
|
||||||
self.handler = create_script(
|
self.handler = create_script(
|
||||||
"evennia.contrib.events.scripts.EventHandler")
|
"evennia.contrib.ingame_python.scripts.EventHandler")
|
||||||
|
|
||||||
# Copy old events if necessary
|
# Copy old events if necessary
|
||||||
if OLD_EVENTS:
|
if OLD_EVENTS:
|
||||||
self.handler.ndb.events = dict(OLD_EVENTS)
|
self.handler.ndb.events = dict(OLD_EVENTS)
|
||||||
|
|
||||||
# Alter typeclasses
|
# Alter typeclasses
|
||||||
self.char1.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
self.char1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||||
self.char2.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
|
self.char2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventCharacter")
|
||||||
self.room1.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
self.room1.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||||
self.room2.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
|
self.room2.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventRoom")
|
||||||
self.exit.swap_typeclass("evennia.contrib.events.typeclasses.EventExit")
|
self.exit.swap_typeclass("evennia.contrib.ingame_python.typeclasses.EventExit")
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Stop the callback handler."""
|
"""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,
|
To use thm, one should inherit from these classes (EventObject,
|
||||||
EventRoom, EventCharacter and EventExit).
|
EventRoom, EventCharacter and EventExit).
|
||||||
|
|
@ -9,8 +9,8 @@ EventRoom, EventCharacter and EventExit).
|
||||||
from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
|
from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
|
||||||
from evennia import ScriptDB
|
from evennia import ScriptDB
|
||||||
from evennia.utils.utils import delay, inherits_from, lazy_property
|
from evennia.utils.utils import delay, inherits_from, lazy_property
|
||||||
from evennia.contrib.events.callbackhandler import CallbackHandler
|
from evennia.contrib.ingame_python.callbackhandler import CallbackHandler
|
||||||
from evennia.contrib.events.utils import register_events, time_event, phrase_event
|
from evennia.contrib.ingame_python.utils import register_events, time_event, phrase_event
|
||||||
|
|
||||||
# Character help
|
# Character help
|
||||||
CHARACTER_CAN_DELETE = """
|
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
|
spoken phrase. For instance, you can set an event tthat would
|
||||||
fire if the phrase spoken by the character contains "menu" or
|
fire if the phrase spoken by the character contains "menu" or
|
||||||
"dinner" or "lunch":
|
"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,
|
Then if one of the words is present in what the character says,
|
||||||
this event will fire.
|
this event will fire.
|
||||||
|
|
||||||
|
|
@ -135,12 +135,12 @@ CHARACTER_TIME = """
|
||||||
A repeated event to be called regularly.
|
A repeated event to be called regularly.
|
||||||
This event is scheduled to repeat at different times, specified
|
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
|
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:
|
time). You have to specify the time as an argument to @call/add, like:
|
||||||
@event/add here = time 8:00
|
@call/add here = time 8:00
|
||||||
The parameter (8:00 here) must be a suite of digits separated by
|
The parameter (8:00 here) must be a suite of digits separated by
|
||||||
spaces, colons or dashes. Keep it as close from a recognizable
|
spaces, colons or dashes. Keep it as close from a recognizable
|
||||||
date format, like this:
|
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
|
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
|
game time). Units have to be specified depending on your set calendar
|
||||||
(ask a developer for more details).
|
(ask a developer for more details).
|
||||||
|
|
@ -461,12 +461,12 @@ EXIT_TIME = """
|
||||||
A repeated event to be called regularly.
|
A repeated event to be called regularly.
|
||||||
This event is scheduled to repeat at different times, specified
|
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
|
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:
|
time). You have to specify the time as an argument to @call/add, like:
|
||||||
@event/add north = time 8:00
|
@call/add north = time 8:00
|
||||||
The parameter (8:00 here) must be a suite of digits separated by
|
The parameter (8:00 here) must be a suite of digits separated by
|
||||||
spaces, colons or dashes. Keep it as close from a recognizable
|
spaces, colons or dashes. Keep it as close from a recognizable
|
||||||
date format, like this:
|
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
|
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
|
game time). Units have to be specified depending on your set calendar
|
||||||
(ask a developer for more details).
|
(ask a developer for more details).
|
||||||
|
|
@ -559,12 +559,12 @@ OBJECT_TIME = """
|
||||||
A repeated event to be called regularly.
|
A repeated event to be called regularly.
|
||||||
This event is scheduled to repeat at different times, specified
|
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
|
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:
|
time). You have to specify the time as an argument to @call/add, like:
|
||||||
@event/add here = time 8:00
|
@call/add here = time 8:00
|
||||||
The parameter (8:00 here) must be a suite of digits separated by
|
The parameter (8:00 here) must be a suite of digits separated by
|
||||||
spaces, colons or dashes. Keep it as close from a recognizable
|
spaces, colons or dashes. Keep it as close from a recognizable
|
||||||
date format, like this:
|
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
|
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
|
game time). Units have to be specified depending on your set calendar
|
||||||
(ask a developer for more details).
|
(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
|
as separate words, in the spoken phrase. For instance, you can
|
||||||
set an event tthat would fire if the phrase spoken by the character
|
set an event tthat would fire if the phrase spoken by the character
|
||||||
contains "menu" or "dinner" or "lunch":
|
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,
|
Then if one of the words is present in what the character says,
|
||||||
this event will fire.
|
this event will fire.
|
||||||
|
|
||||||
|
|
@ -716,12 +716,12 @@ ROOM_TIME = """
|
||||||
A repeated event to be called regularly.
|
A repeated event to be called regularly.
|
||||||
This event is scheduled to repeat at different times, specified
|
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
|
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:
|
time). You have to specify the time as an argument to @call/add, like:
|
||||||
@event/add here = time 8:00
|
@call/add here = time 8:00
|
||||||
The parameter (8:00 here) must be a suite of digits separated by
|
The parameter (8:00 here) must be a suite of digits separated by
|
||||||
spaces, colons or dashes. Keep it as close from a recognizable
|
spaces, colons or dashes. Keep it as close from a recognizable
|
||||||
date format, like this:
|
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
|
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
|
game time). Units have to be specified depending on your set calendar
|
||||||
(ask a developer for more details).
|
(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)
|
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.key = key
|
||||||
script.desc = "event on {}".format(key)
|
script.desc = "event on {}".format(key)
|
||||||
script.db.time_format = parameters
|
script.db.time_format = parameters
|
||||||
|
|
@ -936,3 +936,51 @@ class TestTurnBattleFunc(EvenniaTest):
|
||||||
self.assertTrue(turnhandler.db.fighters == [joiner, attacker, defender])
|
self.assertTrue(turnhandler.db.fighters == [joiner, attacker, defender])
|
||||||
# Remove the script at the end
|
# Remove the script at the end
|
||||||
turnhandler.stop()
|
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)
|
obj.msg(text=(outmessage, outkwargs), from_obj=from_obj, **kwargs)
|
||||||
|
|
||||||
def move_to(self, destination, quiet=False,
|
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.
|
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
|
(at_before/after_move etc) with quiet=True, this is as quiet a move
|
||||||
as can be done.
|
as can be done.
|
||||||
|
|
||||||
|
Kwargs:
|
||||||
|
Passed on to announce_move_to and announce_move_from hooks.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
result (bool): True/False depending on if there were problems with the move.
|
result (bool): True/False depending on if there were problems with the move.
|
||||||
This method may also return various error messages to the
|
This method may also return various error messages to the
|
||||||
|
|
@ -699,7 +703,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
if not quiet:
|
if not quiet:
|
||||||
# tell the old room we are leaving
|
# tell the old room we are leaving
|
||||||
try:
|
try:
|
||||||
self.announce_move_from(destination)
|
self.announce_move_from(destination, **kwargs)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logerr(errtxt % "at_announce_move()", err)
|
logerr(errtxt % "at_announce_move()", err)
|
||||||
return False
|
return False
|
||||||
|
|
@ -714,7 +718,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
if not quiet:
|
if not quiet:
|
||||||
# Tell the new room we are there.
|
# Tell the new room we are there.
|
||||||
try:
|
try:
|
||||||
self.announce_move_to(source_location)
|
self.announce_move_to(source_location, **kwargs)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logerr(errtxt % "announce_move_to()", err)
|
logerr(errtxt % "announce_move_to()", err)
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ class AttributeHandler(object):
|
||||||
def _fullcache(self):
|
def _fullcache(self):
|
||||||
"""Cache all attributes of this object"""
|
"""Cache all attributes of this object"""
|
||||||
query = {"%s__id" % self._model: self._objid,
|
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_attrtype": self._attrtype}
|
||||||
attrs = [
|
attrs = [
|
||||||
conn.attribute for conn in getattr(
|
conn.attribute for conn in getattr(
|
||||||
|
|
@ -278,7 +278,7 @@ class AttributeHandler(object):
|
||||||
return [] # no such attribute: return an empty list
|
return [] # no such attribute: return an empty list
|
||||||
else:
|
else:
|
||||||
query = {"%s__id" % self._model: self._objid,
|
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_attrtype": self._attrtype,
|
||||||
"attribute__db_key__iexact": key.lower(),
|
"attribute__db_key__iexact": key.lower(),
|
||||||
"attribute__db_category__iexact": category.lower() if category else None}
|
"attribute__db_category__iexact": category.lower() if category else None}
|
||||||
|
|
@ -303,7 +303,7 @@ class AttributeHandler(object):
|
||||||
else:
|
else:
|
||||||
# we have to query to make this category up-date in the cache
|
# we have to query to make this category up-date in the cache
|
||||||
query = {"%s__id" % self._model: self._objid,
|
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_attrtype": self._attrtype,
|
||||||
"attribute__db_category__iexact": category.lower() if category else None}
|
"attribute__db_category__iexact": category.lower() if category else None}
|
||||||
attrs = [conn.attribute for conn
|
attrs = [conn.attribute for conn
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue