diff --git a/evennia/server/amp.py b/evennia/server/amp.py index 621600e15..4b4435b31 100644 --- a/evennia/server/amp.py +++ b/evennia/server/amp.py @@ -7,7 +7,7 @@ The separation works like this: Portal - (AMP client) handles protocols. It contains a list of connected sessions in a dictionary for identifying the respective player - connected. If it looses the AMP connection it will automatically + connected. If it loses the AMP connection it will automatically try to reconnect. Server - (AMP server) Handles all mud operations. The server holds its own list @@ -32,33 +32,33 @@ from twisted.internet import protocol from twisted.internet.defer import Deferred from evennia.utils import logger from evennia.utils.utils import to_str, variable_from_module +import zlib # Used in Compressed class DUMMYSESSION = namedtuple('DummySession', ['sessid'])(0) # communication bits # (chr(9) and chr(10) are \t and \n, so skipping them) -PCONN = chr(1) # portal session connect -PDISCONN = chr(2) # portal session disconnect -PSYNC = chr(3) # portal session sync -SLOGIN = chr(4) # server session login -SDISCONN = chr(5) # server session disconnect -SDISCONNALL = chr(6) # server session disconnect all -SSHUTD = chr(7) # server shutdown -SSYNC = chr(8) # server session sync +PCONN = chr(1) # portal session connect +PDISCONN = chr(2) # portal session disconnect +PSYNC = chr(3) # portal session sync +SLOGIN = chr(4) # server session login +SDISCONN = chr(5) # server session disconnect +SDISCONNALL = chr(6) # server session disconnect all +SSHUTD = chr(7) # server shutdown +SSYNC = chr(8) # server session sync SCONN = chr(11) # server creating new connection (for irc bots and etc) -PCONNSYNC = chr(12) # portal post-syncing a session -PDISCONNALL = chr(13) # portal session disconnect all +PCONNSYNC = chr(12) # portal post-syncing a session +PDISCONNALL = chr(13) # portal session disconnect all AMP_MAXLEN = amp.MAX_VALUE_LENGTH # max allowed data length in AMP protocol (cannot be changed) -BATCH_RATE = 250 # max commands/sec before switching to batch-sending -BATCH_TIMEOUT = 0.5 # how often to poll to empty batch queue, in seconds +BATCH_RATE = 250 # max commands/sec before switching to batch-sending +BATCH_TIMEOUT = 0.5 # how often to poll to empty batch queue, in seconds # buffers _SENDBATCH = defaultdict(list) _MSGBUFFER = defaultdict(list) -import zlib def get_restart_mode(restart_file): """ @@ -323,9 +323,9 @@ dumps = lambda data: to_str(pickle.dumps(to_str(data), pickle.HIGHEST_PROTOCOL)) loads = lambda data: pickle.loads(to_str(data)) -#------------------------------------------------------------ +# ------------------------------------------------------------- # Core AMP protocol for communication Server <-> Portal -#------------------------------------------------------------ +# ------------------------------------------------------------- class AMPProtocol(amp.AMP): """ @@ -385,7 +385,6 @@ class AMPProtocol(amp.AMP): """ pass - # Error handling def errback(self, e, info): @@ -447,7 +446,7 @@ class AMPProtocol(amp.AMP): Access method called by the Portal and executed on the Portal. Args: - sessid (int): Unique Session id. + session (session): Session kwargs (any, optional): Optional data. Returns: @@ -473,7 +472,6 @@ class AMPProtocol(amp.AMP): self.factory.portal.sessions.data_out(session, **kwargs) return {} - def send_MsgServer2Portal(self, session, **kwargs): """ Access method - executed on the Server for sending data @@ -506,7 +504,7 @@ class AMPProtocol(amp.AMP): # create a new session and sync it server_sessionhandler.portal_connect(kwargs.get("sessiondata")) - elif operation == PCONNSYNC: #portal_session_sync + elif operation == PCONNSYNC: # portal_session_sync server_sessionhandler.portal_session_sync(kwargs.get("sessiondata")) elif operation == PDISCONN: # portal_session_disconnect @@ -515,7 +513,7 @@ class AMPProtocol(amp.AMP): if session: server_sessionhandler.portal_disconnect(session) - elif operation == PDISCONNALL: # portal_disconnect_all + elif operation == PDISCONNALL: # portal_disconnect_all # portal orders all sessions to close server_sessionhandler.portal_disconnect_all() @@ -545,7 +543,7 @@ class AMPProtocol(amp.AMP): """ return self.send_data(AdminPortal2Server, session.sessid, operation=operation, **kwargs) - # Portal administraton from the Server side + # Portal administration from the Server side @AdminServer2Portal.responder def portal_receive_adminserver2portal(self, packed_data): @@ -562,7 +560,6 @@ class AMPProtocol(amp.AMP): operation = kwargs.pop("operation") portal_sessionhandler = self.factory.portal.sessions - if operation == SLOGIN: # server_session_login # a session has authenticated; sync it. session = portal_sessionhandler.get(sessid) @@ -591,7 +588,7 @@ class AMPProtocol(amp.AMP): # set a flag in case we are about to shut down soon self.factory.server_restart_mode = True - elif operation == SCONN: # server_force_connection (for irc/etc) + elif operation == SCONN: # server_force_connection (for irc/etc) portal_sessionhandler.server_connect(**kwargs) else: @@ -665,4 +662,5 @@ class AMPProtocol(amp.AMP): module=modulepath, function=functionname, args=dumps(args), - kwargs=dumps(kwargs)).addCallback(lambda r: loads(r["result"])).addErrback(self.errback, "FunctionCall") + kwargs=dumps(kwargs)).addCallback( + lambda r: loads(r["result"])).addErrback(self.errback, "FunctionCall") diff --git a/evennia/server/portal/irc.py b/evennia/server/portal/irc.py index 274f7bc83..f8aa7e43a 100644 --- a/evennia/server/portal/irc.py +++ b/evennia/server/portal/irc.py @@ -198,7 +198,7 @@ class IRCBot(irc.IRCClient, Session): logger.log_info("IRC bot '%s' connected to %s at %s:%s." % (self.nickname, self.channel, self.network, self.port)) - def disconnect(self, reason=None): + def disconnect(self, reason=""): """ Called by sessionhandler to disconnect this protocol. @@ -206,7 +206,7 @@ class IRCBot(irc.IRCClient, Session): reason (str): Motivation for the disconnect. """ - self.sessionhandler.disconnect(self) + self.sessionhandler.disconnect(self, reason=reason) self.stopping = True self.transport.loseConnection() diff --git a/evennia/server/portal/portal.py b/evennia/server/portal/portal.py index 5de5d5bd8..3a8fb5338 100644 --- a/evennia/server/portal/portal.py +++ b/evennia/server/portal/portal.py @@ -37,9 +37,9 @@ if os.name == 'nt': # For Windows we need to handle pid files manually. PORTAL_PIDFILE = os.path.join(settings.GAME_DIR, "server", 'portal.pid') -#------------------------------------------------------------ +# ------------------------------------------------------------- # Evennia Portal settings -#------------------------------------------------------------ +# ------------------------------------------------------------- VERSION = get_evennia_version() @@ -76,6 +76,8 @@ AMP_ENABLED = AMP_HOST and AMP_PORT and AMP_INTERFACE # Maintenance function - this is called repeatedly by the portal. _IDLE_TIMEOUT = settings.IDLE_TIMEOUT + + def _portal_maintenance(): """ The maintenance function handles repeated checks and updates that @@ -94,12 +96,12 @@ def _portal_maintenance(): if _IDLE_TIMEOUT > 0: # only start the maintenance task if we care about idling. _maintenance_task = LoopingCall(_portal_maintenance) - _maintenance_task.start(60) # called every minute + _maintenance_task.start(60) # called every minute -#------------------------------------------------------------ +# ------------------------------------------------------------- # Portal Service object -#------------------------------------------------------------ +# ------------------------------------------------------------- class Portal(object): """ @@ -180,11 +182,11 @@ class Portal(object): self.shutdown_complete = True reactor.callLater(0, reactor.stop) -#------------------------------------------------------------ +# ------------------------------------------------------------- # # Start the Portal proxy server and add all active services # -#------------------------------------------------------------ +# ------------------------------------------------------------- # twistd requires us to define the variable 'application' so it knows # what to execute from. diff --git a/evennia/server/portal/ssh.py b/evennia/server/portal/ssh.py index 1d70f14cf..07e6fe64e 100644 --- a/evennia/server/portal/ssh.py +++ b/evennia/server/portal/ssh.py @@ -283,7 +283,8 @@ class SshProtocol(Manhole, session.Session): else: # we need to make sure to kill the color at the end in order # to match the webclient output. - linetosend = ansi.parse_ansi(_RE_N.sub("", text) + "|n", strip_ansi=nocolor, xterm256=xterm256, mxp=False) + linetosend = ansi.parse_ansi(_RE_N.sub("", text) + ("|n" if text[-1] != "|" else "||n"), + strip_ansi=nocolor, xterm256=xterm256, mxp=False) self.sendLine(linetosend) def send_prompt(self, *args, **kwargs): diff --git a/evennia/server/portal/ssl.py b/evennia/server/portal/ssl.py index c48cbf0a1..a3d78fbef 100644 --- a/evennia/server/portal/ssl.py +++ b/evennia/server/portal/ssl.py @@ -54,6 +54,7 @@ class SSLProtocol(TelnetProtocol): super(SSLProtocol, self).__init__(*args, **kwargs) self.protocol_name = "ssl" + def verify_SSL_key_and_cert(keyfile, certfile): """ This function looks for RSA key and certificate in the current @@ -82,7 +83,7 @@ def verify_SSL_key_and_cert(keyfile, certfile): # try to create the certificate CERT_EXPIRE = 365 * 20 # twenty years validity # default: - #openssl req -new -x509 -key ssl.key -out ssl.cert -days 7300 + # openssl req -new -x509 -key ssl.key -out ssl.cert -days 7300 exestring = "openssl req -new -x509 -key %s -out %s -days %s" % (keyfile, certfile, CERT_EXPIRE) try: subprocess.call(exestring) diff --git a/evennia/server/portal/telnet.py b/evennia/server/portal/telnet.py index 6b432876c..e9734f14a 100644 --- a/evennia/server/portal/telnet.py +++ b/evennia/server/portal/telnet.py @@ -141,7 +141,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): enable (bool): If this option should be enabled. """ - return (option == MCCP or option==ECHO) + return option == MCCP or option == ECHO def disableLocal(self, option): """ @@ -189,7 +189,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): # legacy clients. There should never be a reason to send a # lone NULL character so this seems to be a safe thing to # support for backwards compatibility. It also stops the - # NULL from continously popping up as an unknown command. + # NULL from continuously popping up as an unknown command. data = [_IDLE_COMMAND] else: data = _RE_LINEBREAK.split(data) @@ -225,16 +225,16 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): # Session hooks - def disconnect(self, reason=None): + def disconnect(self, reason=""): """ generic hook for the engine to call in order to disconnect this protocol. Args: - reason (str): Reason for disconnecting. + reason (str, optional): Reason for disconnecting. """ - self.data_out(text=((reason or "",), {})) + self.data_out(text=((reason,), {})) self.connectionLost(reason) def data_in(self, **kwargs): @@ -306,9 +306,11 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): if options.get("send_prompt"): # send a prompt instead. + prompt = text if not raw: # processing - prompt = ansi.parse_ansi(_RE_N.sub("", text) + "|n", strip_ansi=nocolor, xterm256=xterm256) + prompt = ansi.parse_ansi(_RE_N.sub("", prompt) + ("|n" if prompt[-1] != "|" else "||n"), + strip_ansi=nocolor, xterm256=xterm256) if mxp: prompt = mxp_parse(prompt) prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n') @@ -335,7 +337,8 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): else: # we need to make sure to kill the color at the end in order # to match the webclient output. - linetosend = ansi.parse_ansi(_RE_N.sub("", text) + "|n", strip_ansi=nocolor, xterm256=xterm256, mxp=mxp) + linetosend = ansi.parse_ansi(_RE_N.sub("", text) + ("|n" if text[-1] != "|" else "||n"), + strip_ansi=nocolor, xterm256=xterm256, mxp=mxp) if mxp: linetosend = mxp_parse(linetosend) self.sendLine(linetosend) diff --git a/evennia/server/portal/telnet_oob.py b/evennia/server/portal/telnet_oob.py index df200d4d2..7e450c4aa 100644 --- a/evennia/server/portal/telnet_oob.py +++ b/evennia/server/portal/telnet_oob.py @@ -32,12 +32,12 @@ from evennia.utils.utils import to_str # MSDP-relevant telnet cmd/opt-codes MSDP = chr(69) -MSDP_VAR = chr(1) #^A -MSDP_VAL = chr(2) #^B -MSDP_TABLE_OPEN = chr(3) #^C -MSDP_TABLE_CLOSE = chr(4) #^D -MSDP_ARRAY_OPEN = chr(5) #^E -MSDP_ARRAY_CLOSE = chr(6) #^F +MSDP_VAR = chr(1) # ^A +MSDP_VAL = chr(2) # ^B +MSDP_TABLE_OPEN = chr(3) # ^C +MSDP_TABLE_CLOSE = chr(4) # ^D +MSDP_ARRAY_OPEN = chr(5) # ^E +MSDP_ARRAY_CLOSE = chr(6) # ^F # GMCP GMCP = chr(201) @@ -51,13 +51,15 @@ force_str = lambda inp: to_str(inp, force_string=True) # pre-compiled regexes # returns 2-tuple -msdp_regex_table = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, - MSDP_TABLE_OPEN, - MSDP_TABLE_CLOSE)) +msdp_regex_table = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" + % (MSDP_VAR, MSDP_VAL, + MSDP_TABLE_OPEN, + MSDP_TABLE_CLOSE)) # returns 2-tuple -msdp_regex_array = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, - MSDP_ARRAY_OPEN, - MSDP_ARRAY_CLOSE)) +msdp_regex_array = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" + % (MSDP_VAR, MSDP_VAL, + MSDP_ARRAY_OPEN, + MSDP_ARRAY_CLOSE)) msdp_regex_var = re.compile(r"%s" % MSDP_VAR) msdp_regex_val = re.compile(r"%s" % MSDP_VAL) @@ -67,6 +69,7 @@ EVENNIA_TO_GMCP = {"client_options": "Core.Supports.Get", "repeat": "Char.Repeat.Update", "monitor": "Char.Monitor.Update"} + # MSDP/GMCP communication handler class TelnetOOB(object): @@ -100,7 +103,7 @@ class TelnetOOB(object): Client reports No msdp supported or wanted. Args: - options (Option): Not used. + option (Option): Not used. """ # no msdp, check GMCP @@ -173,7 +176,7 @@ class TelnetOOB(object): if not (args or kwargs): return msdp_cmdname - #print "encode_msdp in:", cmdname, args, kwargs + # print("encode_msdp in:", cmdname, args, kwargs) # DEBUG msdp_args = '' if args: @@ -182,30 +185,30 @@ class TelnetOOB(object): msdp_args += args[0] else: msdp_args += "{msdp_array_open}" \ - "{msdp_args}" \ - "{msdp_array_close}".format( - msdp_array_open=MSDP_ARRAY_OPEN, - msdp_array_close=MSDP_ARRAY_CLOSE, - msdp_args= "".join("%s%s" % ( - MSDP_VAL, json.dumps(val)) - for val in args)) - + "{msdp_args}" \ + "{msdp_array_close}".format( + msdp_array_open=MSDP_ARRAY_OPEN, + msdp_array_close=MSDP_ARRAY_CLOSE, + msdp_args="".join("%s%s" + % (MSDP_VAL, json.dumps(val)) + for val in args)) msdp_kwargs = "" if kwargs: msdp_kwargs = msdp_cmdname msdp_kwargs += "{msdp_table_open}" \ - "{msdp_kwargs}" \ - "{msdp_table_close}".format( - msdp_table_open=MSDP_TABLE_OPEN, - msdp_table_close=MSDP_TABLE_CLOSE, - msdp_kwargs = "".join("%s%s%s%s" % ( - MSDP_VAR, key, MSDP_VAL, json.dumps(val)) - for key, val in kwargs.iteritems())) + "{msdp_kwargs}" \ + "{msdp_table_close}".format( + msdp_table_open=MSDP_TABLE_OPEN, + msdp_table_close=MSDP_TABLE_CLOSE, + msdp_kwargs="".join("%s%s%s%s" + % (MSDP_VAR, key, MSDP_VAL, + json.dumps(val)) + for key, val in kwargs.iteritems())) msdp_string = msdp_args + msdp_kwargs - #print "msdp_string:", msdp_string + # print("msdp_string:", msdp_string) # DEBUG return msdp_string def encode_gmcp(self, cmdname, *args, **kwargs): @@ -238,10 +241,10 @@ class TelnetOOB(object): gmcp_string = "%s %s" % (cmdname, json.dumps([args, kwargs])) else: gmcp_string = "%s %s" % (cmdname, json.dumps(args)) - else: # only kwargs + else: # only kwargs gmcp_string = "%s %s" % (cmdname, json.dumps(kwargs)) - #print "gmcp string", gmcp_string + # print("gmcp string", gmcp_string) # DEBUG return gmcp_string def decode_msdp(self, data): @@ -271,7 +274,7 @@ class TelnetOOB(object): if hasattr(data, "__iter__"): data = "".join(data) - #print "decode_msdp in:", data + # print("decode_msdp in:", data) # DEBUG tables = {} arrays = {} @@ -279,7 +282,7 @@ class TelnetOOB(object): # decode tables for key, table in msdp_regex_table.findall(data): - tables[key] = {} if not key in tables else tables[key] + tables[key] = {} if key not in tables else tables[key] for varval in msdp_regex_var.split(table)[1:]: var, val = msdp_regex_val.split(varval, 1) if var: @@ -288,7 +291,7 @@ class TelnetOOB(object): # decode arrays from all that was not a table data_no_tables = msdp_regex_table.sub("", data) for key, array in msdp_regex_array.findall(data_no_tables): - arrays[key] = [] if not key in arrays else arrays[key] + arrays[key] = [] if key not in arrays else arrays[key] parts = msdp_regex_val.split(array) if len(parts) == 2: arrays[key].append(parts[1]) @@ -326,10 +329,9 @@ class TelnetOOB(object): for key, var in variables.iteritems(): cmds[key] = [[var], {}] - #print "msdp data in:", cmds + # print("msdp data in:", cmds) # DEBUG self.protocol.data_in(**cmds) - def decode_gmcp(self, data): """ Decodes incoming GMCP data on the form 'varname '. @@ -353,7 +355,7 @@ class TelnetOOB(object): if hasattr(data, "__iter__"): data = "".join(data) - #print "decode_gmcp in:", data + # print("decode_gmcp in:", data) # DEBUG if data: try: cmdname, structure = data.split(None, 1) @@ -368,7 +370,7 @@ class TelnetOOB(object): args, kwargs = [], {} if hasattr(structure, "__iter__"): if isinstance(structure, dict): - kwargs = {key: value for key, value in structure.iteritems() if key } + kwargs = {key: value for key, value in structure.iteritems() if key} else: args = list(structure) else: diff --git a/evennia/server/portal/ttype.py b/evennia/server/portal/ttype.py index de7d1b4c1..5e9ea3997 100644 --- a/evennia/server/portal/ttype.py +++ b/evennia/server/portal/ttype.py @@ -27,6 +27,7 @@ MTTS = [(128, 'PROXY'), (2, 'VT100'), (1, 'ANSI')] + class Ttype(object): """ Handles ttype negotiations. Called and initiated by the @@ -104,22 +105,21 @@ class Ttype(object): # use name to identify support for xterm256. Many of these # only support after a certain version, but all support # it since at least 4 years. We assume recent client here for now. - xterm256 = False cupper = clientname.upper() if cupper.startswith("MUDLET"): # supports xterm256 stably since 1.1 (2010?) - xterm256 = cupper.split("MUDLET",1)[1].strip() >= "1.1" + xterm256 = cupper.split("MUDLET", 1)[1].strip() >= "1.1" else: xterm256 = (cupper.startswith("XTERM") or cupper.endswith("-256COLOR") or cupper in ("ATLANTIS", # > 0.9.9.0 (aug 2009) - "CMUD", # > 3.04 (mar 2009) - "KILDCLIENT", # > 2.2.0 (sep 2005) - "MUDLET", # > beta 15 (sep 2009) - "MUSHCLIENT", # > 4.02 (apr 2007) - "PUTTY", # > 0.58 (apr 2005) - "BEIP", # > 2.00.206 (late 2009) (BeipMu) - "POTATO")) # > 2.00 (maybe earlier) + "CMUD", # > 3.04 (mar 2009) + "KILDCLIENT", # > 2.2.0 (sep 2005) + "MUDLET", # > beta 15 (sep 2009) + "MUSHCLIENT", # > 4.02 (apr 2007) + "PUTTY", # > 0.58 (apr 2005) + "BEIP", # > 2.00.206 (late 2009) (BeipMu) + "POTATO")) # > 2.00 (maybe earlier) # all clients supporting TTYPE at all seem to support ANSI self.protocol.protocol_flags['ANSI'] = True diff --git a/evennia/server/serversession.py b/evennia/server/serversession.py index ddbd4b3d4..b3492fe03 100644 --- a/evennia/server/serversession.py +++ b/evennia/server/serversession.py @@ -32,8 +32,9 @@ from django.utils.translation import ugettext as _ # Handlers for Session.db/ndb operation + class NDbHolder(object): - "Holder for allowing property access of attributes" + """Holder for allowing property access of attributes""" def __init__(self, obj, name, manager_name='attributes'): _SA(self, name, _GA(obj, manager_name)) _SA(self, 'name', name) @@ -145,9 +146,9 @@ class NAttributeHandler(object): return [key for key in self._store if not key.startswith("_")] -#------------------------------------------------------------ +# ------------------------------------------------------------- # Server Session -#------------------------------------------------------------ +# ------------------------------------------------------------- class ServerSession(Session): """ @@ -160,7 +161,7 @@ class ServerSession(Session): """ def __init__(self): - "Initiate to avoid AttributeErrors down the line" + """Initiate to avoid AttributeErrors down the line""" self.puppet = None self.player = None self.cmdset_storage_string = "" @@ -203,7 +204,7 @@ class ServerSession(Session): obj.player = self.player self.puid = obj.id self.puppet = obj - #obj.scripts.validate() + # obj.scripts.validate() obj.locks.cache_lock_bypass(obj) def at_login(self, player): @@ -264,7 +265,6 @@ class ServerSession(Session): MONITOR_HANDLER.remove(player, "_saved_webclient_options", self.sessid) - def get_player(self): """ Get the player associated with this session @@ -364,7 +364,6 @@ class ServerSession(Session): self.protocol_flags.update(kwargs) self.sessionhandler.session_portal_sync(self) - def data_out(self, **kwargs): """ Sending data from Evennia->Client @@ -437,7 +436,7 @@ class ServerSession(Session): self.sessionhandler.data_in(self, **kwargs) def __eq__(self, other): - "Handle session comparisons" + """Handle session comparisons""" try: return self.address == other.address except AttributeError: @@ -462,11 +461,9 @@ class ServerSession(Session): return "%s%s@%s" % (self.uname, symbol, address) def __unicode__(self): - "Unicode representation" + """Unicode representation""" return u"%s" % str(self) - - # Dummy API hooks for use during non-loggedin operation def at_cmdset_get(self, **kwargs): @@ -488,7 +485,7 @@ class ServerSession(Session): def attributes(self): return self.nattributes - #@property + # @property def ndb_get(self): """ A non-persistent store (ndb: NonDataBase). Everything stored @@ -503,7 +500,7 @@ class ServerSession(Session): self._ndb_holder = NDbHolder(self, "nattrhandler", manager_name="nattributes") return self._ndb_holder - #@ndb.setter + # @ndb.setter def ndb_set(self, value): """ Stop accidentally replacing the db object @@ -516,9 +513,9 @@ class ServerSession(Session): string += "Use ndb.attr=value instead." raise Exception(string) - #@ndb.deleter + # @ndb.deleter def ndb_del(self): - "Stop accidental deletion." + """Stop accidental deletion.""" raise Exception("Cannot delete the ndb object!") ndb = property(ndb_get, ndb_set, ndb_del) db = property(ndb_get, ndb_set, ndb_del) @@ -526,5 +523,5 @@ class ServerSession(Session): # Mock access method for the session (there is no lock info # at this stage, so we just present a uniform API) def access(self, *args, **kwargs): - "Dummy method to mimic the logged-in API." + """Dummy method to mimic the logged-in API.""" return True