Re: Using a background thread with asyncio/futures with flask
On 2024-03-23 3:25 PM, Frank Millman via Python-list wrote: It is not pretty! call_soon_threadsafe() is a loop function, but the loop is not accessible from a different thread. Therefore I include a reference to the loop in the message passed to in_queue, which in turn passes it to out_queue. I found that you can retrieve the loop from the future using future.get_loop(), so the above is not necessary. Frank -- https://mail.python.org/mailman/listinfo/python-list
Re: Using a background thread with asyncio/futures with flask
On 2024-03-22 12:08 PM, Thomas Nyberg via Python-list wrote: Hi, Yeah so flask does support async (when installed with `pip3 install flask[async]), but you are making a good point that flask in this case is a distraction. Here's an example using just the standard library that exhibits the same issue: `app.py` ``` import asyncio import threading import time from queue import Queue in_queue = Queue() out_queue = Queue() def worker(): print("worker started running") while True: future = in_queue.get() print(f"worker got future: {future}") time.sleep(5) print("worker sleeped") out_queue.put(future) def finalizer(): print("finalizer started running") while True: future = out_queue.get() print(f"finalizer got future: {future}") future.set_result("completed") print("finalizer set result") threading.Thread(target=worker).start() threading.Thread(target=finalizer).start() async def main(): future = asyncio.get_event_loop().create_future() in_queue.put(future) print(f"main put future: {future}") result = await future print(result) if __name__ == "__main__": loop = asyncio.get_event_loop() loop.run_until_complete(main()) ``` If I run that I see the following printed out (after which is just hangs): ``` Combining Dieter's and Mark's ideas, here is a version that works. It is not pretty! call_soon_threadsafe() is a loop function, but the loop is not accessible from a different thread. Therefore I include a reference to the loop in the message passed to in_queue, which in turn passes it to out_queue. Frank === import asyncio import threading import time from queue import Queue in_queue = Queue() out_queue = Queue() def worker(): print("worker started running") while True: loop, future = in_queue.get() print(f"worker got future: {future}") time.sleep(5) print("worker sleeped") out_queue.put((loop, future)) def finalizer(): print("finalizer started running") while True: loop, future = out_queue.get() print(f"finalizer got future: {future}") loop.call_soon_threadsafe(future.set_result, "completed") print("finalizer set result") threading.Thread(target=worker, daemon=True).start() threading.Thread(target=finalizer, daemon=True).start() async def main(): loop = asyncio.get_event_loop() future = loop.create_future() in_queue.put((loop, future)) print(f"main put future: {future}") result = await future print(result) if __name__ == "__main__": # loop = asyncio.get_event_loop() # loop.run_until_complete(main()) asyncio.run(main()) -- https://mail.python.org/mailman/listinfo/python-list
Re: Using a background thread with asyncio/futures with flask
On 2024-03-22 1:23 PM, Frank Millman via Python-list wrote: On 2024-03-22 12:09 PM, Frank Millman via Python-list wrote: I am no expert. However, I do have something similar in my app, and it works. I do not use 'await future', I use 'asyncio.wait_for(future)'. I tested it and it did not work. I am not sure, but I think the problem is that you have a mixture of blocking and non-blocking functions. Here is a version that works. However, it is a bit different, so I don't know if it fits your use case. I have replaced the threads with background asyncio tasks. I have replaced instances of queue.Queue with asyncio.Queue. Frank === import asyncio in_queue = asyncio.Queue() out_queue = asyncio.Queue() async def worker(): print("worker started running") while True: future = await in_queue.get() print(f"worker got future: {future}") await asyncio.sleep(5) print("worker sleeped") await out_queue.put(future) async def finalizer(): print("finalizer started running") while True: future = await out_queue.get() print(f"finalizer got future: {future}") future.set_result("completed") print("finalizer set result") async def main(): asyncio.create_task(worker()) # start a background task asyncio.create_task(finalizer()) # ditto future = asyncio.get_event_loop().create_future() await in_queue.put(future) print(f"main put future: {future}") result = await asyncio.wait_for(future, timeout=None) print(result) if __name__ == "__main__": # loop = asyncio.get_event_loop() # loop.run_until_complete(main()) # this is the preferred way to start an asyncio app asyncio.run(main()) One more point. If I change 'await asyncio.wait_for(future, timeout=None)' back to your original 'await future', it still works. -- https://mail.python.org/mailman/listinfo/python-list
Re: Using a background thread with asyncio/futures with flask
On 2024-03-22 12:09 PM, Frank Millman via Python-list wrote: I am no expert. However, I do have something similar in my app, and it works. I do not use 'await future', I use 'asyncio.wait_for(future)'. I tested it and it did not work. I am not sure, but I think the problem is that you have a mixture of blocking and non-blocking functions. Here is a version that works. However, it is a bit different, so I don't know if it fits your use case. I have replaced the threads with background asyncio tasks. I have replaced instances of queue.Queue with asyncio.Queue. Frank === import asyncio in_queue = asyncio.Queue() out_queue = asyncio.Queue() async def worker(): print("worker started running") while True: future = await in_queue.get() print(f"worker got future: {future}") await asyncio.sleep(5) print("worker sleeped") await out_queue.put(future) async def finalizer(): print("finalizer started running") while True: future = await out_queue.get() print(f"finalizer got future: {future}") future.set_result("completed") print("finalizer set result") async def main(): asyncio.create_task(worker()) # start a background task asyncio.create_task(finalizer()) # ditto future = asyncio.get_event_loop().create_future() await in_queue.put(future) print(f"main put future: {future}") result = await asyncio.wait_for(future, timeout=None) print(result) if __name__ == "__main__": # loop = asyncio.get_event_loop() # loop.run_until_complete(main()) # this is the preferred way to start an asyncio app asyncio.run(main()) -- https://mail.python.org/mailman/listinfo/python-list
Re: Using a background thread with asyncio/futures with flask
On 2024-03-20 10:22 AM, Thomas Nyberg via Python-list wrote: Hello, I have a simple (and not working) example of what I'm trying to do. This is a simplified version of what I'm trying to achieve (obviously the background workers and finalizer functions will do more later): `app.py` ``` import asyncio import threading import time from queue import Queue from flask import Flask in_queue = Queue() out_queue = Queue() def worker(): print("worker started running") while True: future = in_queue.get() print(f"worker got future: {future}") time.sleep(5) print("worker sleeped") out_queue.put(future) def finalizer(): print("finalizer started running") while True: future = out_queue.get() print(f"finalizer got future: {future}") future.set_result("completed") print("finalizer set result") threading.Thread(target=worker, daemon=True).start() threading.Thread(target=finalizer, daemon=True).start() app = Flask(__name__) @app.route("/") async def root(): future = asyncio.get_event_loop().create_future() in_queue.put(future) print(f"root put future: {future}") result = await future return result if __name__ == "__main__": app.run() ``` If I start up that server, and execute `curl http://localhost:5000`, it prints out the following in the server before hanging: ``` $ python3 app.py worker started running finalizer started running * Serving Flask app 'app' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on http://127.0.0.1:5000 Press CTRL+C to quit root put future: worker got future: worker sleeped finalizer got future: finalizer set result ``` Judging by what's printing out, the `final result = await future` doesn't seem to be happy here. Maybe someone sees something obvious I'm doing wrong here? I presume I'm mixing threads and asyncio in a way I shouldn't be. Here's some system information (just freshly installed with pip3 install flask[async] in a virtual environment for python version 3.11.2): ``` $ uname -a Linux x1carbon 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux $ python3 -V Python 3.11.2 $ pip3 freeze asgiref==3.7.2 blinker==1.7.0 click==8.1.7 Flask==3.0.2 itsdangerous==2.1.2 Jinja2==3.1.3 MarkupSafe==2.1.5 Werkzeug==3.0.1 ``` Thanks for any help! Cheers, Thomas Hi Thomas I am no expert. However, I do have something similar in my app, and it works. I do not use 'await future', I use 'asyncio.wait_for(future)'. HTH Frank Millman -- https://mail.python.org/mailman/listinfo/python-list
Re: Question about garbage collection
On 2024-01-17 3:01 AM, Greg Ewing via Python-list wrote: On 17/01/24 1:01 am, Frank Millman wrote: I sometimes need to keep a reference from a transient object to a more permanent structure in my app. To save myself the extra step of removing all these references when the transient object is deleted, I make them weak references. I don't see how weak references help here at all. If the transient object goes away, all references from it to the permanent objects also go away. A weak reference would only be of use if the reference went the other way, i.e. from the permanent object to the transient object. You are right. I got my description above back-to-front. It is a pub/sub scenario. A transient object makes a request to the permanent object to be notified of any changes. The permanent object stores a reference to the transient object and executes a callback on each change. When the transient object goes away, the reference must be removed. Frank -- https://mail.python.org/mailman/listinfo/python-list
Re: Question about garbage collection
On 2024-01-16 2:15 PM, Chris Angelico via Python-list wrote: Where do you tend to "leave a reference dangling somewhere"? How is this occurring? Is it a result of an incomplete transaction (like an HTTP request that never finishes), or a regular part of the operation of the server? I have a class that represents a database table, and another class that represents a database column. There is a one-to-many relationship and they maintain references to each other. In another part of the app, there is a class that represents a form, and another class that represents the gui elements on the form. Again there is a one-to-many relationship. A gui element that represents a piece of data has to maintain a link to its database column object. There can be a many-to-one relationship, as there could be more than one gui element referring to the same column. There are added complications which I won't go into here. The bottom line is that on some occasions a form which has been closed does not get gc'd. I have been trying to reproduce the problem in my toy app, but I cannot get it to fail. There is a clue there! I think I have just over-complicated things. I will start with a fresh approach tomorrow. If you don't hear from me again, you will know that I have solved it! Thanks for the input, it definitely helped. Frank -- https://mail.python.org/mailman/listinfo/python-list
Re: Question about garbage collection
On 2024-01-15 3:51 PM, Frank Millman via Python-list wrote: Hi all I have read that one should not have to worry about garbage collection in modern versions of Python - it 'just works'. I don't want to rely on that. My app is a long-running server, with multiple clients logging on, doing stuff, and logging off. They can create many objects, some of them long-lasting. I want to be sure that all objects created are gc'd when the session ends. I did not explain myself very well. Sorry about that. My problem is that my app is quite complex, and it is easy to leave a reference dangling somewhere which prevents an object from being gc'd. This can create (at least) two problems. The obvious one is a memory leak. The second is that I sometimes need to keep a reference from a transient object to a more permanent structure in my app. To save myself the extra step of removing all these references when the transient object is deleted, I make them weak references. This works, unless the transient object is kept alive by mistake and the weak ref is never removed. I feel it is important to find these dangling references and fix them, rather than wait for problems to appear in production. The only method I can come up with is to use the 'delwatcher' class that I used in my toy program in my original post. I am surprised that this issue does not crop up more often. Does nobody else have these problems? Frank -- https://mail.python.org/mailman/listinfo/python-list
Question about garbage collection
Hi all I have read that one should not have to worry about garbage collection in modern versions of Python - it 'just works'. I don't want to rely on that. My app is a long-running server, with multiple clients logging on, doing stuff, and logging off. They can create many objects, some of them long-lasting. I want to be sure that all objects created are gc'd when the session ends. I do have several circular references. My experience is that if I do not take some action to break the references when closing the session, the objects remain alive. Below is a very simple program to illustrate this. Am I missing something? All comments appreciated. Frank Millman == import gc class delwatcher: # This stores enough information to identify the object being watched. # It does not store a reference to the object itself. def __init__(self, obj): self.id = (obj.type, obj.name, id(obj)) print('***', *self.id, 'created ***') def __del__(self): print('***', *self.id, 'deleted ***') class Parent: def __init__(self, name): self.type = 'parent' self.name = name self.children = [] self._del = delwatcher(self) class Child: def __init__(self, parent, name): self.type = 'child' self.parent = parent self.name = name parent.children.append(self) self._del = delwatcher(self) p1 = Parent('P1') p2 = Parent('P2') c1_1 = Child(p1, 'C1_1') c1_2 = Child(p1, 'C1_2') c2_1 = Child(p2, 'C2_1') c2_2 = Child(p2, 'C2_2') input('waiting ...') # if next 2 lines are included, parent and child can be gc'd # for ch in p1.children: # ch.parent = None # if next line is included, child can be gc'd, but not parent # p1.children = None del c1_1 del p1 gc.collect() input('wait some more ...') -- https://mail.python.org/mailman/listinfo/python-list
Type hints - am I doing it right?
Hi all I am adding type hints to my code base. I support three databases - sqlite3, Sql Server, PostgreSQL. The db parameters are kept in an ini file, under the section name 'DbParams'. This is read on program start, using configparser, and passed to a function config_database() in another module with the argument cfg['DbParams']. In the other module I have this - def config_database(db_params): To add a type hint, I now have this - def config_database(db_params: configparser.SectionProxy): To get this to work, I have to add 'import configparser' at the top of the module. I have three separate modules, one for each database, with a subclass containing the methods and attributes specific to that database. Each one has a connect() method which receives db_params as a parameter. Now I have to add 'import configparser' at the top of each of these modules in order to type hint the method. This seems verbose. If it is the correct way of doing it I can live with it, but I wondered if there was an easier way. BTW I have realised that I can bypass the problem by converting db_params to a dict, using dict(cfg['DbParams']). But I would still like an answer to the original question, as I am sure similar situations will occur without such a simple solution. Thanks Frank Millman -- https://mail.python.org/mailman/listinfo/python-list
Re: Simple webserver
On 2023-10-22 7:35 PM, Dieter Maurer via Python-list wrote: The web server in Python's runtime library is fairly simple, focusing only on the HTTP requirements. You might want additional things for an HTTP server exposed on the internet which should potentially handle high trafic: e.g. * detection of and (partial) protection against denial of service attacks, * load balancing, * virtual hosting * proxing * URL rewriting * high throughput, low latency Depending on your requirements, other web servers might be preferable. Dieter's response was very timely for me, as it provides some answers to a question that I was thinking of posting. My use-case is reasonably on-topic for this thread, so I won't start a new one, if that is ok. I am writing a business/accounting application. The server uses Python and asyncio, the client is written in Javascript. The project is inching towards a point where I may consider releasing it. My concern was whether my home-grown HTTP server was too simplistic for production, and if so, whether I should be looking into using one of the more established frameworks. After some brief investigation into Dieter's list of additional requirements, here are my initial thoughts. Any comments will be welcome. I skimmed through the documentation for flask, Django, and FastAPI. As far as I can tell, none of them address the points listed above directly. Instead, they position themselves as one layer in a stack of technologies, and rely on other layers to provide additional functionality. If I read this correctly, there is nothing to stop me doing the same. Based on this, I am considering the following - 1. Replace my HTTP handler with Uvicorn. Functionality should be the same, but performance should be improved. 2. Instead of running as a stand-alone server, run my app as a reverse-proxy using Nginx. I tested this a few years ago using Apache, and it 'just worked', so I am fairly sure that it will work with Nginx as well. Nginx can then provide the additional functionality that Dieter has mentioned. My main concern is that, if I do release my app, I want it to be taken seriously and not dismissed as 'Mickey Mouse'. Do you think the above changes would assist with that? When I talk about releasing it, it is already available on Github here - https://github.com/FrankMillman/AccInABox. You are welcome to look at it, but it needs a lot of tidying up before it will be ready for a wider audience. Frank Millman -- https://mail.python.org/mailman/listinfo/python-list