Re: Using a background thread with asyncio/futures with flask

2024-03-24 Thread Frank Millman via Python-list

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

2024-03-23 Thread Frank Millman via Python-list

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

2024-03-22 Thread Frank Millman via Python-list

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

2024-03-22 Thread Frank Millman via Python-list

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

2024-03-22 Thread Frank Millman via Python-list

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

2024-01-16 Thread Frank Millman via Python-list

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

2024-01-16 Thread Frank Millman via Python-list

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

2024-01-16 Thread Frank Millman via Python-list

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

2024-01-15 Thread Frank Millman via Python-list

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?

2023-12-12 Thread Frank Millman via Python-list

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

2023-10-25 Thread Frank Millman via Python-list

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