mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-09 22:24:22 +00:00
get_variable now allows a default value to be passed to the call.
If the key is not found, the default value is returned. set_variable exception handling has been fixed to return an exception in the current call frame. Many wesnoth module functions now release the python GIL when it is both safe to do so and the function call takes long enough where it also makes sense. A new global boolean variable, 'restricted' is now set before the user AI script is invoked. This variable indicates if it is running inside of a restricted python environment or not. A new class of unrestricted scripts are now listed. Previously only scripts which have '#!WPY' at the top are allowed. If only allow safe python scripts is disabled, scripts which start with #!UNSAFE_WPY are also shown to users. This allows AI authors to specifically target either a restricted or unrestricted environment. New "system" class attributes are exposed in the restricted environment. These include; '__call__', '__copy__', '__deepcopy__', '__doc__', '__name__', '__repr__' and '__str__', in addition to the old __init__ method.
This commit is contained in:
parent
36a973f478
commit
2618cc3d15
24
changelog
24
changelog
@ -29,7 +29,27 @@ Version 1.5.1+svn:
|
||||
* Removed persistance from team configuration (bug: #10916)
|
||||
* Made automaticaly generated macro reference easier to naviagate and link to
|
||||
(patch: #1076)
|
||||
|
||||
* Python AI
|
||||
* get_variable now allows a default value to be passed to the call. If the
|
||||
key is not found, the default value is returned.
|
||||
* set_variable exception handling has been fixed to return an exception in
|
||||
the current call frame.
|
||||
* Many wesnoth module functions now release the python GIL when it is both
|
||||
safe to do so and the function call takes long enough where it also
|
||||
makes sense. More changes coming.
|
||||
* A new global boolean variable, 'restricted' is now set before the user AI
|
||||
script is invoked. This variable indicates if it is running inside of
|
||||
a restricted python environment or not.
|
||||
* A new class of unrestricted scripts are now listed. Previously only scripts which have
|
||||
#!WPY at the top are allowed. If only allow safe python scripts is disabled,
|
||||
scripts which start with #!UNSAFE_WPY are also shown to users. This allows
|
||||
AI authors to specifically target either a restricted or unrestricted environment.
|
||||
* New "system" class attributes are exposed in the restricted environment. These
|
||||
include; '__call__', '__copy__', '__deepcopy__', '__doc__', '__name__',
|
||||
'__repr__' and '__str__', in addition to the old __init__ method.
|
||||
* ValueError can now be caught.
|
||||
|
||||
|
||||
Version 1.5.1:
|
||||
* campaigns:
|
||||
* Descent into Darkness:
|
||||
@ -88,7 +108,7 @@ Version 1.5.1:
|
||||
* Try/Except clauses are now allowed. A subset of builtin exceptions are available.
|
||||
ArithmeticError, AssertionError, AttributeError, BaseException,
|
||||
StopIteration, IndexError, KeyError, NameError, RuntimeError,
|
||||
RuntimeWarning, ZeroDivisionError
|
||||
RuntimeWarning, and ZeroDivisionError
|
||||
* Exceptions can now be raised by user code.
|
||||
* terrains:
|
||||
* Fixed city village not being alias of the village terrain type; this was
|
||||
|
@ -23,7 +23,7 @@ def include(matchob):
|
||||
except IOError:
|
||||
pass
|
||||
else:
|
||||
raise safe.SafeException("Could not include %s." % name)
|
||||
raise safe.SafeException("Could not import '%s'." % name)
|
||||
|
||||
r += code
|
||||
|
||||
|
@ -45,7 +45,8 @@ _NODE_ATTR_OK = []
|
||||
# Expanded to allow repr, str, call, and doc. These are commonly overloaded
|
||||
# to provided fundamental functionality. Without __call__ support, most
|
||||
# categories of decorators are simply impossible.
|
||||
_STR_OK = [ '__call__', '__doc__', '__init__', '__name__', '__repr__', '__str__' ]
|
||||
_STR_OK = [ '__call__', '__copy__', '__deepcopy__', '__doc__',
|
||||
'__init__', '__name__', '__repr__', '__str__' ]
|
||||
|
||||
# If we put '__' in _STR_NOT_CONTAIN, then we can't have defacto private data
|
||||
_STR_NOT_CONTAIN = []
|
||||
@ -79,15 +80,15 @@ def _check_ast(code):
|
||||
|
||||
_BUILTIN_OK = [
|
||||
'__debug__','quit','exit',
|
||||
'Warning',
|
||||
'Warning', 'restricted',
|
||||
'None','True','False',
|
||||
'abs', 'bool', 'callable', 'chr', 'cmp', 'complex', 'dict', 'divmod', 'filter',
|
||||
'float', 'frozenset', 'hash', 'hex', 'int', 'isinstance', 'issubclass', 'len',
|
||||
'list', 'long', 'map', 'max', 'min', 'object', 'oct', 'ord', 'pow', 'range',
|
||||
'repr', 'round', 'set', 'slice', 'str', 'sum', 'super', 'tuple', 'xrange', 'zip',
|
||||
'ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'StopIteration',
|
||||
'IndexError', 'KeyError', 'NameError', 'RuntimeError', 'RuntimeWarning',
|
||||
'ZeroDivisionError'
|
||||
'ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'Exception',
|
||||
'IndexError', 'KeyError', 'NameError', 'RuntimeError', 'RuntimeWarning', 'StopIteration',
|
||||
'ValueError', 'ZeroDivisionError'
|
||||
]
|
||||
|
||||
_BUILTIN_STR = [
|
||||
@ -100,6 +101,7 @@ def _builtin_fnc(k):
|
||||
return fnc
|
||||
_builtin_globals = None
|
||||
_builtin_globals_r = None
|
||||
|
||||
def _builtin_init():
|
||||
global _builtin_globals, _builtin_globals_r
|
||||
if _builtin_globals != None: return
|
||||
@ -107,14 +109,22 @@ def _builtin_init():
|
||||
r = _builtin_globals = {}
|
||||
for k in __builtin__.__dict__.keys():
|
||||
v = None
|
||||
if k in _BUILTIN_OK: v = __builtin__.__dict__[k]
|
||||
elif k in _BUILTIN_STR: v = ''
|
||||
else: v = _builtin_fnc(k)
|
||||
if k in _BUILTIN_OK:
|
||||
v = __builtin__.__dict__[k]
|
||||
|
||||
elif k in _BUILTIN_STR:
|
||||
v = ''
|
||||
|
||||
else:
|
||||
v = _builtin_fnc(k)
|
||||
|
||||
r[k] = v
|
||||
|
||||
def _builtin_destroy():
|
||||
_builtin_init()
|
||||
for k,v in _builtin_globals.items():
|
||||
__builtin__.__dict__[k] = v
|
||||
|
||||
def _builtin_restore():
|
||||
for k,v in _builtin_globals_r.items():
|
||||
__builtin__.__dict__[k] = v
|
||||
@ -147,8 +157,8 @@ def safe_exec_op( code, context=None ):
|
||||
# Wrapper allowing safe_exec to be dynamically controlled
|
||||
# from wesnoth binary.
|
||||
def safe_exec( code, context=None, runSafe=True ):
|
||||
# Allow the AI to know if it is restricted or not
|
||||
context["restricted"] = runSafe
|
||||
context[ 'restricted' ] = runSafe
|
||||
|
||||
if runSafe:
|
||||
safe_exec_op( code, context )
|
||||
|
||||
|
@ -1006,6 +1006,7 @@ static PyObject* wrapper_team_is_enemy(wesnoth_team* team, void* /*closure*/)
|
||||
|
||||
// Find side number of team
|
||||
int side = 0;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
for (size_t t = 0; t < running_instance->get_teams().size(); t++) {
|
||||
if (team->team_ == &running_instance->get_teams()[t]) {
|
||||
side = 1 + t;
|
||||
@ -1014,6 +1015,8 @@ static PyObject* wrapper_team_is_enemy(wesnoth_team* team, void* /*closure*/)
|
||||
}
|
||||
|
||||
result = running_instance->current_team().is_enemy(side) == true ? 1 : 0;
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
return Py_BuildValue(INTVALUE, result);
|
||||
}
|
||||
|
||||
@ -1705,8 +1708,13 @@ PyObject* python_ai::wrapper_set_variable(PyObject* /*self*/, PyObject* args)
|
||||
PyObject* python_ai::wrapper_get_variable(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
char const *variable;
|
||||
if (!PyArg_ParseTuple(args, STRINGVALUE, &variable))
|
||||
PyObject *default_value = Py_BuildValue( STRINGVALUE, "" ) ;
|
||||
|
||||
// If a default value was not provided, see if we were called with
|
||||
// just the value to find.
|
||||
if (!PyArg_ParseTuple(args, CC("s|O"), &variable, &default_value))
|
||||
return NULL;
|
||||
|
||||
config const &memory = running_instance->current_team().ai_memory();
|
||||
char const *s = memory[variable].c_str();
|
||||
if (s && s[0])
|
||||
@ -1725,10 +1733,14 @@ PyObject* python_ai::wrapper_get_variable(PyObject* /*self*/, PyObject* args)
|
||||
}
|
||||
PyObject *ret = PyMarshal_ReadObjectFromString(data, len / 2);
|
||||
delete[] data;
|
||||
return ret;
|
||||
}
|
||||
return ret ;
|
||||
} else if( default_value ) {
|
||||
// Value did not exist - do return default value
|
||||
return default_value ;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* python_ai::wrapper_get_version(PyObject* /*self*/, PyObject* args)
|
||||
@ -1882,10 +1894,12 @@ static PyMethodDef wesnoth_python_methods[] = {
|
||||
"used to make the AI save strings (and other python values which can "
|
||||
"be marshalled) over multiple turns.")
|
||||
MDEF("get_variable", python_ai::wrapper_get_variable,
|
||||
"Parameters: variable\n"
|
||||
"Returns: value\n"
|
||||
"Parameters: variable, default value\n"
|
||||
"Returns: value or default value\n"
|
||||
"Retrieves a persistent variable 'variable' from the AI, which has "
|
||||
"previously been set with set_variable - or None if it can't be found.")
|
||||
"previously been set with set_variable - or None if it can't be found and\n"
|
||||
"default value has not been set. If a default value is set and the value"
|
||||
"can not be found, the default value is returned.")
|
||||
MDEF("get_version", python_ai::wrapper_get_version,
|
||||
"Returns a string containing current Wesnoth version")
|
||||
MDEF("raise_user_interact", python_ai::wrapper_raise_user_interact,
|
||||
@ -2054,6 +2068,7 @@ void python_ai::play_turn()
|
||||
// They have to end with .py, and have #!WPY as first line.
|
||||
std::vector<std::string> python_ai::get_available_scripts()
|
||||
{
|
||||
int allow_unsafe = !preferences::run_safe_python() ;
|
||||
std::vector<std::string> scripts;
|
||||
const std::vector<std::string>& paths = get_binary_paths("data");
|
||||
for(std::vector<std::string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
@ -2068,6 +2083,9 @@ std::vector<std::string> python_ai::get_available_scripts()
|
||||
if (mark == "#!WPY" &&
|
||||
std::find(scripts.begin(), scripts.end(), name) == scripts.end())
|
||||
scripts.push_back(name);
|
||||
else if (allow_unsafe && mark == "#!UNSAFE_WPY" &&
|
||||
std::find(scripts.begin(), scripts.end(), name) == scripts.end())
|
||||
scripts.push_back(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user