0

I am following some example code to use asyncore here, only having set a timeout value for asyncore.loop as in the following full example:

import smtpd
import asyncore

class CustomSMTPServer(smtpd.SMTPServer):

    def process_message(self, peer, mailfrom, rcpttos, data):
        print 'Receiving message from:', peer
        print 'Message addressed from:', mailfrom
        print 'Message addressed to  :', rcpttos
        print 'Message length        :', len(data)
        return

server = CustomSMTPServer(('127.0.0.1', 1025), None)

asyncore.loop(timeout = 1)

I have expected that a timeout occurs after 1 second, but this is not the case. The code runs much longer for than one second. What am I missing here?

Alex
  • 41,580
  • 88
  • 260
  • 469
  • @pst: Count is not an option. I do not know if there will be zero or more than zero connections beforehand. – Alex Jan 23 '13 at 17:22
  • Then stop beating around the bush –  Jan 23 '13 at 17:23
  • @pst I do not know what you mean. My question is: Why does the timeout for `asyncore.loop()` does not end the function `asyncore.loop()` after the specified time. – Alex Jan 23 '13 at 17:24
  • Well, consider some cases: 1) the documentation is lying that it will stop running when there are no more open channels, 2) the documentation is lying about what the `timeout` does, 3) asyncore is not using the specified timeout with select 4) there *are* open channels .. I suspect it will end shortly if a count (say, 2) is specified. If that is indeed the case then #2 and #3 can likely be ruled out. –  Jan 23 '13 at 17:26
  • @pst: I do not understand what you mean. Maybe you could start answering the following question in a clear, concise and well-phrased answer: What is the purpose of the `timeout` argument in `asyncore.loop()`? – Alex Jan 23 '13 at 17:28
  • The documentation said what it does. Since *you* are making an assertion, I have provided some hypothesis to follow to verify or disprove said claim. –  Jan 23 '13 at 17:28
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/23246/discussion-between-alex-and-pst) – Alex Jan 23 '13 at 17:35

3 Answers3

2

The timeout argument to asyncore.loop() is the amount of time the select.select call will wait for data. If there is no data before the timeout runs out it will loop and call select.select again.

Same for the channels idea. This does not mean open sockets but means active asyncore.dispatcher or asynchat.async_chat instances. If you want to stop the loop you will have to call the close() method on ALL instances registered.

In your case server.close() will close the instance/channel and remove it from the asyncore loop. If no more channels are active this loop will then terminate itself.

Wessie
  • 3,460
  • 2
  • 13
  • 17
  • How can I call a `close` method if `asyncore.loop()` blocks my code? – Alex Jan 23 '13 at 17:31
  • @Alex You should either run the `asyncore.loop()` in a new thread or call the `close` inside one of the `handlers`. I don't know how you determine when to exit so if you tell us how you determine that we could help more. – Wessie Jan 23 '13 at 17:32
  • I have been trying to run `asyncore.loop()` in a thread, see http://stackoverflow.com/questions/14483195/how-to-handle-asyncore-within-a-class-in-python-without-blocking-anything, but I need to figure out where and how to call `close` or how and where to modify any `hander` method. If you know how, I really would appreciate if you could elaborate this in the other question. – Alex Jan 23 '13 at 17:34
  • @Alex I posted an answer on the linked question. Please comment there if you need more help. – Wessie Jan 23 '13 at 17:44
1

I really do not know if the timeout argument to asyncore.loop() really is meant to timeout the function call asyncore.loop() after the specified time, but here is a receipt to make that function timeout after a specified time (replacing the line with asyncore.loop() in the example code):

import signal

class TimeoutError(Exception): pass

# define the timeout handler
def handler(signum, frame):
    raise TimeoutError()

# set the timeout handler and the signal duration
signal.signal(signal.SIGALRM, handler)
signal.alarm(1)
try:
    asyncore.loop()
except TimeoutError as exc:
    print "timeout"
finally:
    signal.alarm(0)
Alex
  • 41,580
  • 88
  • 260
  • 469
0

The timeout of asyncore.loop() is the timeout for select().

It is not useful, because when select() timeout, it loops back, see the pseudo code:

while True:
    do_something()
    select(...)
    do_something_else()

If I do simulation with firewall-ed sockets, in my Python 2.7.3 asyncore.loop() timeout 1 minute after no data is received from some socket.

I found very useful to have following method in asyncore.dispatcher "subclass":

def handle_error(self):
    raise

In this way I had "proper" exception dump.

Because I do not wanted to have exception, later I changed it to something like:

def handle_error(self):
    print "Error downloading %s" % self.host
    pass

Now my code works correct, without exception.

I did not found a way to control the timeout.

Nick
  • 9,962
  • 4
  • 42
  • 80