Skip to content

Commit 45c5cd1

Browse files
authored
Merge pull request #65 from ManualForArchipelago/req-function-args-conversion
Convert Req functions args to real type
2 parents c3a5771 + 8834344 commit 45c5cd1

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed

src/Rules.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import re
1010
import math
11+
import inspect
12+
import logging
1113

1214
if TYPE_CHECKING:
1315
from . import ManualWorld
@@ -94,6 +96,7 @@ def checkRequireStringForArea(state: CollectionState, area: dict):
9496
if not callable(func):
9597
raise ValueError(f"Invalid function `{func_name}` in {area}.")
9698

99+
convert_req_function_args(func, func_args, area["name"])
97100
result = func(world, multiworld, state, player, *func_args)
98101
if isinstance(result, bool):
99102
requires_list = requires_list.replace("{" + func_name + "(" + item[1] + ")}", "1" if result else "0")
@@ -271,6 +274,78 @@ def allRegionsAccessible(state):
271274
# Victory requirement
272275
multiworld.completion_condition[player] = lambda state: state.has("__Victory__", player)
273276

277+
def convert_req_function_args(func, args: list[str], areaName: str, warn: bool = False):
278+
parameters = inspect.signature(func).parameters
279+
knownArguments = ["world", "multiworld", "state", "player"]
280+
index = 0
281+
for parameter, info in parameters.items():
282+
if parameter in knownArguments:
283+
continue
284+
285+
argType = info.annotation
286+
optional = False
287+
try:
288+
if issubclass(argType, inspect._empty): #if not set then it wont get converted but still be checked for valid data at index
289+
argType = str
290+
291+
except TypeError: # Optional
292+
if argType.__module__ == 'typing' and argType._name == 'Optional':
293+
optional = True
294+
argType = argType.__args__[0]
295+
else:
296+
#Implementing complex typing is not simple so ill skip it for now
297+
index += 1
298+
continue
299+
300+
try:
301+
value = args[index].strip()
302+
303+
except IndexError:
304+
if info is not inspect.Parameter.empty:
305+
value = info.default
306+
307+
else:
308+
raise Exception(f"A call of the {func.__name__} function in '{areaName}'s requirement, asks for a value of type {argType}\nfor its argument '{info.name}' but its missing")
309+
310+
if optional:
311+
if isinstance(value, type(None)):
312+
index += 1
313+
continue
314+
elif isinstance(value, str):
315+
if value.lower() == 'none':
316+
value = None
317+
args[index] = value
318+
index += 1
319+
continue
320+
321+
322+
if not isinstance(value, argType):
323+
if issubclass(argType, bool):
324+
#Special conversion to bool
325+
if value.lower() in ['true', '1']:
326+
value = True
327+
328+
elif value.lower() in ['false', '0']:
329+
value = False
330+
331+
else:
332+
value = bool(value)
333+
if warn:
334+
# warning here spam the console if called from rules.py, might be worth to make it a data validation instead
335+
logging.warn(f"A call of the {func.__name__} function in '{areaName}'s requirement, asks for a value of type {argType}\nfor its argument '{info.name}' but an unknown string was passed and thus converted to {value}")
336+
337+
else:
338+
try:
339+
value = argType(value)
340+
341+
except ValueError:
342+
raise Exception(f"A call of the {func.__name__} function in '{areaName}'s requirement, asks for a value of type {argType}\nfor its argument '{info.name}' but its value '{value}' cannot be converted to {argType}")
343+
344+
args[index] = value
345+
346+
index += 1
347+
348+
274349
def YamlEnabled(world: "ManualWorld", multiworld: MultiWorld, state: CollectionState, player: int, param: str) -> bool:
275350
"""Is a yaml option enabled?"""
276351
return is_option_enabled(multiworld, player, param)

0 commit comments

Comments
 (0)