Merge pull request #191 from lykoss/proxy
Introduce proxies as a means of breaking circular import chains
This commit is contained in:
commit
837cf49c44
@ -5,6 +5,32 @@ import time
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
|
||||
# Segue to logger, since src.gamemodes requires it
|
||||
# TODO: throw this into a logger.py perhaps so we aren't breaking up imports with non-import stuff
|
||||
def logger(file, write=True, display=True):
|
||||
if file is not None:
|
||||
open(file, "a").close() # create the file if it doesn't exist
|
||||
def log(*output, write=write, display=display):
|
||||
output = " ".join([str(x) for x in output]).replace("\u0002", "").replace("\\x02", "") # remove bold
|
||||
if botconfig.DEBUG_MODE:
|
||||
write = True
|
||||
if botconfig.DEBUG_MODE or botconfig.VERBOSE_MODE:
|
||||
display = True
|
||||
timestamp = get_timestamp()
|
||||
if display:
|
||||
print(timestamp + output, file=utf8stdout)
|
||||
if write and file is not None:
|
||||
with open(file, "a", errors="replace") as f:
|
||||
f.seek(0, 2)
|
||||
f.write(timestamp + output + "\n")
|
||||
|
||||
return log
|
||||
|
||||
stream_handler = logger(None)
|
||||
debuglog = logger("debug.log", write=False, display=False)
|
||||
errlog = logger("errors.log")
|
||||
plog = logger(None) # use this instead of print so that logs have timestamps
|
||||
|
||||
# Import the user-defined game modes
|
||||
# These are not required, so failing to import it doesn't matter
|
||||
# The file then imports our game modes
|
||||
@ -86,28 +112,6 @@ def get_timestamp(use_utc=None, ts_format=None):
|
||||
offset += str(time.timezone // 36).zfill(4)
|
||||
return tmf.format(tzname=tz, tzoffset=offset).strip().upper() + " "
|
||||
|
||||
def logger(file, write=True, display=True):
|
||||
if file is not None:
|
||||
open(file, "a").close() # create the file if it doesn't exist
|
||||
def log(*output, write=write, display=display):
|
||||
output = " ".join([str(x) for x in output]).replace("\u0002", "").replace("\\x02", "") # remove bold
|
||||
if botconfig.DEBUG_MODE:
|
||||
write = True
|
||||
if botconfig.DEBUG_MODE or botconfig.VERBOSE_MODE:
|
||||
display = True
|
||||
timestamp = get_timestamp()
|
||||
if display:
|
||||
print(timestamp + output, file=utf8stdout)
|
||||
if write and file is not None:
|
||||
with open(file, "a", errors="replace") as f:
|
||||
f.seek(0, 2)
|
||||
f.write(timestamp + output + "\n")
|
||||
|
||||
return log
|
||||
|
||||
stream_handler = logger(None)
|
||||
debuglog = logger("debug.log", write=False, display=False)
|
||||
|
||||
def stream(output, level="normal"):
|
||||
if botconfig.VERBOSE_MODE or botconfig.DEBUG_MODE:
|
||||
stream_handler(output)
|
||||
|
@ -7,8 +7,7 @@ import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src.messages import messages
|
||||
# utilities imported per Vgr's temporary fix to chk_nightdone(cli) in Sleepy
|
||||
from src import events, utilities
|
||||
from src import events
|
||||
|
||||
def game_mode(name, minp, maxp, likelihood = 0):
|
||||
def decor(c):
|
||||
@ -852,7 +851,7 @@ class SleepyMode(GameMode):
|
||||
if "correct" in self.on_path:
|
||||
pm(cli, self.having_nightmare, messages["sleepy_nightmare_wake"])
|
||||
self.having_nightmare = None
|
||||
utilities.chk_nightdone(cli)
|
||||
chk_nightdone(cli)
|
||||
elif "fake1" in self.on_path:
|
||||
pm(cli, self.having_nightmare, messages["sleepy_nightmare_fake_1"])
|
||||
self.step = 0
|
||||
|
101
src/proxy.py
Normal file
101
src/proxy.py
Normal file
@ -0,0 +1,101 @@
|
||||
import inspect
|
||||
|
||||
from src.decorators import handle_error
|
||||
|
||||
""" This module introduces two decorators - @proxy.stub and @proxy.impl
|
||||
|
||||
@proxy.stub is used to decorate a stub method that should be filled in
|
||||
with an implementation in some other module. Calling the stub method
|
||||
calls the implementation method in the other module instead of the stub
|
||||
method itself (or raises a NotImplementedError if no such
|
||||
implementation exists)
|
||||
|
||||
@proxy.impl is used to define a previously-declared stub with an actual
|
||||
implementation to call -- the signature for the implementation must
|
||||
exactly match the signature for the stub (enforced for Python 3.3+).
|
||||
|
||||
Attempting to implement a non-existent stub is an error, as is trying
|
||||
to re-implement a stub that is already implemented.
|
||||
|
||||
Example:
|
||||
(foo.py)
|
||||
@proxy.stub
|
||||
def my_method(foo, bar=10):
|
||||
pass
|
||||
|
||||
(bar.py)
|
||||
@proxy.impl
|
||||
def my_method(foo, bar=10):
|
||||
return foo * bar
|
||||
"""
|
||||
IMPLS = {}
|
||||
SIGS = {}
|
||||
|
||||
def stub(f):
|
||||
def inner(*args, **kwargs):
|
||||
if f.__name__ not in IMPLS:
|
||||
raise NotImplementedError(("This proxy stub has not yet been "
|
||||
"implemented in another module"))
|
||||
return IMPLS[f.__name__](*args, **kwargs)
|
||||
|
||||
if hasattr(inspect, "signature"):
|
||||
# Python 3.3+
|
||||
if f.__name__ in SIGS:
|
||||
_sigmatch(f)
|
||||
SIGS[f.__name__] = inspect.signature(f)
|
||||
else:
|
||||
# Python 3.2; not allowed to have nice things
|
||||
SIGS[f.__name__] = None
|
||||
|
||||
return inner
|
||||
|
||||
def impl(f):
|
||||
if f.__name__ not in SIGS:
|
||||
raise NameError(("Attempting to implement a proxy stub {0} that does "
|
||||
"not exist").format(f.__name__))
|
||||
if f.__name__ in IMPLS:
|
||||
raise SyntaxError(("Attempting to implement a proxy stub {0} that "
|
||||
"already has an implementation").format(f.__name__))
|
||||
if hasattr(inspect, "signature"):
|
||||
# Python 3.3+
|
||||
_sigmatch(f)
|
||||
|
||||
# Always wrap proxy implementations in an error handler
|
||||
IMPLS[f.__name__] = handle_error(f)
|
||||
# allows this method to be called directly in our module rather
|
||||
# than forcing use of the stub's module
|
||||
return handle_error(f)
|
||||
|
||||
def _sigmatch(f):
|
||||
rs = inspect.signature(f)
|
||||
ts = SIGS[f.__name__]
|
||||
if len(rs.parameters) != len(ts.parameters):
|
||||
raise TypeError(
|
||||
("Arity does not match existing stub, "
|
||||
"expected {0} parameters but got {1}.").format(
|
||||
len(ts.parameters), len(rs.parameters)))
|
||||
opl = list(rs.parameters.values())
|
||||
tpl = list(ts.parameters.values())
|
||||
for i in range(len(rs.parameters)):
|
||||
op = opl[i]
|
||||
tp = tpl[i]
|
||||
if op.name != tp.name:
|
||||
raise TypeError(
|
||||
("Parameter name does not match existing stub, "
|
||||
"expected {0} but got {1}.").format(tp.name, op.name))
|
||||
if op.default != tp.default:
|
||||
raise TypeError(
|
||||
("Default value of parameter does not match existing stub "
|
||||
"for parameter {0}, expected {1} but got {2}.").format(
|
||||
op.name,
|
||||
("no default" if tp.default is inspect.Parameter.empty
|
||||
else repr(tp.default)),
|
||||
("no default" if op.default is inspect.Parameter.empty
|
||||
else repr(op.default))))
|
||||
if op.kind != tp.kind:
|
||||
raise TypeError(
|
||||
("Parameter type does not match existing stub for "
|
||||
"parameter {0}, expected {1} but got {2}.").format(
|
||||
op.name, str(tp.kind), str(op.kind)))
|
||||
|
||||
# vim: set expandtab:sw=4:ts=4:
|
@ -1,7 +1,9 @@
|
||||
import src.settings as var
|
||||
import botconfig
|
||||
import re
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src import proxy, debuglog
|
||||
|
||||
# Some miscellaneous helper functions
|
||||
|
||||
def mass_mode(cli, md_param, md_plain):
|
||||
@ -156,4 +158,16 @@ def relay_wolfchat_command(cli, nick, message, roles, is_wolf_command=False, is_
|
||||
mass_privmsg(cli, wcwolves, message)
|
||||
mass_privmsg(cli, var.SPECTATING_WOLFCHAT, "[wolfchat] " + message)
|
||||
|
||||
@proxy.stub
|
||||
def chk_nightdone(cli):
|
||||
pass
|
||||
|
||||
@proxy.stub
|
||||
def chk_decision(cli, force=""):
|
||||
pass
|
||||
|
||||
@proxy.stub
|
||||
def chk_win(cli, end_game=True, winner=None):
|
||||
pass
|
||||
|
||||
# vim: set expandtab:sw=4:ts=4:
|
||||
|
@ -44,19 +44,12 @@ from oyoyo.parse import parse_nick
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import decorators, events, logger, utilities, debuglog
|
||||
|
||||
# make debuglog accessible anywhere
|
||||
utilities.debuglog = debuglog
|
||||
|
||||
from src import decorators, events, logger, proxy, debuglog, errlog, plog
|
||||
from src.messages import messages
|
||||
|
||||
# done this way so that events is accessible in !eval (useful for debugging)
|
||||
Event = events.Event
|
||||
|
||||
errlog = logger("errors.log")
|
||||
plog = logger(None) #use this instead of print so that logs have timestamps
|
||||
|
||||
is_admin = var.is_admin
|
||||
is_owner = var.is_owner
|
||||
|
||||
@ -2073,7 +2066,7 @@ def fday(cli, nick, chan, rest):
|
||||
transition_day(cli)
|
||||
|
||||
# Specify force = "nick" to force nick to be lynched
|
||||
@handle_error
|
||||
@proxy.impl
|
||||
def chk_decision(cli, force = ""):
|
||||
with var.GRAVEYARD_LOCK:
|
||||
if var.PHASE != "day":
|
||||
@ -2595,6 +2588,7 @@ def stop_game(cli, winner = "", abort = False, additional_winners = None):
|
||||
|
||||
return True
|
||||
|
||||
@proxy.impl
|
||||
def chk_win(cli, end_game=True, winner=None):
|
||||
""" Returns True if someone won """
|
||||
chan = botconfig.CHANNEL
|
||||
@ -4603,6 +4597,7 @@ def transition_day(cli, gameid=0):
|
||||
|
||||
begin_day(cli)
|
||||
|
||||
@proxy.impl
|
||||
def chk_nightdone(cli):
|
||||
if var.PHASE != "night":
|
||||
return
|
||||
@ -4702,8 +4697,6 @@ def chk_nightdone(cli):
|
||||
if var.PHASE == "night": # Double check
|
||||
transition_day(cli)
|
||||
|
||||
utilities.chk_nightdone = chk_nightdone # for some events to access
|
||||
|
||||
@cmd("nolynch", "nl", "novote", "nv", "abstain", "abs", playing=True, phases=("day",))
|
||||
def no_lynch(cli, nick, chan, rest):
|
||||
"""Allows you to abstain from voting for the day."""
|
||||
|
Loading…
Reference in New Issue
Block a user