Werkzeug: Apakah werkzeug memiliki rencana untuk mendukung ASGI?

Dibuat pada 7 Jun 2018  ·  21Komentar  ·  Sumber: pallets/werkzeug

Werkzeug menawarkan banyak metode yang berguna, akan jauh lebih mudah jika mendukung ASGI daripada jika kita memulai dari awal.

ASGI

Komentar yang paling membantu

Ya, Werkzeug dan Flask pada akhirnya akan mendukung ASGI. Saya tidak memiliki garis waktu untuk ini, meskipun saya akan dengan senang hati membantu meninjau PR jika seseorang memulainya.

Namun, saya tidak akan menjadi orang yang menerapkannya, saya butuh bantuan dari masyarakat. Lihat pembaruan terbaru di bawah ini: https://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145

Semua 21 komentar

Ya, Werkzeug dan Flask pada akhirnya akan mendukung ASGI. Saya tidak memiliki garis waktu untuk ini, meskipun saya akan dengan senang hati membantu meninjau PR jika seseorang memulainya.

Namun, saya tidak akan menjadi orang yang menerapkannya, saya butuh bantuan dari masyarakat. Lihat pembaruan terbaru di bawah ini: https://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145

Saya tertarik untuk mengerjakan ini.

Saya memiliki beberapa dukungan ASGI yang berfungsi tetapi retas yang sedang berlangsung: werkzeug , flask . Saya ingin memahami lebih lanjut tentang rencana apa pun yang mungkin Anda miliki sebelum melangkah lebih jauh.

Hal-hal yang sudah terjadi pada saya, di luar kenyataan bahwa semua yang telah saya lakukan di sana secara umum bisa lebih baik:

  • Saya mendukung pengurai formulir dengan hanya menjalankan kode sinkronnya di utas (yang saya blokir saat saya membaca data secara tidak sinkron). Saya tidak berpikir ini akan berhasil. Pendekatan pilihan saya adalah dengan memfaktorkan IO .
  • Dukungan konteks-lokal cukup rapuh. Tampaknya ada cara yang tepat untuk melakukan ini , tetapi ini melibatkan campur tangan dengan negara global, dan terutama dengan ASGI, kita tidak dapat berasumsi bahwa kita memiliki dunia.
  • Tidak ada cara yang jelas, saat berjalan di bawah ASGI, untuk mengurai data formulir sesuai permintaan untuk fungsi sinkron. Mungkin ada baiknya mempertimbangkan untuk menguraikan data formulir dengan penuh semangat kecuali kami dapat memberi tahu bahwa fungsi tampilan tidak sinkron. Apa pun defaultnya, pasti ada dekorator yang menggantikannya.

Ceritakan apa yang sedang kau pikirkan.

Saya mendukung pengurai formulir dengan hanya menjalankan kode sinkronnya di utas (yang saya blokir saat saya membaca data secara tidak sinkron). Saya tidak berpikir ini akan berhasil. Pendekatan pilihan saya adalah dengan memfaktorkan IO.

Saya kira pendekatan sentuhan ringan hanya untuk mengimplementasikan kembali parser yang ada, tetapi dengan async IO. Akan lebih bersih jika dua kelas parser dapat berbagi implementasi sans-IO umum di bawah tenda, tetapi mungkin lebih praktis hanya dengan menduplikasi & sedikit memodifikasi implementasi yang ada.

Dukungan konteks-lokal cukup rapuh.

Konteks lokal (untuk asyncio) ada di 3.7 stdlib. https://docs.python.org/3.7/library/contextvars.html
Saya kira gunakan itu, dengan perpustakaan compat opsional untuk mendukung versi python sebelumnya. (Atau anggap saja ASGI pada Flask akan berakhir menjadi 3,7+ hal)

Apakah werkzeug menggunakan thread-local? (Saya tahu bahwa Flask melakukannya)

Tidak ada cara yang jelas, saat berjalan di bawah ASGI, untuk mengurai data formulir sesuai permintaan untuk fungsi sinkron

Saya sarankan untuk tidak menawarkan antarmuka sinkron untuk mem-parsing data formulir. (Atau untuk mengakses badan permintaan dengan cara apa pun), dan sebagai gantinya hanya menawarkan API async ke dalamnya. Lihat API Starlette untuk beberapa contoh di sini... https://github.com/encode/starlette#body

Sejak Python 3.7 keluar, saya tidak akan menentang memiliki fitur async yang membutuhkan Python 3.7 selama tidak ada kode lain yang terpengaruh olehnya. Tetapi jika ada backport yang sebagus solusi 3.7 asli - bahkan lebih baik!

Mengenai penguraian data formulir async... Saya kira sesuatu seperti await request.parse() dalam fungsi asinkron sudah cukup, dan kemudian memunculkan pengecualian ketika mencoba mengakses data formulir yang tidak diuraikan?

Tentu, atau buat saja values dan teman-teman asinkron sendiri: values = await request.values atau values = await request.values() , sebagian besar tergantung pada yang terlihat lebih bagus.

