From 00b5309295a348ef575208b1a1778477387d2a68 Mon Sep 17 00:00:00 2001 From: Griatch Date: Tue, 23 Jun 2015 10:42:51 +0200 Subject: [PATCH] Updated more of server/ to google docstrings as per #709. --- evennia/server/serversession.py | 86 +++++++++++---- evennia/server/session.py | 53 +++++++--- evennia/server/sessionhandler.py | 176 +++++++++++++++++++++++++------ 3 files changed, 251 insertions(+), 64 deletions(-) diff --git a/evennia/server/serversession.py b/evennia/server/serversession.py index cdef25bf0..efe6811fc 100644 --- a/evennia/server/serversession.py +++ b/evennia/server/serversession.py @@ -60,12 +60,13 @@ class ServerSession(Session): def at_sync(self): """ - This is called whenever a session has been resynced with the portal. - At this point all relevant attributes have already been set and - self.player been assigned (if applicable). + This is called whenever a session has been resynced with the + portal. At this point all relevant attributes have already + been set and self.player been assigned (if applicable). + + Since this is often called after a server restart we need to + set up the session as it was. - Since this is often called after a server restart we need to set up - the session as it was. """ global _ObjectDB if not _ObjectDB: @@ -94,7 +95,9 @@ class ServerSession(Session): """ Hook called by sessionhandler when the session becomes authenticated. - player - the player associated with the session + Args: + player (Player): The player associated with the session. + """ self.player = player self.uid = self.player.id @@ -115,6 +118,7 @@ class ServerSession(Session): def at_disconnect(self): """ Hook called by sessionhandler when disconnecting this session. + """ if self.logged_in: sessid = self.sessid @@ -136,21 +140,32 @@ class ServerSession(Session): def get_player(self): """ Get the player associated with this session + + Returns: + player (Player): The associated Player. + """ return self.logged_in and self.player def get_puppet(self): """ - Returns the in-game character associated with this session. - This returns the typeclass of the object. + Get the in-game character associated with this session. + + Returns: + puppet (Object): The puppeted object, if any. + """ return self.logged_in and self.puppet get_character = get_puppet def get_puppet_or_player(self): """ - Returns session if not logged in; puppet if one exists, - otherwise return the player. + Get puppet or player. + + Returns: + controller (Object or Player): The puppet if one exists, + otherwise return the player. + """ if self.logged_in: return self.puppet if self.puppet else self.player @@ -159,6 +174,12 @@ class ServerSession(Session): def log(self, message, channel=True): """ Emits session info to the appropriate outputs and info channels. + + Args: + message (str): The message to log. + channel (bool, optional): Log to the CHANNEL_CONNECTINFO channel + in addition to the server log. + """ if channel: try: @@ -173,7 +194,8 @@ class ServerSession(Session): """ Return eventual eventual width and height reported by the client. Note that this currently only deals with a single - client window (windowID==0) as in traditional telnet session + client window (windowID==0) as in a traditional telnet session. + """ flags = self.protocol_flags width = flags.get('SCREENWIDTH', {}).get(0, settings.CLIENT_DEFAULT_WIDTH) @@ -182,8 +204,9 @@ class ServerSession(Session): def update_session_counters(self, idle=False): """ - Hit this when the user enters a command in order to update idle timers - and command counters. + Hit this when the user enters a command in order to update + idle timers and command counters. + """ # Store the timestamp of the user's last command. if not idle: @@ -194,12 +217,16 @@ class ServerSession(Session): def data_in(self, text=None, **kwargs): """ - Send User->Evennia. This will in effect - execute a command string on the server. + Send data User->Evennia. This will in effect execute a command + string on the server. - Note that oob data is already sent to the + Note that oob data is already sent separately to the oobhandler at this point. + Kwargs: + text (str): A text to relay + kwargs (any): Other parameters from the protocol. + """ #explicitly check for None since text can be an empty string, which is #also valid @@ -227,6 +254,11 @@ class ServerSession(Session): def data_out(self, text=None, **kwargs): """ Send Evennia -> User + + Kwargs: + text (str): A text to relay + kwargs (any): Other parameters to the protocol. + """ text = text if text else "" if _INLINEFUNC_ENABLED and not "raw" in kwargs: @@ -244,12 +276,14 @@ class ServerSession(Session): msg = data_out def __eq__(self, other): + "Handle session comparisons" return self.address == other.address def __str__(self): """ String representation of the user session class. We use this a lot in the server logs. + """ symbol = "" if self.logged_in and hasattr(self, "player") and self.player: @@ -264,15 +298,16 @@ 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): - "dummy hook all objects with cmdsets need to have" + """ + A dummy hook all objects with cmdsets need to have + """ + pass # Mock db/ndb properties for allowing easy storage on the session @@ -286,6 +321,7 @@ class ServerSession(Session): to this is guaranteed to be cleared when a server is shutdown. Syntax is same as for the _get_db_holder() method and property, e.g. obj.ndb.attr = value etc. + """ try: return self._ndb_holder @@ -307,7 +343,13 @@ class ServerSession(Session): #@ndb.setter def ndb_set(self, value): - "Stop accidentally replacing the db object" + """ + Stop accidentally replacing the db object + + Args: + value (any): A value to store in the ndb. + + """ string = "Cannot assign directly to ndb object! " string = "Use ndb.attr=value instead." raise Exception(string) @@ -322,5 +364,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." + "Dummy method to mimic the logged-in API." return True diff --git a/evennia/server/session.py b/evennia/server/session.py index b353b1688..02e7c507c 100644 --- a/evennia/server/session.py +++ b/evennia/server/session.py @@ -1,6 +1,6 @@ """ -This defines a generic session class. All connection instances (both -on Portal and Server side) should inherit from this class. +This module defines a generic session class. All connection instances +(both on Portal and Server side) should inherit from this class. """ @@ -18,13 +18,13 @@ class Session(object): Each connection will see two session instances created: - 1) A Portal session. This is customized for the respective connection + 1. A Portal session. This is customized for the respective connection protocols that Evennia supports, like Telnet, SSH etc. The Portal session must call init_session() as part of its initialization. The respective hook methods should be connected to the methods unique for the respective protocol so that there is a unified interface to Evennia. - 2) A Server session. This is the same for all connected players, + 2. A Server session. This is the same for all connected players, regardless of how they connect. The Portal and Server have their own respective sessionhandlers. These @@ -44,9 +44,14 @@ class Session(object): """ Initialize the Session. This should be called by the protocol when a new session is established. - protocol_key - telnet, ssh, ssl or web - address - client address - sessionhandler - reference to the sessionhandler instance + + Args: + protocol_key (str): By default, one of 'telnet', 'ssh', + 'ssl' or 'web'. + address (str): Client address. + sessionhandler (SessionHandler): Reference to the + main sessionhandler instance. + """ # This is currently 'telnet', 'ssh', 'ssl' or 'web' self.protocol_key = protocol_key @@ -85,15 +90,24 @@ class Session(object): def get_sync_data(self): """ - Return all data relevant to sync the session + Get all data relevant to sync the session. + + Args: + syncdata (dict): All syncdata values, based on + the keys given by self._attrs_to_sync. + """ return dict((key, value) for key, value in self.__dict__.items() if key in self._attrs_to_sync) def load_sync_data(self, sessdata): """ - Takes a session dictionary, as created by get_sync_data, - and loads it into the correct properties of the session. + Takes a session dictionary, as created by get_sync_data, and + loads it into the correct properties of the session. + + Args: + sessdata (dict): Session data dictionary. + """ for propname, value in sessdata.items(): setattr(self, propname, value) @@ -103,6 +117,7 @@ class Session(object): Called after a session has been fully synced (including secondary operations such as setting self.player based on uid etc). + """ pass @@ -112,19 +127,33 @@ class Session(object): """ generic hook called from the outside to disconnect this session should be connected to the protocols actual disconnect mechanism. + + Args: + reason (str): Eventual text motivating the disconnect. + """ pass def data_out(self, text=None, **kwargs): """ - generic hook for sending data out through the protocol. Server + Generic hook for sending data out through the protocol. Server protocols can use this right away. Portal sessions should overload this to format/handle the outgoing data as needed. + + Kwargs: + text (str): Text data + kwargs (any): Other data to the protocol. + """ pass def data_in(self, text=None, **kwargs): """ - hook for protocols to send incoming data to the engine. + Hook for protocols to send incoming data to the engine. + + Kwargs: + text (str): Text data + kwargs (any): Other data from the protocol. + """ pass diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index dd9d23005..f1b809cdd 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -3,10 +3,11 @@ This module defines handlers for storing sessions when handles sessions of users connecting to the server. There are two similar but separate stores of sessions: - ServerSessionHandler - this stores generic game sessions + + - ServerSessionHandler - this stores generic game sessions for the game. These sessions has no knowledge about how they are connected to the world. - PortalSessionHandler - this stores sessions created by + - PortalSessionHandler - this stores sessions created by twisted protocols. These are dumb connectors that handle network communication but holds no game info. @@ -53,7 +54,10 @@ _MAX_SERVER_COMMANDS_PER_SECOND = 100.0 _MAX_SESSION_COMMANDS_PER_SECOND = 5.0 def delayed_import(): - "Helper method for delayed import of all needed entities" + """ + Helper method for delayed import of all needed entities. + + """ global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB if not _ServerSession: # we allow optional arbitrary serversession class for overloading @@ -76,16 +80,26 @@ def delayed_import(): class SessionHandler(object): """ This handler holds a stack of sessions. + """ def __init__(self): """ Init the handler. + """ self.sessions = {} def get_sessions(self, include_unloggedin=False): """ Returns the connected session objects. + + Args: + include_unloggedin (bool, optional): Also list Sessions + that have not yet authenticated. + + Returns: + sessions (list): A list of `Session` objects. + """ if include_unloggedin: return self.sessions.values() @@ -94,7 +108,14 @@ class SessionHandler(object): def get_session(self, sessid): """ - Get session by sessid + Get session by sessid. + + Args: + sessid (int): Session id. + + Returns: + session (Session): A `Session` object, if found. + """ return self.sessions.get(sessid, None) @@ -102,6 +123,10 @@ class SessionHandler(object): """ Create a dictionary of sessdata dicts representing all sessions in store. + + Returns: + syncdata (dict): A dict of sync data. + """ return dict((sessid, sess.get_sync_data()) for sessid, sess in self.sessions.items()) @@ -128,26 +153,29 @@ class ServerSessionHandler(SessionHandler): def __init__(self): """ Init the handler. + """ self.sessions = {} self.server = None self.server_data = {"servername": _SERVERNAME} - def portal_connect(self, portalsession): + def portal_connect(self, portalsessiondata): """ Called by Portal when a new session has connected. Creates a new, unlogged-in game session. - portalsession is a dictionary of all property:value keys - defining the session and which is marked to - be synced. + Args: + portalsessiondata (dict): a dictionary of all property:value + keys defining the session and which is marked to be + synced. + """ delayed_import() global _ServerSession, _PlayerDB, _ScriptDB sess = _ServerSession() sess.sessionhandler = self - sess.load_sync_data(portalsession) + sess.load_sync_data(portalsessiondata) if sess.logged_in and sess.uid: # this can happen in the case of auto-authenticating # protocols like SSH @@ -162,6 +190,12 @@ class ServerSessionHandler(SessionHandler): """ Called by Portal when it wants to update a single session (e.g. because of all negotiation protocols have finally replied) + + Args: + portalsessiondata (dict): a dictionary of all property:value + keys defining the session and which is marked to be + synced. + """ sessid = portalsessiondata.get("sessid") session = self.sessions.get(sessid) @@ -177,20 +211,26 @@ class ServerSessionHandler(SessionHandler): """ Called by Portal when portal reports a closing of a session from the portal side. + + Args: + sessid (int): Session id. + """ session = self.sessions.get(sessid, None) if not session: return self.disconnect(session) - def portal_sessions_sync(self, portalsessions): + def portal_sessions_sync(self, portalsessionsdata): """ Syncing all session ids of the portal with the ones of the server. This is instantiated by the portal when reconnecting. - portalsessions is a dictionary {sessid: {property:value},...} defining - each session and the properties in it which should - be synced. + Args: + portalsessionsdata (dict): A dictionary + `{sessid: {property:value},...}` defining each session and + the properties in it which should be synced. + """ delayed_import() global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB @@ -200,7 +240,7 @@ class ServerSessionHandler(SessionHandler): # lingering references. del sess - for sessid, sessdict in portalsessions.items(): + for sessid, sessdict in portalsessionsdata.items(): sess = _ServerSession() sess.sessionhandler = self sess.load_sync_data(sessdict) @@ -221,14 +261,26 @@ class ServerSessionHandler(SessionHandler): def start_bot_session(self, protocol_path, configdict): """ - This method allows the server-side to force the Portal to create - a new bot session using the protocol specified by protocol_path, - which should be the full python path to the class, including the - class name, like "evennia.server.portal.irc.IRCClient". - The new session will use the supplied player-bot uid to - initiate an already logged-in connection. The Portal will - treat this as a normal connection and henceforth so will the - Server. + This method allows the server-side to force the Portal to + create a new bot session. + + Args: + protocol_path (str): The full python path to the bot's + class. + configdict (dict): This dict will be used to configure + the bot (this depends on the bot protocol). + + Examples: + start_bot_session("evennia.server.portal.irc.IRCClient", + {"uid":1, "botname":"evbot", "channel":"#evennia", + "network:"irc.freenode.net", "port": 6667}) + + Notes: + The new session will use the supplied player-bot uid to + initiate an already logged-in connection. The Portal will + treat this as a normal connection and henceforth so will + the Server. + """ data = {"protocol_path":protocol_path, "config":configdict} @@ -239,6 +291,7 @@ class ServerSessionHandler(SessionHandler): def portal_shutdown(self): """ Called by server when shutting down the portal. + """ self.server.amp_protocol.call_remote_PortalAdmin(0, operation=SSHUTD, @@ -247,11 +300,15 @@ class ServerSessionHandler(SessionHandler): def login(self, session, player, testmode=False): """ Log in the previously unloggedin session and the player we by - now should know is connected to it. After this point we - assume the session to be logged in one way or another. + now should know is connected to it. After this point we assume + the session to be logged in one way or another. + + Args: + session (Session): The Session to authenticate. + player (Player): The Player identified as associated with this Session. + testmode (bool, optional): This is used by unittesting for + faking login without any AMP being actually active. - testmode - this is used by unittesting for faking login without - any AMP being actually active """ # we have to check this first before uid has been assigned @@ -295,6 +352,11 @@ class ServerSessionHandler(SessionHandler): """ Called from server side to remove session and inform portal of this fact. + + Args: + session (Session): The Session to disconnect. + reason (str, optional): A motivation for the disconnect. + """ session = self.sessions.get(session.sessid) if not session: @@ -319,6 +381,7 @@ class ServerSessionHandler(SessionHandler): """ This is called by the server when it reboots. It syncs all session data to the portal. Returns a deferred! + """ sessdata = self.get_all_sync_data() return self.server.amp_protocol.call_remote_PortalAdmin(0, @@ -328,6 +391,10 @@ class ServerSessionHandler(SessionHandler): def disconnect_all_sessions(self, reason="You have been disconnected."): """ Cleanly disconnect all of the connected sessions. + + Args: + reason (str, optional): The reason for the disconnection. + """ for session in self.sessions: @@ -341,6 +408,11 @@ class ServerSessionHandler(SessionHandler): reason=_("Logged in from elsewhere. Disconnecting.")): """ Disconnects any existing sessions with the same user. + + args: + curr_session (Session): Disconnect all Sessions matching this one. + reason (str, optional): A motivation for disconnecting. + """ uid = curr_session.uid doublet_sessions = [sess for sess in self.sessions.values() @@ -352,8 +424,9 @@ class ServerSessionHandler(SessionHandler): def validate_sessions(self): """ - Check all currently connected sessions (logged in and not) - and see if any are dead or idle + Check all currently connected sessions (logged in and not) and + see if any are dead or idle. + """ tcurr = time() reason = _("Idle timeout exceeded, disconnecting.") @@ -387,7 +460,14 @@ class ServerSessionHandler(SessionHandler): def session_from_sessid(self, sessid): """ - Return session based on sessid, or None if not found + Get session based on sessid, or None if not found + + Args: + sessid (int or list): Session id(s) + + Return: + sessions (Session or list): Session(s) found. + """ if is_iter(sessid): return [self.sessions.get(sid) for sid in sessid if sid in self.sessions] @@ -395,7 +475,16 @@ class ServerSessionHandler(SessionHandler): def session_from_player(self, player, sessid): """ - Given a player and a session id, return the actual session object + Given a player and a session id, return the actual session + object. + + Args: + player (Player): The Player to get the Session from. + sessid (int or list): Session id(s). + + Returns: + sessions (Session or list): Session(s) found. + """ if is_iter(sessid): sessions = [self.sessions.get(sid) for sid in sessid] @@ -407,6 +496,13 @@ class ServerSessionHandler(SessionHandler): def sessions_from_player(self, player): """ Given a player, return all matching sessions. + + Args: + player (Player): Player to get sessions from. + + Returns: + sessions (list): All Sessions associated with this player. + """ uid = player.uid return [session for session in self.sessions.values() if session.logged_in and session.uid == uid] @@ -414,6 +510,14 @@ class ServerSessionHandler(SessionHandler): def sessions_from_puppet(self, puppet): """ Given a puppeted object, return all controlling sessions. + + Args: + puppet (Object): Object puppeted + + Returns. + sessions (Session or list): Can be more than one of Object is controlled by + more than one Session (MULTISESSION_MODE > 1). + """ sessid = puppet.sessid.get() if is_iter(sessid): @@ -424,6 +528,10 @@ class ServerSessionHandler(SessionHandler): def announce_all(self, message): """ Send message to all connected sessions + + Args: + message (str): Message to send. + """ for sess in self.sessions.values(): self.data_out(sess, message) @@ -482,6 +590,14 @@ class ServerSessionHandler(SessionHandler): """ Data Portal -> Server. We also intercept OOB communication here. + + Args: + sessid (int): Session id. + + Kwargs: + text (str): Text from protocol. + kwargs (any): Other data from protocol. + """ session = self.sessions.get(sessid, None) if session: