Updated more of server/ to google docstrings as per #709.

This commit is contained in:
Griatch 2015-06-23 10:42:51 +02:00
parent 57b2396af7
commit 00b5309295
3 changed files with 251 additions and 64 deletions

View file

@ -60,12 +60,13 @@ class ServerSession(Session):
def at_sync(self): def at_sync(self):
""" """
This is called whenever a session has been resynced with the portal. This is called whenever a session has been resynced with the
At this point all relevant attributes have already been set and portal. At this point all relevant attributes have already
self.player been assigned (if applicable). 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 global _ObjectDB
if not _ObjectDB: if not _ObjectDB:
@ -94,7 +95,9 @@ class ServerSession(Session):
""" """
Hook called by sessionhandler when the session becomes authenticated. 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.player = player
self.uid = self.player.id self.uid = self.player.id
@ -115,6 +118,7 @@ class ServerSession(Session):
def at_disconnect(self): def at_disconnect(self):
""" """
Hook called by sessionhandler when disconnecting this session. Hook called by sessionhandler when disconnecting this session.
""" """
if self.logged_in: if self.logged_in:
sessid = self.sessid sessid = self.sessid
@ -136,21 +140,32 @@ class ServerSession(Session):
def get_player(self): def get_player(self):
""" """
Get the player associated with this session Get the player associated with this session
Returns:
player (Player): The associated Player.
""" """
return self.logged_in and self.player return self.logged_in and self.player
def get_puppet(self): def get_puppet(self):
""" """
Returns the in-game character associated with this session. Get the in-game character associated with this session.
This returns the typeclass of the object.
Returns:
puppet (Object): The puppeted object, if any.
""" """
return self.logged_in and self.puppet return self.logged_in and self.puppet
get_character = get_puppet get_character = get_puppet
def get_puppet_or_player(self): def get_puppet_or_player(self):
""" """
Returns session if not logged in; puppet if one exists, Get puppet or player.
otherwise return the player.
Returns:
controller (Object or Player): The puppet if one exists,
otherwise return the player.
""" """
if self.logged_in: if self.logged_in:
return self.puppet if self.puppet else self.player return self.puppet if self.puppet else self.player
@ -159,6 +174,12 @@ class ServerSession(Session):
def log(self, message, channel=True): def log(self, message, channel=True):
""" """
Emits session info to the appropriate outputs and info channels. 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: if channel:
try: try:
@ -173,7 +194,8 @@ class ServerSession(Session):
""" """
Return eventual eventual width and height reported by the Return eventual eventual width and height reported by the
client. Note that this currently only deals with a single 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 flags = self.protocol_flags
width = flags.get('SCREENWIDTH', {}).get(0, settings.CLIENT_DEFAULT_WIDTH) width = flags.get('SCREENWIDTH', {}).get(0, settings.CLIENT_DEFAULT_WIDTH)
@ -182,8 +204,9 @@ class ServerSession(Session):
def update_session_counters(self, idle=False): def update_session_counters(self, idle=False):
""" """
Hit this when the user enters a command in order to update idle timers Hit this when the user enters a command in order to update
and command counters. idle timers and command counters.
""" """
# Store the timestamp of the user's last command. # Store the timestamp of the user's last command.
if not idle: if not idle:
@ -194,12 +217,16 @@ class ServerSession(Session):
def data_in(self, text=None, **kwargs): def data_in(self, text=None, **kwargs):
""" """
Send User->Evennia. This will in effect Send data User->Evennia. This will in effect execute a command
execute a command string on the server. 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. 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 #explicitly check for None since text can be an empty string, which is
#also valid #also valid
@ -227,6 +254,11 @@ class ServerSession(Session):
def data_out(self, text=None, **kwargs): def data_out(self, text=None, **kwargs):
""" """
Send Evennia -> User Send Evennia -> User
Kwargs:
text (str): A text to relay
kwargs (any): Other parameters to the protocol.
""" """
text = text if text else "" text = text if text else ""
if _INLINEFUNC_ENABLED and not "raw" in kwargs: if _INLINEFUNC_ENABLED and not "raw" in kwargs:
@ -244,12 +276,14 @@ class ServerSession(Session):
msg = data_out msg = data_out
def __eq__(self, other): def __eq__(self, other):
"Handle session comparisons"
return self.address == other.address return self.address == other.address
def __str__(self): def __str__(self):
""" """
String representation of the user session class. We use String representation of the user session class. We use
this a lot in the server logs. this a lot in the server logs.
""" """
symbol = "" symbol = ""
if self.logged_in and hasattr(self, "player") and self.player: 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) return "%s%s@%s" % (self.uname, symbol, address)
def __unicode__(self): def __unicode__(self):
""" "Unicode representation"
Unicode representation
"""
return u"%s" % str(self) return u"%s" % str(self)
# Dummy API hooks for use during non-loggedin operation # Dummy API hooks for use during non-loggedin operation
def at_cmdset_get(self, **kwargs): 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 pass
# Mock db/ndb properties for allowing easy storage on the session # 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. to this is guaranteed to be cleared when a server is shutdown.
Syntax is same as for the _get_db_holder() method and Syntax is same as for the _get_db_holder() method and
property, e.g. obj.ndb.attr = value etc. property, e.g. obj.ndb.attr = value etc.
""" """
try: try:
return self._ndb_holder return self._ndb_holder
@ -307,7 +343,13 @@ class ServerSession(Session):
#@ndb.setter #@ndb.setter
def ndb_set(self, value): 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 = "Cannot assign directly to ndb object! "
string = "Use ndb.attr=value instead." string = "Use ndb.attr=value instead."
raise Exception(string) raise Exception(string)
@ -322,5 +364,5 @@ class ServerSession(Session):
# Mock access method for the session (there is no lock info # Mock access method for the session (there is no lock info
# at this stage, so we just present a uniform API) # at this stage, so we just present a uniform API)
def access(self, *args, **kwargs): def access(self, *args, **kwargs):
"Dummy method." "Dummy method to mimic the logged-in API."
return True return True

View file

@ -1,6 +1,6 @@
""" """
This defines a generic session class. All connection instances (both This module defines a generic session class. All connection instances
on Portal and Server side) should inherit from this class. (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: 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 protocols that Evennia supports, like Telnet, SSH etc. The Portal
session must call init_session() as part of its initialization. The session must call init_session() as part of its initialization. The
respective hook methods should be connected to the methods unique respective hook methods should be connected to the methods unique
for the respective protocol so that there is a unified interface for the respective protocol so that there is a unified interface
to Evennia. 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. regardless of how they connect.
The Portal and Server have their own respective sessionhandlers. These 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 Initialize the Session. This should be called by the protocol when
a new session is established. a new session is established.
protocol_key - telnet, ssh, ssl or web
address - client address Args:
sessionhandler - reference to the sessionhandler instance 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' # This is currently 'telnet', 'ssh', 'ssl' or 'web'
self.protocol_key = protocol_key self.protocol_key = protocol_key
@ -85,15 +90,24 @@ class Session(object):
def get_sync_data(self): 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() return dict((key, value) for key, value in self.__dict__.items()
if key in self._attrs_to_sync) if key in self._attrs_to_sync)
def load_sync_data(self, sessdata): def load_sync_data(self, sessdata):
""" """
Takes a session dictionary, as created by get_sync_data, Takes a session dictionary, as created by get_sync_data, and
and loads it into the correct properties of the session. loads it into the correct properties of the session.
Args:
sessdata (dict): Session data dictionary.
""" """
for propname, value in sessdata.items(): for propname, value in sessdata.items():
setattr(self, propname, value) setattr(self, propname, value)
@ -103,6 +117,7 @@ class Session(object):
Called after a session has been fully synced (including Called after a session has been fully synced (including
secondary operations such as setting self.player based secondary operations such as setting self.player based
on uid etc). on uid etc).
""" """
pass pass
@ -112,19 +127,33 @@ class Session(object):
""" """
generic hook called from the outside to disconnect this session generic hook called from the outside to disconnect this session
should be connected to the protocols actual disconnect mechanism. should be connected to the protocols actual disconnect mechanism.
Args:
reason (str): Eventual text motivating the disconnect.
""" """
pass pass
def data_out(self, text=None, **kwargs): 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 protocols can use this right away. Portal sessions
should overload this to format/handle the outgoing data as needed. should overload this to format/handle the outgoing data as needed.
Kwargs:
text (str): Text data
kwargs (any): Other data to the protocol.
""" """
pass pass
def data_in(self, text=None, **kwargs): 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 pass

View file

@ -3,10 +3,11 @@ This module defines handlers for storing sessions when handles
sessions of users connecting to the server. sessions of users connecting to the server.
There are two similar but separate stores of sessions: 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 for the game. These sessions has no knowledge about
how they are connected to the world. 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 twisted protocols. These are dumb connectors that
handle network communication but holds no game info. 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 _MAX_SESSION_COMMANDS_PER_SECOND = 5.0
def delayed_import(): 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 global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
if not _ServerSession: if not _ServerSession:
# we allow optional arbitrary serversession class for overloading # we allow optional arbitrary serversession class for overloading
@ -76,16 +80,26 @@ def delayed_import():
class SessionHandler(object): class SessionHandler(object):
""" """
This handler holds a stack of sessions. This handler holds a stack of sessions.
""" """
def __init__(self): def __init__(self):
""" """
Init the handler. Init the handler.
""" """
self.sessions = {} self.sessions = {}
def get_sessions(self, include_unloggedin=False): def get_sessions(self, include_unloggedin=False):
""" """
Returns the connected session objects. 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: if include_unloggedin:
return self.sessions.values() return self.sessions.values()
@ -94,7 +108,14 @@ class SessionHandler(object):
def get_session(self, sessid): 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) return self.sessions.get(sessid, None)
@ -102,6 +123,10 @@ class SessionHandler(object):
""" """
Create a dictionary of sessdata dicts representing all Create a dictionary of sessdata dicts representing all
sessions in store. 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()) return dict((sessid, sess.get_sync_data()) for sessid, sess in self.sessions.items())
@ -128,26 +153,29 @@ class ServerSessionHandler(SessionHandler):
def __init__(self): def __init__(self):
""" """
Init the handler. Init the handler.
""" """
self.sessions = {} self.sessions = {}
self.server = None self.server = None
self.server_data = {"servername": _SERVERNAME} self.server_data = {"servername": _SERVERNAME}
def portal_connect(self, portalsession): def portal_connect(self, portalsessiondata):
""" """
Called by Portal when a new session has connected. Called by Portal when a new session has connected.
Creates a new, unlogged-in game session. Creates a new, unlogged-in game session.
portalsession is a dictionary of all property:value keys Args:
defining the session and which is marked to portalsessiondata (dict): a dictionary of all property:value
be synced. keys defining the session and which is marked to be
synced.
""" """
delayed_import() delayed_import()
global _ServerSession, _PlayerDB, _ScriptDB global _ServerSession, _PlayerDB, _ScriptDB
sess = _ServerSession() sess = _ServerSession()
sess.sessionhandler = self sess.sessionhandler = self
sess.load_sync_data(portalsession) sess.load_sync_data(portalsessiondata)
if sess.logged_in and sess.uid: if sess.logged_in and sess.uid:
# this can happen in the case of auto-authenticating # this can happen in the case of auto-authenticating
# protocols like SSH # protocols like SSH
@ -162,6 +190,12 @@ class ServerSessionHandler(SessionHandler):
""" """
Called by Portal when it wants to update a single session (e.g. Called by Portal when it wants to update a single session (e.g.
because of all negotiation protocols have finally replied) 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") sessid = portalsessiondata.get("sessid")
session = self.sessions.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 Called by Portal when portal reports a closing of a session
from the portal side. from the portal side.
Args:
sessid (int): Session id.
""" """
session = self.sessions.get(sessid, None) session = self.sessions.get(sessid, None)
if not session: if not session:
return return
self.disconnect(session) 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 Syncing all session ids of the portal with the ones of the
server. This is instantiated by the portal when reconnecting. server. This is instantiated by the portal when reconnecting.
portalsessions is a dictionary {sessid: {property:value},...} defining Args:
each session and the properties in it which should portalsessionsdata (dict): A dictionary
be synced. `{sessid: {property:value},...}` defining each session and
the properties in it which should be synced.
""" """
delayed_import() delayed_import()
global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
@ -200,7 +240,7 @@ class ServerSessionHandler(SessionHandler):
# lingering references. # lingering references.
del sess del sess
for sessid, sessdict in portalsessions.items(): for sessid, sessdict in portalsessionsdata.items():
sess = _ServerSession() sess = _ServerSession()
sess.sessionhandler = self sess.sessionhandler = self
sess.load_sync_data(sessdict) sess.load_sync_data(sessdict)
@ -221,14 +261,26 @@ class ServerSessionHandler(SessionHandler):
def start_bot_session(self, protocol_path, configdict): def start_bot_session(self, protocol_path, configdict):
""" """
This method allows the server-side to force the Portal to create This method allows the server-side to force the Portal to
a new bot session using the protocol specified by protocol_path, create a new bot session.
which should be the full python path to the class, including the
class name, like "evennia.server.portal.irc.IRCClient". Args:
The new session will use the supplied player-bot uid to protocol_path (str): The full python path to the bot's
initiate an already logged-in connection. The Portal will class.
treat this as a normal connection and henceforth so will the configdict (dict): This dict will be used to configure
Server. 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, data = {"protocol_path":protocol_path,
"config":configdict} "config":configdict}
@ -239,6 +291,7 @@ class ServerSessionHandler(SessionHandler):
def portal_shutdown(self): def portal_shutdown(self):
""" """
Called by server when shutting down the portal. Called by server when shutting down the portal.
""" """
self.server.amp_protocol.call_remote_PortalAdmin(0, self.server.amp_protocol.call_remote_PortalAdmin(0,
operation=SSHUTD, operation=SSHUTD,
@ -247,11 +300,15 @@ class ServerSessionHandler(SessionHandler):
def login(self, session, player, testmode=False): def login(self, session, player, testmode=False):
""" """
Log in the previously unloggedin session and the player we by Log in the previously unloggedin session and the player we by
now should know is connected to it. After this point we now should know is connected to it. After this point we assume
assume the session to be logged in one way or another. 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 # 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 Called from server side to remove session and inform portal
of this fact. of this fact.
Args:
session (Session): The Session to disconnect.
reason (str, optional): A motivation for the disconnect.
""" """
session = self.sessions.get(session.sessid) session = self.sessions.get(session.sessid)
if not session: if not session:
@ -319,6 +381,7 @@ class ServerSessionHandler(SessionHandler):
""" """
This is called by the server when it reboots. It syncs all session data This is called by the server when it reboots. It syncs all session data
to the portal. Returns a deferred! to the portal. Returns a deferred!
""" """
sessdata = self.get_all_sync_data() sessdata = self.get_all_sync_data()
return self.server.amp_protocol.call_remote_PortalAdmin(0, 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."): def disconnect_all_sessions(self, reason="You have been disconnected."):
""" """
Cleanly disconnect all of the connected sessions. Cleanly disconnect all of the connected sessions.
Args:
reason (str, optional): The reason for the disconnection.
""" """
for session in self.sessions: for session in self.sessions:
@ -341,6 +408,11 @@ class ServerSessionHandler(SessionHandler):
reason=_("Logged in from elsewhere. Disconnecting.")): reason=_("Logged in from elsewhere. Disconnecting.")):
""" """
Disconnects any existing sessions with the same user. 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 uid = curr_session.uid
doublet_sessions = [sess for sess in self.sessions.values() doublet_sessions = [sess for sess in self.sessions.values()
@ -352,8 +424,9 @@ class ServerSessionHandler(SessionHandler):
def validate_sessions(self): def validate_sessions(self):
""" """
Check all currently connected sessions (logged in and not) Check all currently connected sessions (logged in and not) and
and see if any are dead or idle see if any are dead or idle.
""" """
tcurr = time() tcurr = time()
reason = _("Idle timeout exceeded, disconnecting.") reason = _("Idle timeout exceeded, disconnecting.")
@ -387,7 +460,14 @@ class ServerSessionHandler(SessionHandler):
def session_from_sessid(self, sessid): 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): if is_iter(sessid):
return [self.sessions.get(sid) for sid in sessid if sid in self.sessions] 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): 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): if is_iter(sessid):
sessions = [self.sessions.get(sid) for sid in sessid] sessions = [self.sessions.get(sid) for sid in sessid]
@ -407,6 +496,13 @@ class ServerSessionHandler(SessionHandler):
def sessions_from_player(self, player): def sessions_from_player(self, player):
""" """
Given a player, return all matching sessions. 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 uid = player.uid
return [session for session in self.sessions.values() if session.logged_in and session.uid == 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): def sessions_from_puppet(self, puppet):
""" """
Given a puppeted object, return all controlling sessions. 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() sessid = puppet.sessid.get()
if is_iter(sessid): if is_iter(sessid):
@ -424,6 +528,10 @@ class ServerSessionHandler(SessionHandler):
def announce_all(self, message): def announce_all(self, message):
""" """
Send message to all connected sessions Send message to all connected sessions
Args:
message (str): Message to send.
""" """
for sess in self.sessions.values(): for sess in self.sessions.values():
self.data_out(sess, message) self.data_out(sess, message)
@ -482,6 +590,14 @@ class ServerSessionHandler(SessionHandler):
""" """
Data Portal -> Server. Data Portal -> Server.
We also intercept OOB communication here. 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) session = self.sessions.get(sessid, None)
if session: if session: