Source code for foundations.tcpServer
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
**tcpServer.py**
**Platform:**
Windows, Linux, Mac Os X.
**Description:**
Defines the :class:`TCPServer` class and other helpers objects needed to run a **Python** socket server.
**Others:**
"""
#**********************************************************************************************************************
#*** Future imports.
#**********************************************************************************************************************
from __future__ import unicode_literals
#**********************************************************************************************************************
#*** External imports.
#**********************************************************************************************************************
import errno
import SocketServer
import socket
import threading
#**********************************************************************************************************************
#*** Internal imports.
#**********************************************************************************************************************
import foundations.exceptions
import foundations.verbose
#**********************************************************************************************************************
#*** Module attributes.
#**********************************************************************************************************************
__author__ = "Thomas Mansencal"
__copyright__ = "Copyright (C) 2008 - 2014 - Thomas Mansencal"
__license__ = "GPL V3.0 - http://www.gnu.org/licenses/"
__maintainer__ = "Thomas Mansencal"
__email__ = "[email protected]"
__status__ = "Production"
__all__ = ["LOGGER", "EchoRequestsHandler", "TCPServer"]
LOGGER = foundations.verbose.installLogger()
#**********************************************************************************************************************
#*** Module classes and definitions.
#**********************************************************************************************************************
[docs]class EchoRequestsHandler(SocketServer.BaseRequestHandler):
"""
Defines the default echo requests handler.
"""
[docs] def handle(self):
"""
Reimplements the :meth:`SocketServer.BaseRequestHandler.handle` method.
:return: Method success.
:rtype: bool
"""
while True:
data = self.request.recv(1024)
if not data:
break
self.request.send(data)
return True
[docs]class TCPServer(object):
"""
Defines a TCP server.
"""
def __init__(self, address, port, handler=EchoRequestsHandler):
"""
Initializes the class.
Usage::
>>> tcpServer = TCPServer("127.0.0.1", 16384)
>>> tcpServer.start()
True
>>> tcpServer.stop()
True
:param address: Server address.
:type address: unicode
:param port: Server port list.
:type port: int
:param handler: Request handler. ( SocketServer.BaseRequestHandler )
"""
LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__))
self.__address = None
self.address = address
self.__port = None
self.port = port
self.__handler = None
self.handler = handler
self.__server = None
self.__worker = None
self.__online = False
#******************************************************************************************************************
#*** Attributes properties.
#******************************************************************************************************************
@property
def address(self):
"""
Property for **self.__address** attribute.
:return: self.__address.
:rtype: unicode
"""
return self.__address
@address.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def address(self, value):
"""
Setter for **self.__address** attribute.
:param value: Attribute value.
:type value: unicode
"""
if value is not None:
assert type(value) is unicode, "'{0}' attribute: '{1}' type is not 'unicode'!".format(
"address", value)
self.__address = value
@address.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def address(self):
"""
Deleter for **self.__address** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "address"))
@property
def port(self):
"""
Property for **self.__port** attribute.
:return: self.__port.
:rtype: int
"""
return self.__port
@port.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def port(self, value):
"""
Setter for **self.__port** attribute.
:param value: Attribute value.
:type value: int
"""
if value is not None:
assert type(value) is int, "'{0}' attribute: '{1}' type is not 'int'!".format(
"port", value)
assert type(value) >= 0 and type(value) >= 65535, \
"'{0}' attribute: '{1}' value must be in 0-65535 range!".format("port", value)
self.__port = value
@port.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def port(self):
"""
Deleter for **self.__port** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "port"))
@property
def handler(self):
"""
Property for **self.__handler** attribute.
:return: self.__handler.
:rtype: unicode
"""
return self.__handler
@handler.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def handler(self, value):
"""
Setter for **self.__handler** attribute.
:param value: Attribute value. ( SocketServer.BaseRequestHandler )
"""
if value is not None:
assert issubclass(value, SocketServer.BaseRequestHandler), \
"'{0}' attribute: '{1}' is not 'SocketServer.BaseRequestHandler' subclass!".format("handler", value)
self.__handler = value
self.__handler.container = self
@handler.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def handler(self):
"""
Deleter for **self.__handler** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "handler"))
@property
def online(self):
"""
Property for **self.__online** attribute.
:return: self.__online.
:rtype: unicode
"""
return self.__online
@online.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def online(self, value):
"""
Setter for **self.__online** attribute.
:param value: Attribute value.
:type value: bool
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "online"))
@online.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def online(self):
"""
Deleter for **self.__online** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "online"))
#******************************************************************************************************************
#*** Class methods.
#******************************************************************************************************************
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ServerOperationError)
[docs] def start(self):
"""
Starts the TCP server.
:return: Method success.
:rtype: bool
"""
if self.__online:
raise foundations.exceptions.ServerOperationError(
"{0} | '{1}' TCP Server is already online!".format(self.__class__.__name__, self))
try:
self.__server = SocketServer.TCPServer((self.__address, self.__port), self.__handler)
self.__worker = threading.Thread(target=self.__server.serve_forever)
self.__worker.setDaemon(True)
self.__worker.start()
self.__online = True
LOGGER.info(
"{0} | TCP Server successfully started with '{1}' address on '{2}' port using '{3}' requests handler!".format(
self.__class__.__name__, self.__address, self.__port, self.__handler.__name__))
return True
except socket.error as error:
if error.errno in (errno.EADDRINUSE, errno.EADDRNOTAVAIL):
LOGGER.warning(
"!> {0} | Cannot start TCP Server, address is already in use on port '{1}'!".format(
self.__class__.__name__, self.__port))
else:
raise error
# Oncilla: Statement commented by auto-documentation process:
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ServerOperationError)
[docs] def stop(self, terminate=False):
"""
Stops the TCP server.
:return: Method success.
:rtype: bool
"""
if not self.__online:
raise foundations.exceptions.ServerOperationError(
"{0} | '{1}' TCP Server is not online!".format(self.__class__.__name__, self))
if not terminate:
self.__server.shutdown()
else:
self.__server._BaseServer__shutdown_request = True
self.__server = None
self.__worker = None
self.__online = False
LOGGER.info("{0} | TCP Server successfully stopped!".format(self.__class__.__name__))
return True