Python 等效于 Perl 的 HTTP::Async->next_response

Posted

技术标签:

【中文标题】Python 等效于 Perl 的 HTTP::Async->next_response【英文标题】:Python equivalent of Perl's HTTP::Async->next_response 【发布时间】:2013-05-12 05:57:55 【问题描述】:

我正在寻找一种与 Perl 的 HTTP::Async 模块的 next_response 方法等效的方法

HTTP::Async 模块不产生任何后台线程,也不使用任何回调。相反,每次任何人(在我的例子中,主线程)在对象上调用 next_response 时,操作系统到目前为止接收到的所有数据都会被读取(阻塞,但是是瞬时的,因为它只处理已经接收到的数据)。如果这是响应的结尾,则 next_response 返回一个 HTTP::Response 对象,否则返回 undef。

这个模块的用法看起来像(伪代码):

request = HTTP::Async(url)
do:
    response = request->next_response()
    if not response:
        sleep 5 # or process events or whatever
while not response

# Do things with response

据我所知,Python 的 urllib 或 http.client 不支持这种风格。至于我为什么要做这种风格:

这适用于嵌入式 Python 环境,在该环境中我无法生成线程,也无法生成任何 Python。 我仅限于单个线程,它实际上是嵌入应用程序的线程。这意味着我也不能有任何延迟的回调——应用程序决定何时让我的 Python 代码运行。我所能做的就是请求嵌入应用程序每隔 50 毫秒调用一次我选择的回调。

有没有办法在 Python 中做到这一点?

作为参考,这是我现在拥有的 Perl 代码示例,我希望将其移植到 Python:

httpAsync = HTTP::Async->new()

sub httpRequestAsync 
    my ($url, $callback) = @_; # $callback will be called with the response text

    $httpAsync->add(new HTTP::Request(GET => $url));

    # create_timer causes the embedding application to call the supplied callback every 50ms
    application::create_timer(50, sub 
        my $timer_result = application::keep_timer;
        my $response = $httpAsync->next_response;
        if ($response) 
            my $responseText = $response->decoded_content;
            if ($responseText) 
                $callback->($responseText);
            
            $timer_result = application::remove_timer;
        

        # Returning application::keep_timer will preserve the timer to be called again.
        # Returning application::remove_timer will remove the timer.
        return $timer_result;
    );


httpRequestAsync('http://www.example.com/', sub 
    my $responseText = $_[0];
    application::display($responseText);
);

编辑:鉴于这是针对嵌入式 Python 实例,我将采用所有我能获得的替代方案(标准库的一部分或其他),因为我必须评估所有这些替代方案以确保它们可以运行在我的特殊限制下。

【问题讨论】:

【参考方案1】:

注意:如果您只想在调用接收数据时检索数据,只需向handle_receive 添加一个标志并将其添加到handle_receive 内的睡眠块中,这样只有在您调用时才为您提供数据你的功能。

#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
import asyncore, errno
from socket import AF_INET, SOCK_STREAM
from time import sleep

class sender():
    def __init__(self, sock_send):
        self.s = sock_send
        self.bufferpos = 0
        self.buffer = 
        self.alive = 1

    def send(self, what):
        self.buffer[len(self.buffer)] = what

    def writable(self):
        return (len(self.buffer) > self.bufferpos)

    def run(self):
        while self.alive:
            if self.writable():
                logout = str([self.buffer[self.bufferpos]])
                self.s(self.buffer[self.bufferpos])
                self.bufferpos += 1
            sleep(0.01)

class SOCK(asyncore.dispatcher):
    def __init__(self, _s=None, config=None):
        self.conf = config
        Thread.__init__(self)

        self._s = _s

        self.inbuffer = ''
        #self.buffer = ''
        self.lockedbuffer = False
        self.is_writable = False

        self.autounlockAccounts = 

        if _s:
            asyncore.dispatcher.__init__(self, _s)
            self.sender = sender(self.send)

        else:
            asyncore.dispatcher.__init__(self)
            self.create_socket(AF_INET, SOCK_STREAM)
            #if self.allow_reuse_address:
            #   self.set_resue_addr()

            self.bind((self.conf['SERVER'], self.conf['PORT']))
            self.listen(5)

            self.sender = None

        self.start()

    def parse(self):
        self.lockedbuffer = True

        ## Parse here
        print self.inbuffer

        self.inbuffer = ''
        self.lockedbuffer = False

    def readable(self):
        return True
    def handle_connect(self):
        pass
    def handle_accept(self):
        (conn_sock, client_address) = self.accept()
        if self.verify_request(conn_sock, client_address):
            self.process_request(conn_sock, client_address)
    def process_request(self, sock, addr):
        x = SOCK(sock, config='PARSER' : self.conf['PARSER'], 'ADDR' : addr[0], 'NAME' : 'CORE_SUB_SOCK_('+str(addr[0]) + ')')
    def verify_request(self, conn_sock, client_address):
        return True
    def handle_close(self):
        self.close()
            if self.sender:
                self.sender.alive = False
    def handle_read(self):
        data = self.recv(8192)
        while self.lockedbuffer:
            sleep(0.01)
        self.inbuffer += data
    def writable(self):
        return True
    def handle_write(self):
        pass

    def run(self):
            if not self._s:
            asyncore.loop()

imap = SOCK(config='SERVER' : '', 'PORT' : 6668)
imap.run()

while 1
    sleep(1)

类似的东西? 当有数据要接收时,总是附加到 inbuffer 的异步套接字。

你可以随心所欲地修改它,我只是从另一个恰好是线程的项目中粘贴了一段代码:)

最后一次尝试:

class EchoHandler(asyncore.dispatcher_with_send):

    def handle_read(self):
        data = self.recv(8192)
        if data:
            self.send(data)

【讨论】:

谢谢,但正如我在问题中所说,嵌入环境的限制使我无法启动新线程。 这是一个“你能做什么”的答案,修改它,试一试,摆弄一下。 抱歉,我说的不是你对 Thread 类的使用,而是 asyncore.loop() 位。我不能阻止线程等待 asynccore 在任何重要的时间段内完成。尽管我想即使响应未完成,我也可以伪造异步任务以提前返回,并在计时器回调中重复调用 asyncore.loop() ... 是的,可以工作,可惜你不能使用线程,因为就像我在第一个示例中所做的那样,我只是将 asyncore.loop() 放入一个线程中,因此它是非阻塞的 :) 有没有一种简单的方法可以在此之上放置 HTTP 响应解析器?如果可以的话,我不想自己解析标题并提取正文(当然这只是跳过一些行的问题)。

以上是关于Python 等效于 Perl 的 HTTP::Async->next_response的主要内容,如果未能解决你的问题,请参考以下文章

使用管道在 Perl 中将管道文件输出到 gzip 的 Python 等效项

PHP 等效于 Perl 的“使用严格”(要求在使用前初始化变量)

Qt 等效于 Perl 打包/解包

高效 pre-perl-5.10 等效于 pack("Q>")

是否有与 Perl 的 WWW::Mechanize 等效的 PHP?

Perl 的等价于 PHP 的 print_r() 是啥?