Add @interactive decorator to use yield to pause in a method

This commit is contained in:
Griatch 2019-04-21 14:23:36 +02:00
parent 866e8611b7
commit a2bc979503
2 changed files with 81 additions and 13 deletions

View file

@ -15,10 +15,10 @@ Update to Python 3
- Add new `@force` command to have another object perform a command.
- Add the Portal uptime to the `@time` command.
- Make the `@link` command first make a local search before a global search.
- Have the default Unloggedin-look command look for optional `connection_screen()` callable in
`mygame/server/conf/connection_screen.py`. This allows for more flexible welcome screens
that are calculated on the fly.
- `@py` command now defaults to escaping html tags in its output when viewing in the webclient.
- Have the default Unloggedin-look command look for optional `connection_screen()` callable in
`mygame/server/conf/connection_screen.py`. This allows for more flexible welcome screens
that are calculated on the fly.
- `@py` command now defaults to escaping html tags in its output when viewing in the webclient.
Use new `/clientraw` switch to get old behavior (issue #1369).
### Web
@ -73,7 +73,7 @@ Web/Django standard initiative (@strikaco)
### Prototypes
- `evennia.prototypes.save_prototype` now takes the prototype as a normal
- `evennia.prototypes.save_prototype` now takes the prototype as a normal
argument (`prototype`) instead of having to give it as `**prototype`.
- `evennia.prototypes.search_prototype` has a new kwarg `require_single=False` that
raises a KeyError exception if query gave 0 or >1 results.
@ -106,7 +106,7 @@ Web/Django standard initiative (@strikaco)
### Server
- Convert ServerConf model to store its values as a Picklefield (same as Attributes) instead of using a custom solution.
- OOB: Add support for MSDP LIST, REPORT, UNREPORT commands (re-mapped to `msdp_list`,
- OOB: Add support for MSDP LIST, REPORT, UNREPORT commands (re-mapped to `msdp_list`,
`msdp_report`, `msdp_unreport` inlinefuncs_)
- Added `evennia.ANSIString` to flat API.
- Server/Portal log files now cycle to names on the form `server_.log_19_03_08_` instead of `server.log___19.3.8`, retaining
@ -116,6 +116,9 @@ Web/Django standard initiative (@strikaco)
- `evennia` launcher now fully handles all django-admin commands, like running tests in parallel.
- `evennia.utils.create.account` now also takes `tags` and `attrs` keywords.
- `evennia.utils.interactive` decorator can now allow you to use yield(secs) to pause operation
in any function, not just in Command.func. Likewise, response = yield(question) will work
if the decorated function has an argument or kwarg `caller`.
- Added many more unit tests.
- Swap argument order of `evennia.set_trace` to `set_trace(term_size=(140, 40), debugger='auto')`
since the size is more likely to be changed on the command line.
@ -134,7 +137,7 @@ Web/Django standard initiative (@strikaco)
### Contribs
- The `extended_room` contrib saw some backwards-incompatible refactoring:
- The `extended_room` contrib saw some backwards-incompatible refactoring:
+ All commands now begin with `CmdExtendedRoom`. So before it was `CmdExtendedLook`, now
it's `CmdExtendedRoomLook` etc.
+ The `detail` command was broken out of the `desc` command and is now a new, stand-alone command
@ -142,7 +145,7 @@ Web/Django standard initiative (@strikaco)
command works in the tutorial-world.
+ The `detail` command now also supports deleting details (like the tutorial-world version).
+ The new `ExtendedRoomCmdSet` includes all the extended-room commands and is now the recommended way
to install the extended-room contrib.
to install the extended-room contrib.
- Reworked `menu_login` contrib to use latest EvMenu standards. Now also supports guest logins.
- Mail contrib was refactored to have optional Command classes `CmdMail` for OOC+IC mail (added
to the CharacterCmdSet and `CmdMailCharacter` for IC-only mailing between chars (added to CharacterCmdSet)
@ -260,7 +263,7 @@ Web/Django standard initiative (@strikaco)
- `tb_items` - Extends `tb_equip` with item use with conditions/status effects.
- `tb_magic` - Extends `tb_equip` with spellcasting.
- `tb_range` - Adds system for abstract positioning and movement.
- The `extended_room` contrib saw some backwards-incompatible refactoring:
- The `extended_room` contrib saw some backwards-incompatible refactoring:
- All commands now begin with `CmdExtendedRoom`. So before it was `CmdExtendedLook`, now
it's `CmdExtendedRoomLook` etc.
- The `detail` command was broken out of the `desc` command and is now a new, stand-alone command
@ -268,11 +271,11 @@ Web/Django standard initiative (@strikaco)
command works in the tutorial-world.
- The `detail` command now also supports deleting details (like the tutorial-world version).
- The new `ExtendedRoomCmdSet` includes all the extended-room commands and is now the recommended way
to install the extended-room contrib.
to install the extended-room contrib.
- Updates and some cleanup of existing contribs.
### Internationalization
### Internationalization
- Polish translation by user ogotai

View file

@ -16,10 +16,11 @@ import math
import re
import textwrap
import random
import pickle
import inspect
from twisted.internet.task import deferLater
from os.path import join as osjoin
from importlib import import_module
from importlib.util import find_spec, module_from_spec
from importlib.util import find_spec
from inspect import ismodule, trace, getmembers, getmodule, getmro
from collections import defaultdict, OrderedDict
from twisted.internet import threads, reactor, task
@ -1981,3 +1982,67 @@ def get_all_typeclasses(parent=None):
typeclasses = {name: typeclass for name, typeclass in typeclasses.items()
if inherits_from(typeclass, parent)}
return typeclasses
def interactive(func):
"""
Decorator to make a method pausable with yield(seconds)
and able to ask for user-input with response=yield(question).
For the question-asking to work, 'caller' must the name
of an argument or kwarg to the decorated function.
Note that this turns the method into a generator.
Example usage:
@interactive
def myfunc(caller):
caller.msg("This is a test")
# wait five seconds
yield(5)
# ask user (caller) a question
response = yield("Do you want to continue waiting?")
if response == "yes":
yield(5)
else:
# ...
"""
from evennia.utils.evmenu import get_input
def _process_input(caller, prompt, result, generator):
deferLater(reactor, 0, _iterate, generator, caller, response=result)
return False
def _iterate(generator, caller=None, response=None):
try:
if response is None:
value = next(generator)
else:
value = generator.send(response)
except StopIteration:
pass
else:
if isinstance(value, (int, float)):
delay(value, _iterate, generator, caller=caller)
elif isinstance(value, str):
if not caller:
raise ValueError("To retrieve input from a @pausable method, that method "
"must be called with a 'caller' argument)")
get_input(caller, value, _process_input, generator=generator)
else:
raise ValueError("yield(val) in a @pausable method must have an int/float as arg.")
def decorator(*args, **kwargs):
argnames = inspect.getfullargspec(func).args
caller = None
if 'caller' in argnames:
# we assume this is an object
caller = args[argnames.index('caller')]
ret = func(*args, **kwargs)
if isinstance(ret, types.GeneratorType):
_iterate(ret, caller)
else:
return ret
return decorator