New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
asyncore delayed calls feature #45982
Comments
Hi, The following code sample implements an idle-timeout capability using --- code snippet --- import asyncore, asynchat, socket
class foo(asynchat.async_chat):
def __init__(self, conn=None):
asynchat.async_chat.__init__(self, conn)
self.set_terminator(None)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect(('127.0.0.1', 21))
self.scheduled_timeout = self.call_later(120, self.handle_timeout)
def collect_incoming_data(self, data):
self.scheduled_timeout.reset()
# do something with the data...
def handle_timeout(self):
self.push("500 Connection timed out.\r\n")
self.close_when_done()
def close(self):
if not self.scheduled_timeout.cancelled:
self.scheduled_timeout.cancel()
asyncore.dispatcher.close(self)
foo()
asyncore.loop()
--- /code snippet Today I played a little more with it and I tried to add bandwidth --- code snippet --- class throttled_async_chat(asynchat.async_chat):
# maximum number of bytes to transmit in a second (0 == no limit)
read_limit = 100 * 1024
write_limit = 100 * 1024
# smaller the buffers, the less bursty and smoother the throughput
ac_in_buffer_size = 2048
ac_out_buffer_size = 2048
def __init__(self, conn=None):
asynchat.async_chat.__init__(self, conn)
self.read_this_second = 0
self.written_this_second = 0
self.r_timenext = 0
self.w_timenext = 0
self.r_sleep = False
self.w_sleep = False
self.delayed_r = None
self.delayed_w = None
def readable(self):
return asynchat.async_chat.readable(self) and not self.r_sleep
def writable(self):
return asynchat.async_chat.writable(self) and not self.w_sleep
def recv(self, buffer_size):
chunk = asyncore.dispatcher.recv(self, buffer_size)
if self.read_limit:
self.read_this_second += len(chunk)
self.throttle_read()
return chunk
def send(self, data):
num_sent = asyncore.dispatcher.send(self, data)
if self.write_limit:
self.written_this_second += num_sent
self.throttle_write()
return num_sent
def throttle_read(self):
if self.read_this_second >= self.read_limit:
self.read_this_second = 0
now = time.time()
sleepfor = self.r_timenext - now
if sleepfor > 0:
# we've passed bandwidth limits
self.r_sleep = True
def unthrottle():
self.r_sleep = False
self.delayed_r = self.call_later((sleepfor * 2), unthrottle)
self.r_timenext = now + 1
def throttle_write(self):
if self.written_this_second >= self.write_limit:
self.written_this_second = 0
now = time.time()
sleepfor = self.w_timenext - now
if sleepfor > 0:
# we've passed bandwidth limits
self.w_sleep = True
def unthrottle():
self.w_sleep = False
self.delayed_w = self.call_later((sleepfor * 2), unthrottle)
self.w_timenext = now + 1
def close(self):
if self.delayed_r and not self.delayed_r.cancelled:
self.delayed_r.cancel()
if self.delayed_w and not self.delayed_w.cancelled:
self.delayed_w.cancel()
asyncore.dispatcher.close(self)
--- /code snippet I don't know if there's a better way to implement this "call_later" feature. |
If you want attention, please post to python-dev if you didn't already. |
The issue bpo-2006 (asyncore loop lacks timers and work tasks) was closed |
Giampaolo: Can you pleaes bring this up on python-dev or the normal |
Sean, I already tried to raise two discussion attempts on both lists here: Moreover, before doing anything against asyncore and asynhat there are a |
Unfortunately, it appears that asyncore and asynchat are caught in a We need this situation to be resolved, preferably by somebody with |
Generally speaking, delayed calls, and/or a practical scheduling |
I try to revamp this issue by attaching a new patch which improves the
Josiah, do you have some time to review this? |
I have an updated sched.py module which significantly improves the From there, it's all a matter of desired API and features. My opinion on the matter: it would be very nice to have the asyncore In asyncore, I believe that it would be sufficient to offer the ability |
I'm looking forward to having this functionality in asyncore. It would Giampaolo, I'm concerned that your patch uses a global 'tasks' list Josiah, is your updated sched module the one described in this blog |
Personally I can't think of any use case in which that would come |
The idea is to be able (whether you see a use case or not) to use if tasks is None:
tasks = default_tasks similar to what it does for map. You'd also have to pass the tasks list |
Forest: |
Unless I change the current API I can't add a new optional arguments to def __init__(self, seconds, target, *args, **kwargs): |
You could solve this with a "reserved" keyword argument _tasks. Or you could have two different factory methods, call_later_with_tasks() |
I've just attached a patch to sched.py and asyncore.py to offer a richer There is no documentation or tests, but I can add those based on |
Here's a better patch without tabs. |
A new patch is in attachment.
Tests and documentation are also included. |
I fixed some bugs with my patch, merged in Giampaolo's tests and This new version differs from Giampaolo's patch only in underlying The major difference is that the modifications to sched.py offer a fast |
At the language summit last Thursday there was widespread disappointment |
IIRC, there was a threat to remove asyncore because there were no The delayed calls feature discussed in the current bug doesn't alter the If you are concerned about the sched module, I'd be happy to do a search |
I am the developer of Supervisor (http://supervisord.org) which depends I need to make sure Supervisor works with Python 2.3, 2.4, and 2.5, as I don't know of any other consumers of Medusa other than Zope and I might argue that in retrospect the the current implementation of |
Looking back, I think Zope and Medusa should have adopted and evolved |
This statement is puzzling. No big deal, but I'm curious why you say |
For the record, afaict, Zope wasn't broken by this. Supervisor isn't |
Sidnei da Silva had to put some "straddling" code in the Zope2 trunk to |
[Guido]
[Jim]
ISTR that Zope has or had significant monkeypatches to at least one of |
On Apr 2, 2009, at 1:27 PM, Guido van Rossum wrote:
Not that I'm aware of. I did add the ability to pass in alternative
If we were monkey patching it, it would be at our own risk, which is
I've read a good argument that subclassing across implementation Jim |
-----BEGIN PGP SIGNED MESSAGE----- Guido van Rossum wrote:
Zope does not monkeypatch asyncore or asynchat, and hasn't since at Tres. =================================================================== iD8DBQFJ1QLqFXKVXuSL+CMRAhelAJ9yYgo1RXUhWR2cH8CjYRoXz/qsvACgg13O |
I'm not defending the documentation, I'm merely reposting it. The documentation for asyncore says, "The full set of methods that can The documentation for asynchat says, "To make practical use of the code How can we make the documentation better? I'm too close to the |
On Apr 2, 2009, at 3:35 PM, Josiah Carlson wrote:
Actually, the documentation is better than I remember it to be. The Jim |
Assuming this is still desirable I'd really like to move forward with this issue. My patch pros:
cons:
Josiah's patch pros:
cons:
Both patches should no longer apply cleanly so they should be adjusted a little and the missing parts (full tests, documentation including example usage, etc...) completed. |
I agree with the points raised against Josiah's patch. I'm not sure O(n) cancellation is really a concern. The main focus of optimization should be the scheduler's loop itself, and both approaches have an O(log n) complexity there AFAICT. Also, the cancellation optimization could be backported into Giampaolo's patch. One area tests should check for is when scheduling operations are done from a delayed call. Especially, a delayed call rescheduling itself. By the way, it's too late for 2.7, so this is only for 3.2 now. |
I like the idea of leveraging the sched module. It encapsulates the priority queue, allowing the user to be agnostic to the underlying data structure. If someday we have a data structure in the collections module that provides an efficient delete-key operation, we can switch. Giampaolo's patch forever ties us to heapq. That said, I believe Josiah's patch could be simplified considerably. Here are two ideas, which can be evaluated separately:
Also, fix one small bug:
|
Some prodding from Giampaolo got me to pull out and simplify the sched.py changes here: bpo-8684 . That should be sufficient to add scheduling behavior into async socket servers or otherwise. |
I think a wrapper around sched.py is necessary. Adding a call: scheduler = sched.scheduler(time.time, time.sleep)
scheduler.enter(10, 1, function, (arg,)) ...vs: asyncore.call_later(10, function, arg) Cancelling a call: scheduler = sched.scheduler(time.time, time.sleep)
event = scheduler.enter(10, 1, function, (arg,))
scheduler.cancel(event) ...vs: event = asyncore.call_later(10, function, arg)
event.cancel() Moreover, reset() and delay() methods are not implemented in sched. event = asyncore.call_later(10, function, arg)
event.reset()
event.delay(10) By using sched.py you'll have to recreate a new event from scratch (scheduler.cancel(event) -> calculate the new timeout, scheduler.enter(newtime, 1, function, (arg,)). Other problems which comes to mind are: you can't easily know whether a call has already been cancelled, you can't manually fire it before the timeout has expired and I'm not even sure whether it's possible to pass kwargs to enter(), which is crucial (with call_later you can do it like this: asyncore.call_later(10, function, x, y, z='foo')). |
I don't really see the difference. How hard it is to build a scheduler The main improvement I could see would be to make the arguments to |
On Tue, May 11, 2010 at 11:55 AM, Giampaolo Rodola'
These are nice features, but wouldn't it make more sense to add them to sched? That would provide them to other users of sched, while keeping the |
This patch is now available as a recipe for python 2.x: |
With bpo-13449 fixed I think we can now provide this functionnality by adding a specific section into asyncore doc which explains how to use asyncore in conjunction with sched module. |
How would it work? |
Now that I think of it maybe some kind of wrapper would still be necessary. import asyncore, asynchat, sched
# global
scheduler = sched.scheduler()
while 1:
asyncore.loop(timeout=1.0, count=1) # count=1 makes loop() return after 1 loop
scheduler.run(blocking=False)
Then, every dispatcher can define a scheduled function of its own: class Client(asynchat.async_chat):
# an already connected client
# (the "connector" code is not included in this example)
def __init__(self, *args, **kwargs):
asynchat.async_chat.__init__(self, *args, **kwargs)
self.set_terminator("\r\n")
self.set_timeout()
def set_timeout(self):
self.timeout = scheduler.enter(30, 0, self.handle_timeout)
def reset_timeout(self):
scheduler.cancel(self.timeout)
self.set_timeout()
def found_terminator(self):
scheduler.cancel(self.timeout)
self.timeout = scheduler.enter(30, 0, self.handle_timeout)
# do something with the received data...
def handle_timeout(self):
self.push("400 connection timed out\r\n")
self.close()
def close(self):
scheduler.cancel(self.timeout)
asynchat.async_chat.close(self) |
Isn't that both ugly and imprecise? |
New changeset 59f0e6de54b3 by Giampaolo Rodola' in branch 'default': |
Where does this issue stand now? Did the applied sched patch supersede the proposed asyncore patch? Is enhancing asyncore still on the table given Guido's proposed new module? |
A new implementation is part of Tulip (tulip/selectors.py); once Tulip On Fri, Mar 8, 2013 at 12:03 PM, Terry J. Reedy <report@bugs.python.org> wrote:
|
I'm not sure how many users asyncore has out there nowadays, but if it has to stay in the stdlib then I see some value in adding a scheduler to it because it is an essential component. If this is still desirable I can restart working on a patch, although I'll have to go through some of the messages posted earlier in this topic and figure how's best to proceed: whether reusing sched.py or write a separate scheduler in asyncore.py. |
Now asyncio/tulip has landed in the 3.4 stdlib, asyncore will be effectively obsolete starting 3.4 (even if we don't mark it so). Its presence is required for backwards compatibility, but that doesn't mean we should encourage people to keep using it by adding features. If you agree, please close this issue. |
asyncore documentation now starts with this note (which was approved by the asyncore maintainer): Since asyncio is now part of the stdlib, I don't think that it's worth to enhance asyncore. asyncore has design flaws like its poll() function which doesn't scale well with the number of file descriptors. The latest patch for this issue was written 5 years ago, I don't think that many people are waiting for this feature in asyncore. Delayed calls are part of asyncio core, it's well designed and *efficient*. So I'm now closing this issue. "Upgrade" your code to asyncio! |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: