ГЛАВНАЯ      ДОКУМЕНТАЦИЯ      СТАТЬИ      ПРОГРАММЫ      ССЫЛКИ      ФОРУМ      ДРУГОЕ   

От Perl-скрипта к Twisted-приложению: Черновик

        Итак, в наличии есть работающий Perl-скрипт и некое описания протокола (некое, потому что это логи уже работающего "концентратора" и небольшие комментарии по тексту).
Согласно принятому дизайну Twisted-приложений, общий функционал разбивается на модули, причем разделяется реализация собственно протокола и "сторонней логики". В нашем случае, "сторонняя логика" - это выборка имени человека по номеру договора.
Поскольку у товарища стоит такая СУБД, которой у меня нет, да и особо ставить не хочется, я ввел, опять-таки общепринятый для Twisted-приложений, механизм интерфейсов и классов, реализующих их.
Поясню: мне необходимо на основании одних данных (номера договора) получать другие данные (фамилию, имя и отчество человека). Сделать это можно различными способами, данные могут храниться в различных СУБД и т.д. Поэтому я создаю класс-описание, интерфейс, который описывает какие методы и атрибуты необходимы для реализации, но саму реализацию не указывает. Общепринято такие классы-интерфейсы называть "IName", где Name — общее имя интерфейса. У меня получился такой интерфейс:
from zope.interface import Interface, Attribute

class IClient(Interface):
    """An object that returns info about client"""

    encoding = Attribute("Encoding of client's backend")

    def getClient(agreem_number):
        """Returns an info about client"""
В Twisted используется механизм интерфейсов из Zope. В отличии от "обычных" классов, в классах-интерфейсах не надо в методах ставить первым параметром self.
Итак, теперь у нас есть описание, каким должен быть класс: у него должен быть атрибут encoding (потому что кодировка СУБД может не совпадать с кодировкой транспорта) и метод getClient, получающий один параметр agreem_number.
Далее, реализуем "тупой" клиент в тестовых целях.
import time

from zope.interface import implements

# полагаем, что IClient у нас либо импортирован, либо находится
# в том же модуле

class DummyClient(object):
    """Dummy client for testing purposes"""
    implements(IClient)

    def __init__(self, encoding=None):
        self.encoding = encoding
        self.pause_time = 0

    def getClient(self, agreem_num):
        res = 'Dummy_%s' % agreem_num
        # в описании было одно требование: чтобы возвращаемый результат
        # не был длиннее 20 символов
        if len(res) > 20:
            res = res[:20]
        # для имитации задержки выборки данных
        time.sleep(self.pause_time)

        return res
Теперь у нас уже есть работающий клиент, так что пора браться за реализацию протокола. Поскольку в нашем случае протокол текстовый, т.е. данные передаются построчно, то я за основу взял LineReceiver.
from twisted.protocols import basic
from twisted.python import log

class PythyProto(basic.LineReceiver):
    """Simple text demo protocol"""

    def connectionMade(self):
        log.msg("got connection from %s" % str(self.transport.client))

    def connectionLost(self, reason):
        log.msg("connection from %s lost" % str(self.transport.client))

    def lineReceived(self, line):
        log.msg("data received from %s: `%s'" % (str(self.transport.client), line))
        if line == '':
            # для тестовых целей
            self.sendLine("Good bye")
            self.transport.loseConnection()
            return
        agr = line[10:15]
        client = self.factory.clients.getClient(agr)
        self.sendAnswer(client)

    def sendAnswer(self, client):
        packet = "dummypacketmaker012345678%s" % client
        self.sendLine(packet)

    def sendLine(self, line):
        assert isinstance(line, basestring)
        line = line.replace('r', '').replace('n', '')
        log.msg("data send: %s" % line)
        line = line + "rn"

        self.transport.write(line)
Экземпляр класса протокол создается для каждого соединения. Т.е. некая неизменная информация (например, кодировка данного протокола) не сохраняется, для сохранения такого рода информации существуют "фабрики" (factory):
from twisted.internet import protocol

# полагаем, что PythyProto у нас либо импортирован, либо находится
# в том же модуле

class PythyFactory(protocol.ServerFactory):
    protocol = PythyProto   # class, not instance!

    def __init__(self, clients, transport_encoding):
        self.clients = clients
        self.encoding = transport_encoding
Теперь собираем всё "в кучу" и пробуем запустить:
import sys

from twisted.internet import reactor
from twisted.python import log

from TwistedPythy import proto, clients

log.startLogging(sys.stderr)
client = clients.DummyClient()
client.pause_time = 0
factory = proto.PythyFactory(client, 'utf-8')
reactor.listenTCP(3000, factory)
reactor.run()
Первый рабочий "черновик" создан. Пока главное, что он вообще работает :-) Кодировки будут следующим этапом.
А для разминки советую попробовать поставить паузу в DummyClient секунд в 10-20 и попробовать одновременно сделать запрос двумя клиентами.
P.S. К сожалению, движок блога не дает загрузить архив исходников, поэтому я загрузил на RapidShare.

Источники:
        gorod-omsk.ru/blog/pythy/

ПОМОЩЬ САЙТУ :
sms.Є®ЇЁ«Є  *PythonUA*
Для чего Вы используете Python?
Admin( 46 )
Web( 61 )
GUI( 37 )
Embedding ( 16 )
Другое( 34 )
Какими продуктами Вы пользовались?
Zope( 15 )
Plone( 1 )
TG( 7 )
Django( 15 )
Twisted( 5 )
Другими( 10 )
ДРУЗЬЯ:
LUG.DN.UA
D-FENS.ORG.UA
SLAV0NIC.XSS.RU
CETUS.COM.UA
ENTDEV.ORG
[Python Powered]
Rambler's Top100
Copyright © 2006 python.com.ua