bukankah itu memerlukan hal-hal yang agak jelek seperti (await request.form)['foo'] untuk melakukan panggilan async sambil mendapatkan elemen dict secara langsung tanpa menetapkan di antaranya?

Ya :(

Saya tidak yakin itu sangat bisa dihindari. Saya tidak berpikir

form = await request.form
form['foo']

benar-benar lebih atau kurang jelek dari

await request.parse()
request.form['foo']

meskipun itu jelas tergantung selera.

Saya kira kita juga bisa menciptakan "async dict" yang _members_-nya semuanya async-ified, tetapi tanpa melihatnya, saya membayangkan itu akan menjadi agak membingungkan.

Tentu, atau hanya membuat nilai dan teman menjadi asinkron sendiri: values ​​= menunggu request.values ​​atau values ​​= menunggu request.values(), sebagian besar bergantung pada yang terlihat lebih bagus.

Saya sarankan menggunakan panggilan fungsi untuk operasi yang menjalankan I/O, daripada properti.

bukankah itu memerlukan hal-hal yang agak jelek seperti (menunggu request.form)['foo'] untuk melakukan panggilan async sambil mendapatkan elemen dict secara langsung tanpa menetapkan di antaranya?

Mengangkat bahu - Jangan lakukan itu.

asyncio tentu lebih eksplisit tentang bagian mana dari basis kode yang melakukan I/O, jadi saya cenderung membaginya menjadi baris terpisah.

form = await request.form()
form['foo']

Meskipun saya siap untuk menambahkan dukungan async, kami masih tidak dapat memecahkan Python 2 dan menyinkronkan versi. Salah satu pendekatan yang saya dengar dari @njsmith adalah menulis semuanya sebagai async, lalu menggunakan alat yang mirip dengan 2to3 untuk menghasilkan versi sinkronisasi. Rupanya sedang dicoba di urllib3, tetapi saya tidak cukup tahu tentang itu.

Saya ingin tahu apakah kita bisa menambahkan keajaiban ke objek di belakang request.form dll. Jadi memanggil mereka akan melakukan hal-hal async sementara metode seperti dict yang biasa akan disinkronkan (dan akan gagal dalam mode async). Atau kita bisa saja gagal mengakses request.form dll. dalam mode async dan menggunakan nama terpisah untuk versi async, misalnya request.parse_form() .

Atau... request_class = AsyncRequest jika seseorang ingin async; ini sebenarnya bisa menjadi default di kelas AsyncFlask .

Atau... request_class = AsyncRequest jika seseorang menginginkan async; ini sebenarnya bisa menjadi default di kelas AsyncFlask.

Itu jenis pendekatan yang masuk akal bagi saya, ya.

Mengenai pengurai formulir, saya telah mencoba meng-sansio-nya di #1330.

Ada juga implementasi parser formulir streaming di https://github.com/andrew-d/python-multipart dengan cakupan 100%. (Saya menemukan satu yang lain tetapi tidak jelas bahwa itu dapat dengan mudah diadaptasi menjadi aliran "data umpan, tangani peristiwa".)

python-multipart adalah perpustakaan yang sekarang saya gunakan untuk Starlette. Anda dapat melihat integrasi dengan aliran async di sini: https://github.com/encode/starlette/blob/master/starlette/formparsers.py#L207

Saya juga telah memikirkan cara terbaik untuk menyajikan antarmuka yang kompatibel dengan sinkronisasi dan asinkron, karena saya juga menginginkannya untuk Starlette (walaupun menuju ke arah lain ke Werkzeug, bagi saya ini tentang "Saya memiliki antarmuka async yang sudah ada, bagaimana caranya? sekarang juga hadirkan sinkronisasi")

Untuk Starlette, saya pikir saya mungkin akan mendorong penguraian aktual ke dalam metode async parse(self) , dan biasanya memanggil itu kadang-kadang selama pengiriman permintaan, tetapi mengekspos properti biasa biasa form , files dll ... untuk mengakses hasil dari kode pengguna.

Di luar topik, tetapi terkait dengan ASGI dan kasus werkzeug yang populer (Saya tidak ingin menggagalkan masalah ini, tetapi tidak ingin membuat masalah duplikat tanpa substansi untuk ditambahkan):

Jika Anda menjalankan https://github.com/django-extensions/Django-extensions dengan ./manage.py runserver_plus dan https://github.com/django/channels (yang menggunakan ASGI) memberikan Opcode -1 (opcode minus 1) di inspektur, coba jalankan ./manage.py runserver untuk saat ini hingga ASGI didukung.

(Werkzeug digunakan di bawah tenda pada Django dengan ekstensi-django dan saya menghabiskan beberapa jam mencari tahu mengapa karena saya sangat terbiasa mengembangkan dengan runserver_plus untuk debugging)

Saya menggunakan runserver_plus sehingga saya dapat menggunakan https dalam pengembangan.
Saya sekarang mencoba menambahkan dukungan Websockets menggunakan saluran Django, dan mengalami masalah bahwa saluran menggunakan runserver dan tidak mendukung runserver_plus. Jadi, saya bisa menggunakan saluran ATAU saya bisa menggunakan https, tidak keduanya!

@davidism tolong beri tahu saya jika ada perubahan dalam implementasi dukungan ASGI untuk Flask sejak jawaban terakhir Anda dalam topik ini dan apakah rencana Anda telah berubah sehubungan dengan tugas ini untuk mengakhiri dukungan untuk Python 2 ?

Pada 2 Juli 2018 @davidism menulis:

Rupanya sedang dicoba di urllib3, tetapi saya tidak cukup tahu tentang itu.

Baru saja melihat ini beberapa waktu lalu – jika ada yang tertarik untuk mempelajari lebih lanjut, sepertinya python-trio/urllib3#1 memiliki banyak detail. Perhatikan tautan ke urllib3/urllib3#1323, yang berisi:

Solusi: kami mempertahankan satu salinan kode – versi dengan anotasi async/await – dan kemudian sedikit skrip mempertahankan salinan sinkron dengan menghapusnya lagi secara otomatis. Itu tidak indah, tetapi sejauh yang saya tahu semua alternatif lebih buruk ...

(Terus membaca di sana jika tertarik.)

Senang melihat ini tampaknya terus bekerja dengan baik, berdasarkan kemajuan stabil yang dibuat di https://github.com/python-trio/urllib3/commits/bleach-spike.

Benjolan kecil membuat masalah ini kembali ke radar. Dengan 3,5 mencapai EOL tahun ini, saya pikir ini saat yang tepat untuk mulai memikirkan dukungan async?

Dalam satu setengah tahun sejak ini diposting, belum banyak kegiatan untuk mengimplementasikannya. Saya pribadi tidak memiliki pengalaman atau kebutuhan untuk asyncio, dan meskipun saya menyukai ASGI, itu tidak pernah menjadi sesuatu yang akan saya ambil sendiri.

Sementara itu, @pgjones , penulis Quart, menjadi lebih terlibat dalam Werkzeug. Quart sekarang menggunakan Werkzeug di belakang layar jika memungkinkan, dan kami terus mengembangkannya. Ada beberapa penolakan dari pengelola Flask tentang menggunakan ASGI, jadi Phil juga membuat palet/flask#3412 yang setidaknya memungkinkan perutean ke fungsi async def , tetapi itu sudah lama tidak digunakan. Pada titik ini saya lebih suka pergi ASGI daripada puas dengan itu. @edk0 dibuat #1330 untuk membuat penguraian formulir sans-io, tetapi itu juga sudah ada, dan mungkin harus melalui beberapa desain dan peninjauan lagi terlebih dahulu.

Anda mungkin bertanya, "Mengapa Flask tidak bisa melakukan apa yang Django lakukan?" Saya bukan ahli dalam internal Django, tetapi @andrewgodwin menjelaskan kepada saya beberapa waktu lalu bahwa Django memiliki waktu yang "lebih mudah" (baca: masih sangat rumit) karena cara awalnya disesuaikan dengan WSGI, sebagai lawan dari API yang sangat WSGI-sentris yang dimulai oleh Werkzeug dan Flask. Juga, Django hanya mendapat satu ton lebih banyak perhatian penuh waktu dan sumber daya daripada Pallet.

Jadi di mana itu meninggalkan masalah ini? Jika Anda menginginkan kerangka kerja yang kompatibel dengan Flask yang menggunakan Werkzeug, gunakan Quart. Berkontribusi ke Quart (atau Flask) untuk membuatnya lebih kompatibel dengan API jika tidak ada. Jika Anda ingin Werkzeug dan Flask mendukung ASGI, Anda harus meningkatkannya. Mulailah belajar tentang ASGI. Mulai identifikasi bagian khusus WSGI dan pemblokiran dari API Werkzeug. Mulailah memikirkan abstraksi yang dapat kita buat untuk mengaktifkan implementasi untuk WSGI dan ASGI. Kemudian bawa penelitian itu kembali ke diskusi ini sehingga kita bisa mulai merancang dan menulis PR.

Terima kasih atas saran Quart , saya akan sangat senang menerima kontribusi untuk itu.

Saya sudah mencoba menjawab mengapa menurut saya Flask tidak bisa melakukan apa yang telah dilakukan Django di artikel ini. Pada akhirnya saya pikir pallets/flask#3412 adalah solusi terbaik untuk Flask.

Dalam hal Werkzeug saya pikir ASGI itu mungkin, dengan sedikit rasa sakit. Sebuah contoh penting dari rasa sakit adalah bahwa banyak hal di Werkzeug adalah callable WSGI (misalnya pengecualian). Dengan ASGI tidak jelas bagaimana fungsi ini dapat/harus digunakan, jadi saya lebih memilih untuk menghapusnya.

Rencana saya adalah untuk terus mengintegrasikan Werkzeug ke Quart menyesuaikan Werkzeug terhadap ASGI (sans-io) saat saya pergi (sebanyak yang dapat diterima) - satu-satunya kendala saya adalah kurangnya waktu.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat