diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/back-symbolic.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/back-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0c2d653cbe8f9047f989226d5a00f7fd74ac0695
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/back-symbolic.svg
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/back.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/back.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0c2d653cbe8f9047f989226d5a00f7fd74ac0695
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/back.svg
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/back_large.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/back_large.png
new file mode 100644
index 0000000000000000000000000000000000000000..e44a70a9cd230e86135e1f27492fc649bc464f00
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/back_large.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave-symbolic.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..856721b6b5e2b671f28838a9e3569bc22b38215e
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave-symbolic.svg
@@ -0,0 +1,68 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave.pdf b/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..794a1152f602306c0326b0bac9b194323c521d83
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave.pdf
@@ -0,0 +1,70 @@
+%PDF-1.4
+%† Ǽ
+1 0 obj
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+8 0 obj
+<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R
+/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R
+/XObject 7 0 R >>
+endobj
+10 0 obj
+<< /Annots [ ] /Contents 9 0 R
+/Group << /CS /DeviceRGB /S /Transparency /Type /Group >>
+/MediaBox [ 0 0 72 72 ] /Parent 2 0 R /Resources 8 0 R /Type /Page >>
+endobj
+9 0 obj
+<< /Filter /FlateDecode /Length 11 0 R >>
+stream
+xÚu”Kn1D÷:…N@‹©Ï2A€Ùç
+
ljÑÉÆ@|ý”¤Ö§£ÛoÄb‘"‡í«yúÄöåÍ:ûŠ¿wËöaŸ¾<ÿþq<}|¶Ç›qà?Mòxœõ�Ï Øß�ùß^]UõÝÏ$>zµìȧœ2“y¡,ZßN£Ž4¹ÿÓ_ªþ«:™�äb;¸P&—š¤Ê®§)”˜k¬2¥r#‡ñæ�†3yëû¢§ë™bc—“Ã,¶ÕÁÈËÍ«+òc*aT»1&'ÍÞ´
+Z¸šê•-pñðÞ"=�¹\Â-X´œ Æÿaz�Ý«D’¼IT´™ób%ãû\Õ5Qr½ÿZˆ¥�uzõ(ŠT?PTÅùˆRJwåï@®–f Ûüˆ£¬4„JÁôlãWÅA|Z¨šÓ-AðfäHêEÞ‚ðŒm 6ÒLft"†Vë¹Sô+÷È®¾šº\,Áiøv!³²íRfý#ÉF¦™½‹º2–oƒáív#æ§÷ `:lô¤±%ÅN»vÇLEÚµ¡Âз]J=ÒÎö«ÆHôÑ‹a,Ý5c`¿ªf=¿ài0W¥·`5E?Öu!7†x¢2vk’t
þÔÖ±Ó‚®ÅBa1HÒëwgÀÕ½!¿H¦ð3û͘?»\ª
+endstream
+endobj
+11 0 obj
+500
+endobj
+3 0 obj
+<< >>
+endobj
+4 0 obj
+<< /A1 << /CA 0 /Type /ExtGState /ca 0 >>
+/A2 << /CA 1 /Type /ExtGState /ca 1 >> >>
+endobj
+5 0 obj
+<< >>
+endobj
+6 0 obj
+<< >>
+endobj
+7 0 obj
+<< >>
+endobj
+2 0 obj
+<< /Count 1 /Kids [ 10 0 R ] /Type /Pages >>
+endobj
+12 0 obj
+<< /CreationDate (D:20160601123507-04'00')
+/Creator (matplotlib 1.5.2rc2.post1933.dev0+gb775a26, http://matplotlib.org)
+/Producer (matplotlib pdf backend) >>
+endobj
+xref
+0 13
+0000000000 65535 f
+0000000016 00000 n
+0000001161 00000 n
+0000000978 00000 n
+0000000999 00000 n
+0000001098 00000 n
+0000001119 00000 n
+0000001140 00000 n
+0000000065 00000 n
+0000000383 00000 n
+0000000208 00000 n
+0000000958 00000 n
+0000001221 00000 n
+trailer
+<< /Info 12 0 R /Root 1 0 R /Size 13 >>
+startxref
+1395
+%%EOF
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave.png
new file mode 100644
index 0000000000000000000000000000000000000000..919e40bf58297a9abc727b7fbce9f53cf039ba5e
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave.svg
new file mode 100644
index 0000000000000000000000000000000000000000..856721b6b5e2b671f28838a9e3569bc22b38215e
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/filesave.svg
@@ -0,0 +1,68 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/forward-symbolic.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/forward-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..08b6fe174602b0c729b60cfe63af22468798ee95
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/forward-symbolic.svg
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/forward.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/forward.svg
new file mode 100644
index 0000000000000000000000000000000000000000..08b6fe174602b0c729b60cfe63af22468798ee95
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/forward.svg
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/forward_large.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/forward_large.png
new file mode 100644
index 0000000000000000000000000000000000000000..de65815bba205d862508648dc1fe0d0ed2b058a3
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/forward_large.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/hand.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/hand.png
new file mode 100644
index 0000000000000000000000000000000000000000..d956c5fd72aed98763c20370c025835202484fa8
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/hand.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/hand.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/hand.svg
new file mode 100644
index 0000000000000000000000000000000000000000..28b96a2a9c4ac8abfabe8a0d8ba3d8ff0a62b573
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/hand.svg
@@ -0,0 +1,130 @@
+
+
+]>
+
+
\ No newline at end of file
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/help-symbolic.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/help-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..260528d607aa59c7fabd5e8645a941064967645f
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/help-symbolic.svg
@@ -0,0 +1,52 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/help.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/help.png
new file mode 100644
index 0000000000000000000000000000000000000000..a52fbbe819e2fcbb1c1d5f8f143fc8d069688b3a
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/help.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/help.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/help.svg
new file mode 100644
index 0000000000000000000000000000000000000000..260528d607aa59c7fabd5e8645a941064967645f
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/help.svg
@@ -0,0 +1,52 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/help_large.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/help_large.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f3d4dfed7e99421bd024c14046bf4616b4ca2ff
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/help_large.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/home_large.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/home_large.png
new file mode 100644
index 0000000000000000000000000000000000000000..3357bfeb9011141ebce16febf6539f610f12ecc9
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/home_large.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/matplotlib.pdf b/lib/python3.12/site-packages/matplotlib/mpl-data/images/matplotlib.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..6c44566ad819ba31e93bf50d3b7c2b9fa1bb8f64
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/matplotlib.pdf differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/matplotlib_large.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/matplotlib_large.png
new file mode 100644
index 0000000000000000000000000000000000000000..c7dcfe6cb8255b332d9c6d15d8049ec63c3141e5
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/matplotlib_large.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/move-symbolic.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/move-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f7e23ab0451ce2c3f6290e1f093771a0a1a58c9b
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/move-symbolic.svg
@@ -0,0 +1,73 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/qt4_editor_options.pdf b/lib/python3.12/site-packages/matplotlib/mpl-data/images/qt4_editor_options.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..a92f2cc3aaee87f45926546bcc135f40a357160a
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/qt4_editor_options.pdf differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/qt4_editor_options.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/qt4_editor_options.png
new file mode 100644
index 0000000000000000000000000000000000000000..792ec81244e3b8f3fe3cd28ef4898d934a1d73a3
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/qt4_editor_options.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/qt4_editor_options.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/qt4_editor_options.svg
new file mode 100644
index 0000000000000000000000000000000000000000..02adfbc4ae11f473606591813e3360cbf39111fe
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/qt4_editor_options.svg
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/subplots-symbolic.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/subplots-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9a0fa90972ff2788edb7791e057e2aced59cf84f
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/subplots-symbolic.svg
@@ -0,0 +1,81 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/subplots.png b/lib/python3.12/site-packages/matplotlib/mpl-data/images/subplots.png
new file mode 100644
index 0000000000000000000000000000000000000000..bb0318c40e72343d52227ef29048224b5c7177ae
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/images/subplots.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/subplots.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/subplots.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9a0fa90972ff2788edb7791e057e2aced59cf84f
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/subplots.svg
@@ -0,0 +1,81 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/zoom_to_rect-symbolic.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/zoom_to_rect-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..44e59a8a4fdca9148d0912d4699c5626dd676834
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/zoom_to_rect-symbolic.svg
@@ -0,0 +1,40 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/zoom_to_rect.pdf b/lib/python3.12/site-packages/matplotlib/mpl-data/images/zoom_to_rect.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..22add33bb8f9f99112681f4ddc52ec96dca8c537
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/zoom_to_rect.pdf
@@ -0,0 +1,68 @@
+%PDF-1.4
+%† Ǽ
+1 0 obj
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+8 0 obj
+<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R
+/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R
+/XObject 7 0 R >>
+endobj
+10 0 obj
+<< /Annots [ ] /Contents 9 0 R
+/Group << /CS /DeviceRGB /S /Transparency /Type /Group >>
+/MediaBox [ 0 0 72 72 ] /Parent 2 0 R /Resources 8 0 R /Type /Page >>
+endobj
+9 0 obj
+<< /Filter /FlateDecode /Length 11 0 R >>
+stream
+xÚmRKn1Ûë>�b},ÛËÞ¾W$iƒ×n´×/m÷�H€Ø $’’,é�ž¾Hz}O9½áÿ›$ÝÒÓ·ç??�çï·¯éx§üUÅq¾;Àyü ú
]¯¨,·q'òÆâ¹[MÖÙ¥Gk º ™³7·–>
+endobj
+4 0 obj
+<< /A1 << /CA 0 /Type /ExtGState /ca 0 >>
+/A2 << /CA 1 /Type /ExtGState /ca 1 >> >>
+endobj
+5 0 obj
+<< >>
+endobj
+6 0 obj
+<< >>
+endobj
+7 0 obj
+<< >>
+endobj
+2 0 obj
+<< /Count 1 /Kids [ 10 0 R ] /Type /Pages >>
+endobj
+12 0 obj
+<< /CreationDate (D:20160601123507-04'00')
+/Creator (matplotlib 1.5.2rc2.post1933.dev0+gb775a26, http://matplotlib.org)
+/Producer (matplotlib pdf backend) >>
+endobj
+xref
+0 13
+0000000000 65535 f
+0000000016 00000 n
+0000001036 00000 n
+0000000853 00000 n
+0000000874 00000 n
+0000000973 00000 n
+0000000994 00000 n
+0000001015 00000 n
+0000000065 00000 n
+0000000383 00000 n
+0000000208 00000 n
+0000000833 00000 n
+0000001096 00000 n
+trailer
+<< /Info 12 0 R /Root 1 0 R /Size 13 >>
+startxref
+1270
+%%EOF
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/images/zoom_to_rect.svg b/lib/python3.12/site-packages/matplotlib/mpl-data/images/zoom_to_rect.svg
new file mode 100644
index 0000000000000000000000000000000000000000..44e59a8a4fdca9148d0912d4699c5626dd676834
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/images/zoom_to_rect.svg
@@ -0,0 +1,40 @@
+
+
+
+
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/Minduka_Present_Blue_Pack.png b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/Minduka_Present_Blue_Pack.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b1aff4e3c4b050e2f00a1ed34dbfee8a4558db2
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/Minduka_Present_Blue_Pack.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/README.txt b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..75a534951d60ef83ede2da26aec25747fbfb95b3
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/README.txt
@@ -0,0 +1,2 @@
+This is the sample data needed for some of matplotlib's examples and
+docs. See matplotlib.cbook.get_sample_data for more info.
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/Stocks.csv b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/Stocks.csv
new file mode 100644
index 0000000000000000000000000000000000000000..575d353dffe23db9213e2f7edece89d7f3365877
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/Stocks.csv
@@ -0,0 +1,526 @@
+# Data source: https://finance.yahoo.com
+Date,IBM,AAPL,MSFT,XRX,AMZN,DELL,GOOGL,ADBE,^GSPC,^IXIC
+1990-01-01,10.970438003540039,0.24251236021518707,0.40375930070877075,11.202081680297852,,,,1.379060983657837,329.0799865722656,415.79998779296875
+1990-02-01,11.554415702819824,0.24251236021518707,0.43104037642478943,10.39472484588623,,,,1.7790844440460205,331.8900146484375,425.79998779296875
+1990-02-05,,,,,,,,,,
+1990-03-01,11.951693534851074,0.28801724314689636,0.4834197461605072,11.394058227539062,,,,2.2348830699920654,339.94000244140625,435.5
+1990-04-01,12.275476455688477,0.2817564308643341,0.5063362717628479,10.139430046081543,,,,2.2531447410583496,330.79998779296875,420.1000061035156
+1990-05-01,13.514284133911133,0.295173317193985,0.6372847557067871,9.704153060913086,,,,2.0985164642333984,361.2300109863281,459.0
+1990-05-04,,,,,,,,,,
+1990-06-01,13.380948066711426,0.3211067020893097,0.6634747385978699,9.749448776245117,,,,2.164785146713257,358.0199890136719,462.29998779296875
+1990-07-01,12.697657585144043,0.3013734817504883,0.5805402994155884,9.489465713500977,,,,2.069063901901245,356.1499938964844,438.20001220703125
+1990-08-01,11.601561546325684,0.26549553871154785,0.5368908047676086,8.553519248962402,,,,1.4750508069992065,322.55999755859375,381.20001220703125
+1990-08-06,,,,,,,,,,
+1990-09-01,12.251119613647461,0.20872052013874054,0.5499854683876038,7.255113124847412,,,,1.1210384368896484,306.04998779296875,344.5
+1990-10-01,12.15034294128418,0.22131578624248505,0.5565334558486938,6.248927116394043,,,,1.416249394416809,304.0,329.79998779296875
+1990-11-01,13.08609390258789,0.26449888944625854,0.6307373642921448,7.361023426055908,,,,1.4900128841400146,322.2200012207031,359.1000061035156
+1990-11-05,,,,,,,,,,
+1990-12-01,13.161062240600586,0.31051647663116455,0.6569272875785828,7.519896507263184,,,,1.7186784744262695,330.2200012207031,373.79998779296875
+1991-01-01,14.762505531311035,0.40078282356262207,0.8566243648529053,10.554410934448242,,,,2.242394208908081,343.92999267578125,414.20001220703125
+1991-02-01,14.995450973510742,0.4134202301502228,0.9057300686836243,12.286416053771973,,,,2.8402483463287354,367.07000732421875,453.1000061035156
+1991-02-04,,,,,,,,,,
+1991-03-01,13.39067268371582,0.49208223819732666,0.92646324634552,12.511940002441406,,,,3.1869795322418213,375.2200012207031,482.29998779296875
+1991-04-01,12.111870765686035,0.39800751209259033,0.8642628788948059,12.511940002441406,,,,3.0589873790740967,375.3399963378906,484.7200012207031
+1991-05-01,12.479341506958008,0.34011581540107727,0.9581103920936584,12.73144817352295,,,,2.9850986003875732,389.8299865722656,506.1099853515625
+1991-05-06,,,,,,,,,,
+1991-06-01,11.555957794189453,0.30108359456062317,0.89208984375,11.853414535522461,,,,2.5565452575683594,371.1600036621094,475.9200134277344
+1991-07-01,12.04675579071045,0.33554431796073914,0.9624748229980469,12.397871017456055,,,,3.1678967475891113,387.80999755859375,502.0400085449219
+1991-08-01,11.526222229003906,0.3845158815383911,1.1163398027420044,13.037229537963867,,,,3.012463331222534,395.42999267578125,525.6799926757812
+1991-08-06,,,,,,,,,,
+1991-09-01,12.478833198547363,0.3599340617656708,1.1654454469680786,13.740047454833984,,,,3.0346662998199463,387.8599853515625,526.8800048828125
+1991-10-01,11.83155345916748,0.37447676062583923,1.2292828559875488,14.41578483581543,,,,3.1431360244750977,392.45001220703125,542.97998046875
+1991-11-01,11.139124870300293,0.36902356147766113,1.2734787464141846,13.965290069580078,,,,2.846613883972168,375.2200012207031,523.9000244140625
+1991-11-04,,,,,,,,,,
+1991-12-01,10.851117134094238,0.4109107553958893,1.4568067789077759,15.42939281463623,,,,3.8844411373138428,417.0899963378906,586.3400268554688
+1992-01-01,10.973037719726562,0.4719553291797638,1.5746610164642334,17.584869384765625,,,,3.639812469482422,408.7799987792969,620.2100219726562
+1992-02-01,10.592026710510254,0.49199995398521423,1.61721932888031,18.04088020324707,,,,3.3547844886779785,412.70001220703125,633.469970703125
+1992-02-06,,,,,,,,,,
+1992-03-01,10.317353248596191,0.4253714978694916,1.5517452955245972,16.302349090576172,,,,3.043056011199951,403.69000244140625,603.77001953125
+1992-04-01,11.213163375854492,0.43906375765800476,1.4437123537063599,17.062589645385742,,,,2.631263494491577,414.95001220703125,578.6799926757812
+1992-05-01,11.213163375854492,0.4363253116607666,1.5844820737838745,17.263996124267578,,,,2.705594062805176,415.3500061035156,585.3099975585938
+1992-05-07,,,,,,,,,,
+1992-06-01,12.25231647491455,0.3505205810070038,1.3749638795852661,16.055517196655273,,,,2.705594062805176,408.1400146484375,563.5999755859375
+1992-07-01,11.861115455627441,0.34207984805107117,1.4289811849594116,17.38025665283203,,,,2.263554334640503,424.2099914550781,580.8300170898438
+1992-08-01,10.84400749206543,0.33659130334854126,1.4633547067642212,17.525571823120117,,,,1.9359338283538818,414.0299987792969,563.1199951171875
+1992-08-06,,,,,,,,,,
+1992-09-01,10.243829727172852,0.3310767114162445,1.58120858669281,18.464359283447266,,,,1.6753284931182861,417.79998779296875,583.27001953125
+1992-10-01,8.483662605285645,0.38518592715263367,1.7432584762573242,17.436922073364258,,,,2.12785267829895,418.67999267578125,605.1699829101562
+1992-11-01,8.658098220825195,0.42187052965164185,1.8291932344436646,18.493711471557617,,,,2.030792474746704,431.3500061035156,652.72998046875
+1992-11-05,,,,,,,,,,
+1992-12-01,6.505843639373779,0.43931084871292114,1.6769649982452393,18.788379669189453,,,,1.8814688920974731,435.7099914550781,676.9500122070312
+1993-01-01,6.651134490966797,0.43747296929359436,1.6990629434585571,20.329378128051758,,,,2.4787611961364746,438.7799987792969,696.3400268554688
+1993-02-01,7.022435188293457,0.38968151807785034,1.6376806497573853,19.618152618408203,,,,2.670894145965576,443.3800048828125,670.77001953125
+1993-02-04,,,,,,,,,,
+1993-03-01,6.6405534744262695,0.37947842478752136,1.8169171810150146,19.766319274902344,,,,2.573633909225464,451.6700134277344,690.1300048828125
+1993-04-01,6.346868991851807,0.3776364326477051,1.6794201135635376,18.451818466186523,,,,3.434307336807251,440.19000244140625,661.4199829101562
+1993-05-01,6.885295867919922,0.4172421991825104,1.8193715810775757,18.12286949157715,,,,3.959200859069824,450.19000244140625,700.530029296875
+1993-05-06,,,,,,,,,,
+1993-06-01,6.51603364944458,0.29166528582572937,1.7285257577896118,19.299583435058594,,,,3.7042527198791504,450.5299987792969,703.9500122070312
+1993-07-01,5.872671604156494,0.2049039751291275,1.453533411026001,17.638439178466797,,,,2.9742372035980225,448.1300048828125,704.7000122070312
+1993-08-01,6.037637233734131,0.19567380845546722,1.4756313562393188,17.759246826171875,,,,2.5536391735076904,463.55999755859375,742.8400268554688
+1993-08-05,,,,,,,,,,
+1993-09-01,5.574150562286377,0.1733584851026535,1.620492935180664,17.849544525146484,,,,2.1931254863739014,458.92999267578125,762.780029296875
+1993-10-01,6.105024337768555,0.22805523872375488,1.5738422870635986,19.34462547302246,,,,2.6137242317199707,467.8299865722656,779.260009765625
+1993-11-01,7.150176048278809,0.2336171418428421,1.5713871717453003,20.107433319091797,,,,2.786593437194824,461.7900085449219,754.3900146484375
+1993-11-04,,,,,,,,,,
+1993-12-01,7.535686016082764,0.21771006286144257,1.5836634635925293,22.01595687866211,,,,2.68115496635437,466.45001220703125,776.7999877929688
+1994-01-01,7.535686016082764,0.24376071989536285,1.672054648399353,24.171361923217773,,,,3.64516544342041,481.6099853515625,800.469970703125
+1994-02-01,7.052198886871338,0.27167221903800964,1.620492935180664,23.894229888916016,,,,3.5310842990875244,467.1400146484375,792.5
+1994-02-04,,,,,,,,,,
+1994-03-01,7.31842041015625,0.24837146699428558,1.6646885871887207,23.706161499023438,,,,2.9274797439575195,445.7699890136719,743.4600219726562
+1994-04-01,7.703606128692627,0.22409436106681824,1.8169171810150146,24.543949127197266,,,,3.2354440689086914,450.9100036621094,733.8400268554688
+1994-05-01,8.440468788146973,0.21849241852760315,2.1115520000457764,24.947307586669922,,,,3.4773480892181396,456.5,735.1900024414062
+1994-05-05,,,,,,,,,,
+1994-06-01,7.905290126800537,0.19873161613941193,2.028071165084839,24.444643020629883,,,,3.2959203720092773,444.2699890136719,705.9600219726562
+1994-07-01,8.325791358947754,0.2526327669620514,2.023160934448242,25.569963455200195,,,,3.756444215774536,458.260009765625,722.1599731445312
+1994-08-01,9.217235565185547,0.27138155698776245,2.2834222316741943,26.789077758789062,,,,3.847325563430786,475.489990234375,765.6199951171875
+1994-08-04,,,,,,,,,,
+1994-09-01,9.405942916870117,0.25350791215896606,2.2048532962799072,26.88198471069336,,,,3.9382081031799316,462.7099914550781,764.2899780273438
+1994-10-01,10.064526557922363,0.324998676776886,2.474935293197632,25.811735153198242,,,,4.368794918060303,472.3500061035156,777.489990234375
+1994-11-01,9.557921409606934,0.28031668066978455,2.470024824142456,24.741479873657227,,,,4.004726409912109,453.69000244140625,750.3200073242188
+1994-11-04,,,,,,,,,,
+1994-12-01,9.9633207321167,0.2943686842918396,2.401276111602783,25.12115478515625,,,,3.6103241443634033,459.2699890136719,751.9600219726562
+1995-01-01,9.77692985534668,0.3047473132610321,2.332528591156006,27.753808975219727,,,,3.5117223262786865,470.4200134277344,755.2000122070312
+1995-02-01,10.200541496276855,0.29814332723617554,2.474935293197632,28.13443946838379,,,,4.345465660095215,487.3900146484375,793.72998046875
+1995-02-06,,,,,,,,,,
+1995-03-01,11.169903755187988,0.2667955756187439,2.7941226959228516,29.989917755126953,,,,6.016797065734863,500.7099914550781,817.2100219726562
+1995-04-01,12.870043754577637,0.2895018756389618,3.2115228176116943,31.52293586730957,,,,7.08742618560791,514.7100219726562,843.97998046875
+1995-05-01,12.649023056030273,0.31457316875457764,3.326920747756958,28.96788215637207,,,,6.326970100402832,533.4000244140625,864.5800170898438
+1995-05-04,,,,,,,,,,
+1995-06-01,13.091790199279785,0.3524453043937683,3.5503528118133545,30.15083122253418,,,,7.057007789611816,544.75,933.4500122070312
+1995-07-01,14.847586631774902,0.3415350615978241,3.5552642345428467,30.697277069091797,,,,7.513277530670166,562.0599975585938,1001.2100219726562
+1995-08-01,14.09753131866455,0.32635587453842163,3.6338343620300293,31.08300018310547,,,,6.210629463195801,561.8800048828125,1020.1099853515625
+1995-08-08,,,,,,,,,,
+1995-09-01,12.916881561279297,0.28348639607429504,3.5552642345428467,34.767181396484375,,,,6.301961898803711,584.4099731445312,1043.5400390625
+1995-10-01,13.292764663696289,0.27635207772254944,3.92846941947937,33.5381965637207,,,,6.941293239593506,581.5,1036.06005859375
+1995-11-01,13.207347869873047,0.2901458144187927,3.4226772785186768,35.478694915771484,,,,8.243063926696777,605.3699951171875,1059.199951171875
+1995-11-08,,,,,,,,,,
+1995-12-01,12.52143669128418,0.24333631992340088,3.447230815887451,35.68303298950195,,,,7.557409763336182,615.9299926757812,1052.1300048828125
+1996-01-01,14.868143081665039,0.21089188754558563,3.6338343620300293,32.199371337890625,,,,4.14438533782959,636.02001953125,1059.7900390625
+1996-02-01,16.80373764038086,0.2099376916885376,3.876908779144287,33.924922943115234,,,,4.089058876037598,640.4299926757812,1100.050048828125
+1996-02-07,,,,,,,,,,
+1996-03-01,15.278267860412598,0.1875123232603073,4.051232814788818,32.900360107421875,,,,3.936483860015869,645.5,1101.4000244140625
+1996-04-01,14.797598838806152,0.1860809624195099,4.448990821838379,38.40559768676758,,,,5.248644828796387,654.1699829101562,1190.52001953125
+1996-05-01,14.660264015197754,0.1994406133890152,4.6650567054748535,41.25652313232422,,,,4.538435459136963,669.1199951171875,1243.4300537109375
+1996-05-08,,,,,,,,,,
+1996-06-01,13.641029357910156,0.1603158563375473,4.719073295593262,42.07575225830078,,,,4.385625839233398,670.6300048828125,1185.02001953125
+1996-07-01,14.812233924865723,0.1679503321647644,4.630680561065674,39.836021423339844,,,,3.7132644653320312,639.9500122070312,1080.5899658203125
+1996-08-01,15.759532928466797,0.18512675166130066,4.812370777130127,43.394588470458984,,,,4.269299030303955,651.989990234375,1141.5
+1996-08-07,,,,,,,,,,
+1996-09-01,17.209583282470703,0.16938161849975586,5.180670261383057,42.40607833862305,,,,4.5600385665893555,687.3300170898438,1226.9200439453125
+1996-10-01,17.831613540649414,0.17558389902114868,5.391822814941406,36.86507034301758,,,,4.244296073913574,705.27001953125,1221.510009765625
+1996-11-01,22.03032875061035,0.184172585606575,6.162785530090332,38.95176315307617,,,,4.841867923736572,757.02001953125,1292.6099853515625
+1996-11-06,,,,,,,,,,
+1996-12-01,20.998197555541992,0.15936164557933807,6.491794109344482,41.83340072631836,,,,4.581387996673584,740.739990234375,1291.030029296875
+1997-01-01,21.743183135986328,0.12691716849803925,8.01407527923584,46.8797492980957,,,,4.642676830291748,786.1599731445312,1379.8499755859375
+1997-02-01,19.924028396606445,0.1240537017583847,7.660515785217285,49.97840881347656,,,,4.47982120513916,790.8200073242188,1309.0
+1997-02-06,,,,,,,,,,
+1997-03-01,19.067983627319336,0.13932174444198608,7.203826904296875,45.4803581237793,,,,4.924733638763428,757.1199951171875,1221.699951171875
+1997-04-01,22.298084259033203,0.12977975606918335,9.546177864074707,49.431339263916016,,,,4.807621955871582,801.3400268554688,1260.760009765625
+1997-05-01,24.034704208374023,0.12691716849803925,9.742606163024902,54.45485305786133,,,,5.483453750610352,848.280029296875,1400.3199462890625
+1997-05-07,,,,,,,,,,
+1997-05-28,,,,,,,,,,
+1997-06-01,25.13742446899414,0.10878564417362213,9.929201126098633,63.396705627441406,0.07708299905061722,,,4.3084282875061035,885.1400146484375,1442.0699462890625
+1997-07-01,29.45465660095215,0.1335965245962143,11.107746124267578,66.42845916748047,0.11979199945926666,,,4.592585563659668,954.3099975585938,1593.81005859375
+1997-08-01,28.23609161376953,0.1660410314798355,10.385887145996094,60.97687911987305,0.11692699790000916,,,4.851738929748535,899.469970703125,1587.3199462890625
+1997-08-07,,,,,,,,,,
+1997-09-01,29.579116821289062,0.16556398570537567,10.395710945129395,67.9932632446289,0.21692700684070587,,,6.2071452140808105,947.280029296875,1685.68994140625
+1997-10-01,27.48627281188965,0.13001829385757446,10.214018821716309,64.32245635986328,0.25416699051856995,,,5.889601707458496,914.6199951171875,1593.6099853515625
+1997-11-01,30.555805206298828,0.13550494611263275,11.117568969726562,63.00460433959961,0.20624999701976776,,,5.180382251739502,955.4000244140625,1600.550048828125
+1997-11-06,,,,,,,,,,
+1997-12-01,29.25237274169922,0.10019782930612564,10.155089378356934,59.91267013549805,0.2510420083999634,,,5.087874889373779,970.4299926757812,1570.3499755859375
+1998-01-01,27.609769821166992,0.1397988647222519,11.721570014953613,65.44635009765625,0.24583299458026886,,,4.750911235809326,980.280029296875,1619.3599853515625
+1998-02-01,29.19993782043457,0.1803557574748993,13.317505836486816,72.36756134033203,0.3208329975605011,,,5.452751159667969,1049.3399658203125,1770.510009765625
+1998-02-06,,,,,,,,,,
+1998-03-01,29.10112953186035,0.2099376916885376,14.06391716003418,86.66805267333984,0.35637998580932617,,,5.576151371002197,1101.75,1835.6800537109375
+1998-04-01,32.4630012512207,0.20898354053497314,14.162128448486328,92.79229736328125,0.3822920024394989,,,6.177727699279785,1111.75,1868.4100341796875
+1998-05-01,32.918251037597656,0.2032574564218521,13.327329635620117,84.10579681396484,0.3671880066394806,,,4.930263519287109,1090.8199462890625,1778.8699951171875
+1998-05-06,,,,,,,,,,
+1998-06-01,32.225502014160156,0.2190026193857193,17.029911041259766,83.08383178710938,0.831250011920929,,,5.238887786865234,1133.8399658203125,1894.739990234375
+1998-07-01,37.19002914428711,0.26433059573173523,17.27544403076172,86.6082992553711,0.9239580035209656,,,3.988961935043335,1120.6700439453125,1872.3900146484375
+1998-08-01,31.611520767211914,0.23808826506137848,15.075493812561035,72.04536437988281,0.6979169845581055,,,3.2419238090515137,957.280029296875,1499.25
+1998-08-06,,,,,,,,,,
+1998-09-01,36.12905502319336,0.2910497486591339,17.295076370239258,69.53275299072266,0.9302080273628235,,,4.283971786499023,1017.010009765625,1693.8399658203125
+1998-10-01,41.75224685668945,0.2834153473377228,16.637067794799805,80.36154174804688,1.0536459684371948,,,4.59221076965332,1098.6700439453125,1771.3900146484375
+1998-11-01,46.4265251159668,0.2438134402036667,19.170928955078125,88.54693603515625,1.600000023841858,,,5.535391330718994,1163.6300048828125,1949.5400390625
+1998-11-06,,,,,,,,,,
+1998-12-01,51.91548538208008,0.3125200569629669,21.793180465698242,97.19573974609375,2.6770830154418945,,,5.782783031463623,1229.22998046875,2192.68994140625
+1999-01-01,51.59874725341797,0.3144294023513794,27.499271392822266,102.47120666503906,2.92343807220459,,,5.912916660308838,1279.6400146484375,2505.889892578125
+1999-02-01,47.797481536865234,0.2657618522644043,23.5904541015625,91.21175384521484,3.203125,,,4.984185218811035,1238.3299560546875,2288.030029296875
+1999-02-08,,,,,,,,,,
+1999-03-01,49.97552490234375,0.27435049414634705,28.16712188720703,88.21611785888672,4.304687976837158,,,7.027392387390137,1286.3699951171875,2461.39990234375
+1999-04-01,58.98025894165039,0.35116779804229736,25.554689407348633,97.44929504394531,4.301562786102295,,,7.854666709899902,1335.1800537109375,2542.860107421875
+1999-05-01,65.41220092773438,0.3363769054412842,25.35825538635254,93.19886779785156,2.96875,,,9.187017440795898,1301.8399658203125,2470.52001953125
+1999-05-06,,,,,,,,,,
+1999-05-27,,,,,,,,,,
+1999-06-01,72.96644592285156,0.35355332493782043,28.343910217285156,97.96764373779297,3.128124952316284,,,10.182408332824707,1372.7099609375,2686.1201171875
+1999-07-01,70.95524597167969,0.4251234233379364,26.96893310546875,81.35432434082031,2.50156307220459,,,10.634023666381836,1328.719970703125,2638.489990234375
+1999-08-01,70.32015991210938,0.49812400341033936,29.090301513671875,79.7938461303711,3.109375,,,12.354691505432129,1320.4100341796875,2739.35009765625
+1999-08-06,,,,,,,,,,
+1999-09-01,68.37564849853516,0.48333317041397095,28.46175193786621,69.8066177368164,3.996875047683716,,,14.07535457611084,1282.7099609375,2746.159912109375
+1999-10-01,55.519859313964844,0.6116815209388733,29.090301513671875,47.42917251586914,3.53125,,,17.35419464111328,1362.9300537109375,2966.429931640625
+1999-11-01,58.239349365234375,0.747186541557312,28.613975524902344,45.75767135620117,4.253125190734863,,,17.044021606445312,1388.9100341796875,3336.159912109375
+1999-11-08,,,,,,,,,,
+1999-12-01,61.04003143310547,0.7848799824714661,36.6919059753418,37.92245101928711,3.8062500953674316,,,16.687326431274414,1469.25,4069.31005859375
+2000-01-01,63.515560150146484,0.7920366525650024,30.75990104675293,35.14961242675781,3.2281250953674316,,,13.668244361877441,1394.4599609375,3940.35009765625
+2000-02-01,58.14006042480469,0.8750578165054321,28.088550567626953,36.62297058105469,3.4437499046325684,,,25.31960105895996,1366.4200439453125,4696.68994140625
+2000-02-08,,,,,,,,,,
+2000-03-01,67.05181884765625,1.0368047952651978,33.39197540283203,43.779178619384766,3.3499999046325684,,,27.631258010864258,1498.5799560546875,4572.830078125
+2000-04-01,63.157623291015625,0.947104275226593,21.920852661132812,45.03520965576172,2.7593750953674316,,,30.027847290039062,1452.4300537109375,3860.659912109375
+2000-05-01,60.785640716552734,0.6412634253501892,19.661985397338867,46.097347259521484,2.4156250953674316,,,27.948402404785156,1420.5999755859375,3400.909912109375
+2000-05-08,,,,,,,,,,
+2000-06-01,62.13500213623047,0.7996708750724792,25.142194747924805,35.529056549072266,1.8156249523162842,,,32.277984619140625,1454.5999755859375,3966.110107421875
+2000-07-01,63.65913009643555,0.7758141756057739,21.940492630004883,25.79067039489746,1.506250023841858,,,28.43518829345703,1430.8299560546875,3766.989990234375
+2000-08-01,74.86860656738281,0.9304049015045166,21.940492630004883,27.82395362854004,2.075000047683716,,,32.28449630737305,1517.6800537109375,4206.35009765625
+2000-08-08,,,,,,,,,,
+2000-09-01,63.94328689575195,0.3931553065776825,18.95485496520996,25.980575561523438,1.921875,,,38.555137634277344,1436.510009765625,3672.820068359375
+2000-10-01,55.92375183105469,0.29868337512016296,21.645862579345703,14.6140718460083,1.8312499523162842,,,37.785430908203125,1429.4000244140625,3369.6298828125
+2000-11-01,53.08496856689453,0.2519250810146332,18.03167152404785,12.016016960144043,1.234375,,,31.482683181762695,1314.949951171875,2597.929931640625
+2000-11-08,,,,,,,,,,
+2000-12-01,48.32046127319336,0.22711420059204102,13.631781578063965,8.067792892456055,0.778124988079071,,,28.90570068359375,1320.280029296875,2470.52001953125
+2001-01-01,63.6693229675293,0.33017459511756897,19.190568923950195,14.251648902893066,0.8656250238418579,,,21.702556610107422,1366.010009765625,2772.72998046875
+2001-02-01,56.790767669677734,0.2786443829536438,18.54237174987793,10.536102294921875,0.5093749761581421,,,14.440549850463867,1239.93994140625,2151.830078125
+2001-02-07,,,,,,,,,,
+2001-03-01,54.73835754394531,0.3369686007499695,17.18704605102539,10.535667419433594,0.5115000009536743,,,17.375865936279297,1160.3299560546875,1840.260009765625
+2001-04-01,65.52893829345703,0.38918623328208923,21.292295455932617,15.900238990783691,0.7889999747276306,,,22.32872772216797,1249.4599609375,2116.239990234375
+2001-05-01,63.62804412841797,0.3046000301837921,21.741714477539062,17.430465698242188,0.8345000147819519,,,19.768779754638672,1255.8199462890625,2110.489990234375
+2001-05-08,,,,,,,,,,
+2001-06-01,64.6737060546875,0.35498547554016113,22.942251205444336,16.83243751525879,0.7074999809265137,,,23.362648010253906,1224.3800048828125,2160.5400390625
+2001-07-01,59.949947357177734,0.28688937425613403,20.80202293395996,14.035829544067383,0.6244999766349792,,,18.640790939331055,1211.22998046875,2027.1300048828125
+2001-08-01,56.952762603759766,0.2832247018814087,17.929533004760742,16.18165397644043,0.44699999690055847,,,16.711578369140625,1133.5799560546875,1805.4300537109375
+2001-08-08,,,,,,,,,,
+2001-09-01,52.33213424682617,0.23680917918682098,16.081575393676758,13.6312894821167,0.2985000014305115,,,11.92334270477295,1040.93994140625,1498.800048828125
+2001-10-01,61.660858154296875,0.26810887455940247,18.275238037109375,12.312134742736816,0.3490000069141388,,,13.133195877075195,1059.780029296875,1690.199951171875
+2001-11-01,65.95150756835938,0.3252120614051819,20.17975616455078,14.774558067321777,0.5659999847412109,,,15.958827018737793,1139.449951171875,1930.5799560546875
+2001-11-07,,,,,,,,,,
+2001-12-01,69.1006088256836,0.3343726694583893,20.820878982543945,18.32748794555664,0.5410000085830688,,,15.446427345275879,1148.0799560546875,1950.4000244140625
+2002-01-01,61.63407897949219,0.37742963433265686,20.022619247436523,19.928070068359375,0.7095000147819519,,,16.771486282348633,1130.199951171875,1934.030029296875
+2002-02-01,56.052799224853516,0.3313194811344147,18.334945678710938,17.07868194580078,0.7049999833106995,,,18.105239868164062,1106.72998046875,1731.489990234375
+2002-02-06,,,,,,,,,,
+2002-03-01,59.49020767211914,0.3613981306552887,18.95407485961914,18.907917022705078,0.7149999737739563,,,20.051130294799805,1147.3900146484375,1845.3499755859375
+2002-04-01,47.912540435791016,0.3705587685108185,16.424144744873047,15.56605339050293,0.8345000147819519,,,19.893611907958984,1076.9200439453125,1688.22998046875
+2002-05-01,46.01911926269531,0.35574817657470703,15.999865531921387,15.777122497558594,0.9114999771118164,,,17.971961975097656,1067.1400146484375,1615.72998046875
+2002-05-08,,,,,,,,,,
+2002-06-01,41.26642990112305,0.2705524265766144,17.190977096557617,10.55325984954834,0.8125,,,14.188389778137207,989.8200073242188,1463.2099609375
+2002-07-01,40.349422454833984,0.23299239575862885,15.079033851623535,12.224191665649414,0.7225000262260437,,,11.933782577514648,911.6199951171875,1328.260009765625
+2002-08-01,43.203697204589844,0.22520573437213898,15.424735069274902,12.329721450805664,0.746999979019165,,,10.01123046875,916.0700073242188,1314.8499755859375
+2002-08-07,,,,,,,,,,
+2002-09-01,33.49409484863281,0.22138899564743042,13.746496200561523,8.706437110900879,0.796500027179718,,,9.513158798217773,815.280029296875,1172.06005859375
+2002-10-01,45.344242095947266,0.24535933136940002,16.804426193237305,11.678934097290039,0.9679999947547913,,,11.782204627990723,885.760009765625,1329.75
+2002-11-01,49.92806625366211,0.23665708303451538,18.127525329589844,15.337401390075684,1.1675000190734863,,,14.71778678894043,936.3099975585938,1478.780029296875
+2002-11-06,,,,,,,,,,
+2002-12-01,44.5990104675293,0.2187930941581726,16.248149871826172,14.15894889831543,0.9445000290870667,,,12.360349655151367,879.8200073242188,1335.510009765625
+2003-01-01,45.00184631347656,0.21925139427185059,14.91561222076416,15.56605339050293,1.0924999713897705,,,13.167760848999023,855.7000122070312,1320.9100341796875
+2003-02-01,44.85797119140625,0.22917553782463074,14.896756172180176,15.829883575439453,1.1004999876022339,,,13.712512969970703,841.1500244140625,1337.52001953125
+2003-02-06,,,,,,,,,,
+2003-03-01,45.22197723388672,0.2158920168876648,15.266251564025879,15.302220344543457,1.3014999628067017,,,15.372973442077637,848.1799926757812,1341.1700439453125
+2003-04-01,48.95253372192383,0.21711383759975433,16.123830795288086,17.342517852783203,1.434499979019165,,,17.22471046447754,916.9199829101562,1464.31005859375
+2003-05-01,50.76301956176758,0.2740640342235565,15.518482208251953,19.224523544311523,1.7944999933242798,,,17.618791580200195,963.5900268554688,1595.9100341796875
+2003-05-07,,,,,,,,,,
+2003-06-01,47.655845642089844,0.29101142287254333,16.16796875,18.626508712768555,1.815999984741211,,,15.997578620910645,974.5,1622.800048828125
+2003-07-01,46.933773040771484,0.32185351848602295,16.653514862060547,18.995861053466797,2.0820000171661377,,,16.333431243896484,990.3099975585938,1735.02001953125
+2003-08-01,47.37279510498047,0.34521347284317017,16.722881317138672,18.960683822631836,2.315999984741211,,,19.37755012512207,1008.010009765625,1810.449951171875
+2003-08-06,,,,,,,,,,
+2003-09-01,51.1259651184082,0.3163566291332245,17.530014038085938,18.046072006225586,2.4214999675750732,,,19.657007217407227,995.969970703125,1786.93994140625
+2003-10-01,51.79159927368164,0.3494885563850403,16.48325538635254,18.468202590942383,2.7214999198913574,,,21.84463119506836,1050.7099609375,1932.2099609375
+2003-11-01,52.405128479003906,0.3192576766014099,16.303056716918945,21.423110961914062,2.698499917984009,,,20.621614456176758,1058.199951171875,1960.260009765625
+2003-11-06,,,,,,,,,,
+2003-12-01,53.74093246459961,0.32628077268600464,17.35569190979004,24.272485733032227,2.63100004196167,,,19.5084171295166,1111.9200439453125,2003.3699951171875
+2004-01-01,57.53900146484375,0.3444499373435974,17.533241271972656,25.74994659423828,2.5199999809265137,,,19.119047164916992,1131.1300048828125,2066.14990234375
+2004-02-01,55.95598602294922,0.36521488428115845,16.82303810119629,24.87051010131836,2.1505000591278076,,,18.600963592529297,1144.93994140625,2029.8199462890625
+2004-02-06,,,,,,,,,,
+2004-03-01,53.3401985168457,0.4128514230251312,15.808448791503906,25.6268310546875,2.1640000343322754,,,19.62464141845703,1126.2099609375,1994.219970703125
+2004-04-01,51.208683013916016,0.39361342787742615,16.56939125061035,23.6217041015625,2.180000066757202,,,20.729951858520508,1107.300048828125,1920.1500244140625
+2004-05-01,51.45262145996094,0.4284247159957886,16.632802963256836,23.815183639526367,2.424999952316284,,,22.293439865112305,1120.6800537109375,1986.739990234375
+2004-05-06,,,,,,,,,,
+2004-06-01,51.300846099853516,0.4968261420726776,18.110280990600586,25.503700256347656,2.7200000286102295,,,23.227535247802734,1140.8399658203125,2047.7900390625
+2004-07-01,50.672325134277344,0.4937727451324463,18.065902709960938,24.378023147583008,1.9459999799728394,,,21.075881958007812,1101.719970703125,1887.3599853515625
+2004-08-01,49.28722381591797,0.5265995860099792,17.31130027770996,23.6217041015625,1.906999945640564,,,22.91964340209961,1104.239990234375,1838.0999755859375
+2004-08-06,,,,,,,,,,
+2004-09-01,50.00397872924805,0.5916416049003601,17.5849609375,24.764976501464844,2.0429999828338623,,64.8648681640625,24.718441009521484,1114.5799560546875,1896.8399658203125
+2004-10-01,52.34260559082031,0.800052285194397,17.788476943969727,25.978591918945312,1.7065000534057617,,95.41541290283203,28.003637313842773,1130.199951171875,1974.989990234375
+2004-11-01,54.96117401123047,1.0237311124801636,17.050731658935547,26.945974349975586,1.9839999675750732,,91.0810775756836,30.26772117614746,1173.8199462890625,2096.81005859375
+2004-11-08,,,,,,,,,,
+2004-12-01,57.60344696044922,0.9832708239555359,18.939943313598633,29.918479919433594,2.2144999504089355,,96.49149322509766,31.357276916503906,1211.9200439453125,2175.43994140625
+2005-01-01,54.58831024169922,1.1741228103637695,18.628063201904297,27.930959701538086,2.1610000133514404,,97.90790557861328,28.444419860839844,1181.27001953125,2062.409912109375
+2005-02-01,54.09749221801758,1.3698610067367554,17.83417510986328,27.438472747802734,1.7589999437332153,,94.0890884399414,30.86894416809082,1203.5999755859375,2051.719970703125
+2005-02-08,,,,,,,,,,
+2005-03-01,53.49815368652344,1.2724499702453613,17.18527603149414,26.6469669342041,1.7135000228881836,,90.34534454345703,33.57841110229492,1180.5899658203125,1999.22998046875
+2005-04-01,44.7164421081543,1.1011403799057007,17.988739013671875,23.305103302001953,1.6180000305175781,,110.110107421875,29.735000610351562,1156.8499755859375,1921.6500244140625
+2005-05-01,44.23051071166992,1.2141252756118774,18.3442440032959,23.867952346801758,1.7755000591278076,,138.77377319335938,33.119998931884766,1191.5,2068.219970703125
+2005-05-06,,,,,,,,,,
+2005-06-01,43.555538177490234,1.124043345451355,17.71768569946289,24.254899978637695,1.6545000076293945,,147.22222900390625,28.610000610351562,1191.3299560546875,2056.9599609375
+2005-07-01,48.991188049316406,1.3023751974105835,18.26690673828125,23.234752655029297,2.257499933242798,,144.02401733398438,29.639999389648438,1234.1800537109375,2184.830078125
+2005-08-01,47.3240966796875,1.431849479675293,19.529403686523438,23.58652687072754,2.134999990463257,,143.1431427001953,27.040000915527344,1220.3299560546875,2152.090087890625
+2005-08-08,,,,,,,,,,
+2005-09-01,47.202552795410156,1.6370543241500854,18.40694236755371,24.00865936279297,2.265000104904175,,158.3883819580078,29.850000381469727,1228.81005859375,2151.68994140625
+2005-10-01,48.1793098449707,1.7585887908935547,18.385473251342773,23.867952346801758,1.9930000305175781,,186.25625610351562,32.25,1207.010009765625,2120.300048828125
+2005-11-01,52.309974670410156,2.0709757804870605,19.80194664001465,24.976051330566406,2.4230000972747803,,202.65765380859375,32.61000061035156,1249.47998046875,2232.820068359375
+2005-11-08,,,,,,,,,,
+2005-12-01,48.483585357666016,2.1952593326568604,18.762245178222656,25.76755142211914,2.3575000762939453,,207.63763427734375,36.959999084472656,1248.2900390625,2205.320068359375
+2006-01-01,47.95273208618164,2.305800437927246,20.19721794128418,25.169517517089844,2.240999937057495,,216.5465545654297,39.72999954223633,1280.0799560546875,2305.820068359375
+2006-02-01,47.32752227783203,2.0914342403411865,19.27882957458496,26.207246780395508,1.871999979019165,,181.49148559570312,38.54999923706055,1280.6600341796875,2281.389892578125
+2006-02-08,,,,,,,,,,
+2006-03-01,48.76497268676758,1.9152405261993408,19.588937759399414,26.734920501708984,1.8265000581741333,,195.1951904296875,34.95000076293945,1294.8699951171875,2339.7900390625
+2006-04-01,48.6880989074707,2.149454355239868,17.385986328125,24.694625854492188,1.7604999542236328,,209.17918395996094,39.20000076293945,1310.6099853515625,2322.570068359375
+2006-05-01,47.24531936645508,1.8251585960388184,16.306108474731445,24.149375915527344,1.7304999828338623,,186.09609985351562,28.6299991607666,1270.0899658203125,2178.8798828125
+2006-05-08,,,,,,,,,,
+2006-06-01,45.58832931518555,1.7488168478012085,16.83946990966797,24.465961456298828,1.934000015258789,,209.8748779296875,30.360000610351562,1270.199951171875,2172.090087890625
+2006-07-01,45.938453674316406,2.075251340866089,17.388734817504883,24.78256607055664,1.344499945640564,,193.49349975585938,28.510000228881836,1276.6600341796875,2091.469970703125
+2006-08-01,48.051090240478516,2.0718915462493896,18.574007034301758,26.048952102661133,1.5414999723434448,,189.45445251464844,32.439998626708984,1303.8199462890625,2183.75
+2006-08-08,,,,,,,,,,
+2006-09-01,48.820682525634766,2.350689172744751,19.839282989501953,27.368104934692383,1.6059999465942383,,201.15115356445312,37.459999084472656,1335.8499755859375,2258.429931640625
+2006-10-01,55.01115798950195,2.475886821746826,20.82581329345703,29.90088653564453,1.9045000076293945,,238.4334259033203,38.25,1377.93994140625,2366.7099609375
+2006-11-01,54.766883850097656,2.798962354660034,21.29731559753418,29.021446228027344,2.0169999599456787,,242.64764404296875,40.15999984741211,1400.6300048828125,2431.77001953125
+2006-11-08,,,,,,,,,,
+2006-12-01,58.07079315185547,2.5907039642333984,21.73406219482422,29.812957763671875,1.9730000495910645,,230.47047424316406,41.119998931884766,1418.300048828125,2415.2900390625
+2007-01-01,59.266300201416016,2.617882013320923,22.461923599243164,30.252676010131836,1.8834999799728394,,251.00100708007812,38.869998931884766,1438.239990234375,2463.929931640625
+2007-02-01,55.55428695678711,2.583681344985962,20.50397491455078,30.37578582763672,1.9570000171661377,,224.949951171875,39.25,1406.8199462890625,2416.14990234375
+2007-02-07,,,,,,,,,,
+2007-03-01,56.51309585571289,2.8371317386627197,20.3559513092041,29.707414627075195,1.9895000457763672,,229.30931091308594,41.70000076293945,1420.8599853515625,2421.639892578125
+2007-04-01,61.27952575683594,3.0475287437438965,21.867843627929688,32.539215087890625,3.066499948501587,,235.92591857910156,41.560001373291016,1482.3699951171875,2525.090087890625
+2007-05-01,63.91150665283203,3.700700044631958,22.415639877319336,33.18999099731445,3.4570000171661377,,249.20420837402344,44.060001373291016,1530.6199951171875,2604.52001953125
+2007-05-08,,,,,,,,,,
+2007-06-01,63.347747802734375,3.7266552448272705,21.59428596496582,32.5040397644043,3.4205000400543213,,261.6116027832031,40.150001525878906,1503.3499755859375,2603.22998046875
+2007-07-01,66.59786987304688,4.023471355438232,21.242568969726562,30.70998764038086,3.927000045776367,,255.2552490234375,40.290000915527344,1455.27001953125,2546.27001953125
+2007-08-01,70.23324584960938,4.228675365447998,21.052053451538086,30.12954330444336,3.995500087738037,,257.88287353515625,42.75,1473.989990234375,2596.360107421875
+2007-08-08,,,,,,,,,,
+2007-09-01,71.1520004272461,4.68641471862793,21.662628173828125,30.49891471862793,4.65749979019165,,283.9189147949219,43.65999984741211,1526.75,2701.5
+2007-10-01,70.13726806640625,5.800380706787109,27.067256927490234,30.674802780151367,4.457499980926514,,353.8538513183594,47.900001525878906,1549.3800048828125,2859.1201171875
+2007-11-01,63.529457092285156,5.564334392547607,24.70686912536621,29.6898193359375,4.5279998779296875,,346.8468322753906,42.13999938964844,1481.1400146484375,2660.9599609375
+2007-11-07,,,,,,,,,,
+2007-12-01,65.52474212646484,6.048642158508301,26.26405906677246,28.4762020111084,4.631999969482422,,346.0860900878906,42.72999954223633,1468.3599853515625,2652.280029296875
+2008-01-01,64.92465209960938,4.133399963378906,24.050800323486328,27.21040916442871,3.884999990463257,,282.43243408203125,34.93000030517578,1378.550048828125,2389.860107421875
+2008-02-01,69.0161361694336,3.8176541328430176,20.066930770874023,25.923078536987305,3.2235000133514404,,235.82582092285156,33.650001525878906,1330.6300048828125,2271.47998046875
+2008-02-06,,,,,,,,,,
+2008-03-01,70.05885314941406,4.381966590881348,21.01883316040039,26.399211883544922,3.565000057220459,,220.45545959472656,35.59000015258789,1322.699951171875,2279.10009765625
+2008-04-01,73.44194030761719,5.311798572540283,21.122520446777344,24.706567764282227,3.93149995803833,,287.43243408203125,37.290000915527344,1385.5899658203125,2412.800048828125
+2008-05-01,78.75386810302734,5.763737201690674,20.974388122558594,24.016834259033203,4.080999851226807,,293.1932067871094,44.060001373291016,1400.3800048828125,2522.659912109375
+2008-05-07,,,,,,,,,,
+2008-06-01,72.41636657714844,5.11300802230835,20.449499130249023,23.981456756591797,3.6665000915527344,,263.4734802246094,39.38999938964844,1280.0,2292.97998046875
+2008-07-01,78.18988800048828,4.853753566741943,19.118900299072266,24.197914123535156,3.816999912261963,,237.1121063232422,41.349998474121094,1267.3800048828125,2325.550048828125
+2008-08-01,74.37141418457031,5.176827907562256,20.285959243774414,24.712379455566406,4.040500164031982,,231.8768768310547,42.83000183105469,1282.8299560546875,2367.52001953125
+2008-08-06,,,,,,,,,,
+2008-09-01,71.73551177978516,3.470762014389038,19.91908073425293,20.454687118530273,3.638000011444092,,200.46046447753906,39.470001220703125,1166.3599853515625,2091.8798828125
+2008-10-01,57.02163314819336,3.2854056358337402,16.66515350341797,14.2785062789917,2.861999988555908,,179.85986328125,26.639999389648438,968.75,1720.949951171875
+2008-11-01,50.04803466796875,2.8298041820526123,15.090431213378906,12.444729804992676,2.134999990463257,,146.6266326904297,23.15999984741211,896.239990234375,1535.5699462890625
+2008-11-06,,,,,,,,,,
+2008-12-01,51.90673828125,2.6062774658203125,14.606595039367676,14.189484596252441,2.563999891281128,,153.97897338867188,21.290000915527344,903.25,1577.030029296875
+2009-01-01,56.52627944946289,2.7522425651550293,12.848398208618164,11.888165473937988,2.940999984741211,,169.43443298339844,19.309999465942383,825.8800048828125,1476.4200439453125
+2009-02-01,56.76066589355469,2.7272019386291504,12.13459587097168,9.274202346801758,3.239500045776367,,169.16416931152344,16.700000762939453,735.0900268554688,1377.8399658203125
+2009-02-06,,,,,,,,,,
+2009-03-01,60.08320999145508,3.209982395172119,13.897279739379883,8.146258354187012,3.671999931335449,,174.20420837402344,21.389999389648438,797.8699951171875,1528.5899658203125
+2009-04-01,64.00231170654297,3.8423893451690674,15.3270902633667,11.028571128845215,4.026000022888184,,198.1831817626953,27.350000381469727,872.8099975585938,1717.300048828125
+2009-05-01,65.90607452392578,4.147140979766846,15.803704261779785,12.274019241333008,3.8994998931884766,,208.82382202148438,28.18000030517578,919.1400146484375,1774.3299560546875
+2009-05-06,,,,,,,,,,
+2009-06-01,65.09091186523438,4.349293231964111,18.0966796875,11.69642448425293,4.183000087738037,,211.00601196289062,28.299999237060547,919.3200073242188,1835.0400390625
+2009-07-01,73.51245880126953,4.989336013793945,17.906352996826172,14.879191398620605,4.288000106811523,,221.7467498779297,32.41999816894531,987.47998046875,1978.5
+2009-08-01,73.58724975585938,5.1365203857421875,18.766645431518555,15.714898109436035,4.059500217437744,,231.06607055664062,31.420000076293945,1020.6199951171875,2009.06005859375
+2009-08-06,,,,,,,,,,
+2009-09-01,74.90744018554688,5.659914016723633,19.69136619567871,14.061647415161133,4.668000221252441,,248.1731719970703,33.040000915527344,1057.0799560546875,2122.419921875
+2009-10-01,75.53370666503906,5.756102561950684,21.230228424072266,13.72740650177002,5.940499782562256,,268.3283386230469,32.939998626708984,1036.18994140625,2045.1099853515625
+2009-11-01,79.12847900390625,6.1045241355896,22.51645278930664,14.055986404418945,6.795499801635742,,291.7917785644531,35.08000183105469,1095.6300048828125,2144.60009765625
+2009-11-06,,,,,,,,,,
+2009-12-01,82.34589385986328,6.434925556182861,23.438796997070312,15.443329811096191,6.72599983215332,,310.30029296875,36.779998779296875,1115.0999755859375,2269.14990234375
+2010-01-01,76.99246215820312,5.86481237411499,21.670120239257812,15.999075889587402,6.270500183105469,,265.2352294921875,32.29999923706055,1073.8699951171875,2147.35009765625
+2010-02-01,79.99315643310547,6.248349666595459,22.04693031311035,17.19167137145996,5.920000076293945,,263.6636657714844,34.650001525878906,1104.489990234375,2238.260009765625
+2010-02-08,,,,,,,,,,
+2010-03-01,81.0396728515625,7.176041126251221,22.629026412963867,17.88887596130371,6.78849983215332,,283.8438415527344,35.369998931884766,1169.4300537109375,2397.9599609375
+2010-04-01,81.51359558105469,7.972740650177002,23.594758987426758,20.0878963470459,6.855000019073486,,263.11309814453125,33.599998474121094,1186.68994140625,2461.18994140625
+2010-05-01,79.1503677368164,7.84417724609375,19.932706832885742,17.157638549804688,6.2729997634887695,,243.0580596923828,32.08000183105469,1089.4100341796875,2257.0400390625
+2010-05-06,,,,,,,,,,
+2010-06-01,78.42550659179688,7.680809020996094,17.857412338256836,14.817129135131836,5.4629998207092285,,222.69769287109375,26.43000030517578,1030.7099609375,2109.239990234375
+2010-07-01,81.55033874511719,7.855478763580322,20.03040885925293,18.038850784301758,5.894499778747559,,242.66766357421875,28.719999313354492,1101.5999755859375,2254.699951171875
+2010-08-01,78.20323944091797,7.4233856201171875,18.214401245117188,15.64971923828125,6.241499900817871,,225.2352294921875,27.700000762939453,1049.3299560546875,2114.030029296875
+2010-08-06,,,,,,,,,,
+2010-09-01,85.6181411743164,8.664690971374512,19.10737419128418,19.168588638305664,7.853000164031982,,263.1581726074219,26.149999618530273,1141.199951171875,2368.6201171875
+2010-10-01,91.65619659423828,9.190831184387207,20.808244705200195,21.757949829101562,8.261500358581543,,307.15716552734375,28.149999618530273,1183.260009765625,2507.409912109375
+2010-11-01,90.290283203125,9.501388549804688,19.70814323425293,21.311634063720703,8.770000457763672,,278.1331481933594,27.799999237060547,1180.550048828125,2498.22998046875
+2010-11-08,,,,,,,,,,
+2010-12-01,94.08943176269531,9.84980583190918,21.909496307373047,21.423206329345703,9.0,,297.28228759765625,30.780000686645508,1257.6400146484375,2652.8701171875
+2011-01-01,103.8599853515625,10.361597061157227,21.76820182800293,19.823131561279297,8.482000350952148,,300.48046875,33.04999923706055,1286.1199951171875,2700.080078125
+2011-02-01,103.78302001953125,10.785745620727539,20.865453720092773,20.065792083740234,8.66450023651123,,307.00701904296875,34.5,1327.219970703125,2782.27001953125
+2011-02-08,,,,,,,,,,
+2011-03-01,104.95984649658203,10.64222526550293,20.04909324645996,19.879127502441406,9.006500244140625,,293.6736755371094,33.15999984741211,1325.8299560546875,2781.070068359375
+2011-04-01,109.79368591308594,10.691693305969238,20.467607498168945,18.910266876220703,9.790499687194824,,272.32232666015625,33.54999923706055,1363.6099853515625,2873.5400390625
+2011-05-01,108.73165130615234,10.621461868286133,19.7490291595459,19.135168075561523,9.834500312805176,,264.7747802734375,34.630001068115234,1345.199951171875,2835.300048828125
+2011-05-06,,,,,,,,,,
+2011-06-01,110.91179656982422,10.25013542175293,20.665348052978516,19.510000228881836,10.224499702453613,,253.4434356689453,31.450000762939453,1320.6400146484375,2773.52001953125
+2011-07-01,117.571044921875,11.923834800720215,21.778095245361328,17.562105178833008,11.12600040435791,,302.14715576171875,27.709999084472656,1292.280029296875,2756.3798828125
+2011-08-01,111.14454650878906,11.75130844116211,21.142244338989258,15.623312950134277,10.761500358581543,,270.7507629394531,25.239999771118164,1218.8900146484375,2579.4599609375
+2011-08-08,,,,,,,,,,
+2011-09-01,113.55061340332031,11.644124984741211,19.90796661376953,13.119815826416016,10.81149959564209,,257.77777099609375,24.170000076293945,1131.4200439453125,2415.39990234375
+2011-10-01,119.88819122314453,12.360504150390625,21.299680709838867,15.485393524169922,10.67549991607666,,296.6166076660156,29.40999984741211,1253.300048828125,2684.409912109375
+2011-11-01,122.07645416259766,11.670994758605957,20.459848403930664,15.42860221862793,9.614500045776367,,299.9949951171875,27.420000076293945,1246.9599609375,2620.340087890625
+2011-11-08,,,,,,,,,,
+2011-12-01,119.88117218017578,12.367225646972656,20.92014503479004,15.068914413452148,8.654999732971191,,323.2732849121094,28.270000457763672,1257.5999755859375,2605.14990234375
+2012-01-01,125.56623077392578,13.939234733581543,23.79706382751465,14.749091148376465,9.722000122070312,,290.3453369140625,30.950000762939453,1312.4100341796875,2813.840087890625
+2012-02-01,128.25875854492188,16.564136505126953,25.57801628112793,15.662582397460938,8.98449993133545,,309.4344482421875,32.88999938964844,1365.6800537109375,2966.889892578125
+2012-02-08,,,,,,,,,,
+2012-03-01,136.5597381591797,18.308074951171875,26.1682071685791,15.377117156982422,10.125499725341797,,320.9409484863281,34.310001373291016,1408.469970703125,3091.570068359375
+2012-04-01,135.53221130371094,17.83262062072754,25.97353172302246,14.882647514343262,11.595000267028809,,302.72772216796875,33.54999923706055,1397.9100341796875,3046.360107421875
+2012-05-01,126.25150299072266,17.64176368713379,23.677928924560547,13.811394691467285,10.645500183105469,,290.7207336425781,31.049999237060547,1310.3299560546875,2827.340087890625
+2012-05-08,,,,,,,,,,
+2012-06-01,128.5418243408203,17.83323097229004,24.976381301879883,15.054808616638184,11.417499542236328,,290.3253173828125,32.369998931884766,1362.1600341796875,2935.050048828125
+2012-07-01,128.80467224121094,18.650381088256836,24.06191635131836,13.332178115844727,11.664999961853027,,316.8017883300781,30.8799991607666,1379.3199462890625,2939.52001953125
+2012-08-01,128.06199645996094,20.314008712768555,25.1641845703125,14.178669929504395,12.41349983215332,,342.88787841796875,31.270000457763672,1406.5799560546875,3066.9599609375
+2012-08-08,,,,,,,,,,
+2012-09-01,136.92532348632812,20.458269119262695,24.459667205810547,14.120949745178223,12.715999603271484,,377.62762451171875,32.439998626708984,1440.6700439453125,3116.22998046875
+2012-10-01,128.39756774902344,18.256948471069336,23.456958770751953,12.4627103805542,11.644499778747559,,340.490478515625,34.029998779296875,1412.1600341796875,2977.22998046875
+2012-11-01,125.45381927490234,17.94904899597168,21.878910064697266,13.178736686706543,12.602499961853027,,349.5345458984375,34.61000061035156,1416.1800537109375,3010.239990234375
+2012-11-07,,,,,,,,,,
+2012-12-01,126.98394775390625,16.39484405517578,22.13326644897461,13.19808578491211,12.543499946594238,,354.0440368652344,37.68000030517578,1426.18994140625,3019.510009765625
+2013-01-01,134.6208953857422,14.03252124786377,22.746475219726562,15.598185539245605,13.274999618530273,,378.2232360839844,37.83000183105469,1498.1099853515625,3142.1298828125
+2013-02-01,133.1359405517578,13.598445892333984,23.036500930786133,15.79291820526123,13.213500022888184,,401.0010070800781,39.310001373291016,1514.6800537109375,3160.18994140625
+2013-02-06,,,,,,,,,,
+2013-03-01,141.99786376953125,13.716739654541016,23.903995513916016,16.74711036682129,13.32450008392334,,397.49249267578125,43.52000045776367,1569.18994140625,3267.52001953125
+2013-04-01,134.8347625732422,13.720457077026367,27.655447006225586,16.822824478149414,12.690500259399414,,412.69769287109375,45.08000183105469,1597.5699462890625,3328.7900390625
+2013-05-01,138.48284912109375,13.935823440551758,29.15936851501465,17.234567642211914,13.460000038146973,,436.0460510253906,42.90999984741211,1630.739990234375,3455.909912109375
+2013-05-08,,,,,,,,,,
+2013-06-01,127.82191467285156,12.368638038635254,29.060949325561523,17.783567428588867,13.884499549865723,,440.6256408691406,45.560001373291016,1606.280029296875,3403.25
+2013-07-01,130.45040893554688,14.115401268005371,26.789243698120117,19.142587661743164,15.060999870300293,,444.3193054199219,47.279998779296875,1685.72998046875,3626.3701171875
+2013-08-01,121.90938568115234,15.19745922088623,28.101774215698242,19.695158004760742,14.048999786376953,,423.8738708496094,45.75,1632.969970703125,3589.8701171875
+2013-08-07,,,,,,,,,,
+2013-09-01,124.47478485107422,14.96906566619873,28.19812774658203,20.306934356689453,15.631999969482422,,438.3934020996094,51.939998626708984,1681.550048828125,3771.47998046875
+2013-10-01,120.46192169189453,16.41180992126465,30.002870559692383,19.72601890563965,18.201499938964844,,515.8057861328125,54.220001220703125,1756.5400390625,3919.7099609375
+2013-11-01,120.77778625488281,17.45956802368164,32.30753707885742,22.58371353149414,19.680999755859375,,530.3253173828125,56.779998779296875,1805.81005859375,4059.889892578125
+2013-11-06,,,,,,,,,,
+2013-12-01,126.7584228515625,17.71782684326172,31.937856674194336,24.151473999023438,19.93950080871582,,560.9158935546875,59.880001068115234,1848.3599853515625,4176.58984375
+2014-01-01,119.39906311035156,15.80967903137207,32.304969787597656,21.634523391723633,17.934499740600586,,591.0760498046875,59.189998626708984,1782.5899658203125,4103.8798828125
+2014-02-01,125.13655090332031,16.61942481994629,32.70622634887695,21.913677215576172,18.104999542236328,,608.4334106445312,68.62999725341797,1859.449951171875,4308.1201171875
+2014-02-06,,,,,,,,,,
+2014-03-01,130.79644775390625,17.052499771118164,35.256614685058594,22.53180694580078,16.818500518798828,,557.8128051757812,65.73999786376953,1872.3399658203125,4198.990234375
+2014-04-01,133.50091552734375,18.747455596923828,34.7491340637207,24.246538162231445,15.206500053405762,,534.8800048828125,61.689998626708984,1883.949951171875,4114.56005859375
+2014-05-01,125.27215576171875,20.11072540283203,35.21359634399414,24.767969131469727,15.6274995803833,,571.6500244140625,64.54000091552734,1923.5699462890625,4242.6201171875
+2014-05-07,,,,,,,,,,
+2014-06-01,123.88963317871094,20.782461166381836,36.12032699584961,24.94846534729004,16.23900032043457,,584.6699829101562,72.36000061035156,1960.22998046875,4408.18017578125
+2014-07-01,130.99761962890625,21.37957000732422,37.38497543334961,26.72607421875,15.649499893188477,,579.5499877929688,69.25,1930.6700439453125,4369.77001953125
+2014-08-01,131.4281463623047,22.922651290893555,39.35124206542969,27.834640502929688,16.95199966430664,,582.3599853515625,71.9000015258789,2003.3699951171875,4580.27001953125
+2014-08-06,,,,,,,,,,
+2014-09-01,130.50729370117188,22.643360137939453,40.40761947631836,26.66560935974121,16.121999740600586,,588.4099731445312,69.19000244140625,1972.2900390625,4493.39013671875
+2014-10-01,113.0242691040039,24.27278709411621,40.92185974121094,26.89417266845703,15.27299976348877,,567.8699951171875,70.12000274658203,2018.050048828125,4630.740234375
+2014-11-01,111.49117279052734,26.729284286499023,41.67144012451172,28.27128028869629,16.93199920654297,,549.0800170898438,73.68000030517578,2067.56005859375,4791.6298828125
+2014-11-06,,,,,,,,,,
+2014-12-01,111.05672454833984,24.915250778198242,40.74142074584961,28.068769454956055,15.517499923706055,,530.6599731445312,72.69999694824219,2058.89990234375,4736.0498046875
+2015-01-01,106.12132263183594,26.445661544799805,35.434940338134766,26.79075813293457,17.726499557495117,,537.5499877929688,70.12999725341797,1994.989990234375,4635.240234375
+2015-02-01,112.09505462646484,28.996322631835938,38.460933685302734,27.767194747924805,19.007999420166016,,562.6300048828125,79.0999984741211,2104.5,4963.52978515625
+2015-02-06,,,,,,,,,,
+2015-03-01,111.87759399414062,28.197509765625,35.91679000854492,26.139816284179688,18.604999542236328,,554.7000122070312,73.94000244140625,2067.889892578125,4900.8798828125
+2015-04-01,119.3988265991211,28.360668182373047,42.96587371826172,23.521541595458984,21.089000701904297,,548.77001953125,76.05999755859375,2085.510009765625,4941.419921875
+2015-05-01,118.25563049316406,29.523195266723633,41.39352035522461,23.3579158782959,21.46150016784668,,545.3200073242188,79.08999633789062,2107.389892578125,5070.02978515625
+2015-05-06,,,,,,,,,,
+2015-06-01,114.24130249023438,28.542850494384766,39.253108978271484,21.762542724609375,21.704500198364258,,540.0399780273438,81.01000213623047,2063.110107421875,4986.8701171875
+2015-07-01,113.77072143554688,27.60302734375,41.520286560058594,22.683992385864258,26.8075008392334,,657.5,81.98999786376953,2103.840087890625,5128.27978515625
+2015-08-01,103.86784362792969,25.65966796875,38.692989349365234,20.93431854248047,25.644500732421875,,647.8200073242188,78.56999969482422,1972.1800537109375,4776.509765625
+2015-08-06,,,,,,,,,,
+2015-09-01,102.66226959228516,25.21347999572754,39.61040496826172,20.028608322143555,25.594499588012695,,638.3699951171875,82.22000122070312,1920.030029296875,4620.16015625
+2015-10-01,99.1993637084961,27.31651496887207,47.11008071899414,19.46390151977539,31.295000076293945,,737.3900146484375,88.66000366210938,2079.360107421875,5053.75
+2015-11-01,98.7319564819336,27.042200088500977,48.64043045043945,21.868392944335938,33.2400016784668,,762.8499755859375,91.45999908447266,2080.409912109375,5108.669921875
+2015-11-06,,,,,,,,,,
+2015-12-01,98.37144470214844,24.16438102722168,49.98638916015625,22.03421974182129,33.794498443603516,,778.010009765625,93.94000244140625,2043.93994140625,5007.41015625
+2016-01-01,89.20050811767578,22.3461971282959,49.63501739501953,20.343839645385742,29.350000381469727,,761.3499755859375,89.12999725341797,1940.239990234375,4613.9501953125
+2016-02-01,93.6608657836914,22.196985244750977,45.841888427734375,20.051719665527344,27.625999450683594,,717.219970703125,85.1500015258789,1932.22998046875,4557.9501953125
+2016-02-08,,,,,,,,,,
+2016-03-01,109.36297607421875,25.15644073486328,50.11842727661133,23.285871505737305,29.68199920654297,,762.9000244140625,93.80000305175781,2059.739990234375,4869.85009765625
+2016-04-01,105.3841552734375,21.636524200439453,45.25450134277344,20.178363800048828,32.97949981689453,,707.8800048828125,94.22000122070312,2065.300048828125,4775.35986328125
+2016-05-01,111.01663208007812,23.049110412597656,48.094825744628906,20.956079483032227,36.13949966430664,,748.8499755859375,99.47000122070312,2096.949951171875,4948.0498046875
+2016-05-06,,,,,,,,,,
+2016-06-01,110.65898895263672,22.20018196105957,46.75897216796875,19.947153091430664,35.78099822998047,,703.530029296875,95.79000091552734,2098.860107421875,4842.669921875
+2016-07-01,117.10401916503906,24.199600219726562,51.79398727416992,21.839828491210938,37.94049835205078,,791.3400268554688,97.86000061035156,2173.60009765625,5162.1298828125
+2016-08-01,115.83541107177734,24.638490676879883,52.506752014160156,20.885658264160156,38.45800018310547,,789.8499755859375,102.30999755859375,2170.949951171875,5213.22021484375
+2016-08-08,,,,,,,,,,
+2016-09-01,116.81381225585938,26.394628524780273,52.96271896362305,21.479366302490234,41.865501403808594,13.321450233459473,804.0599975585938,108.54000091552734,2168.27001953125,5312.0
+2016-10-01,113.019287109375,26.509033203125,55.095947265625,20.878395080566406,39.49100112915039,13.680961608886719,809.9000244140625,107.51000213623047,2126.14990234375,5189.14013671875
+2016-11-01,119.29200744628906,25.803936004638672,55.40858840942383,19.980859756469727,37.528499603271484,14.926712036132812,775.8800048828125,102.80999755859375,2198.81005859375,5323.68017578125
+2016-11-08,,,,,,,,,,
+2016-12-01,123.1717300415039,27.180198669433594,57.52321243286133,18.655929565429688,37.493499755859375,15.31966781616211,792.4500122070312,102.94999694824219,2238.830078125,5383.1201171875
+2017-01-01,129.5013427734375,28.47795867919922,59.84672927856445,22.670724868774414,41.17399978637695,17.554771423339844,820.1900024414062,113.37999725341797,2278.8701171875,5614.7900390625
+2017-02-01,133.43417358398438,32.148292541503906,59.22650909423828,24.339130401611328,42.25199890136719,17.69411849975586,844.9299926757812,118.33999633789062,2363.639892578125,5825.43994140625
+2017-02-08,,,,,,,,,,
+2017-03-01,130.24111938476562,33.85976028442383,61.33644485473633,24.011991500854492,44.32699966430664,17.858545303344727,847.7999877929688,130.1300048828125,2362.719970703125,5911.740234375
+2017-04-01,119.88253784179688,33.85739517211914,63.75787353515625,23.72492027282715,46.2495002746582,18.70298194885254,924.52001953125,133.74000549316406,2384.199951171875,6047.60986328125
+2017-05-01,114.1535415649414,36.00456237792969,65.0430908203125,23.328956604003906,49.73099899291992,19.338397979736328,987.0900268554688,141.86000061035156,2411.800048828125,6198.52001953125
+2017-05-08,,,,,,,,,,
+2017-06-01,116.17494201660156,34.084716796875,64.56354522705078,23.700172424316406,48.400001525878906,17.030832290649414,929.6799926757812,141.44000244140625,2423.409912109375,6140.419921875
+2017-07-01,109.25714874267578,35.19940948486328,68.0947265625,25.520694732666016,49.388999938964844,17.911497116088867,945.5,146.49000549316406,2470.300048828125,6348.1201171875
+2017-08-01,108.01860809326172,38.81330490112305,70.03360748291016,26.852062225341797,49.029998779296875,20.882347106933594,955.239990234375,155.16000366210938,2471.64990234375,6428.66015625
+2017-08-08,,,,,,,,,,
+2017-09-01,110.72441864013672,36.6182861328125,70.14307403564453,27.700807571411133,48.067501068115234,21.517763137817383,973.719970703125,149.17999267578125,2519.360107421875,6495.9599609375
+2017-10-01,117.57792663574219,40.163211822509766,78.32596588134766,25.408233642578125,55.263999938964844,23.067289352416992,1033.0400390625,175.16000366210938,2575.260009765625,6727.669921875
+2017-11-01,117.50923919677734,40.83085632324219,79.2582015991211,24.86334991455078,58.837501525878906,21.80481719970703,1036.1700439453125,181.47000122070312,2647.580078125,6873.97021484375
+2017-11-09,,,,,,,,,,
+2017-12-01,118.25981140136719,40.3528938293457,80.95279693603516,24.43583106994629,58.4734992980957,22.65203857421875,1053.4000244140625,175.24000549316406,2673.610107421875,6903.39013671875
+2018-01-01,126.18390655517578,39.9236946105957,89.91493225097656,28.855159759521484,72.54450225830078,19.982173919677734,1182.219970703125,199.75999450683594,2823.81005859375,7411.47998046875
+2018-02-01,120.11751556396484,42.472713470458984,88.74141693115234,25.634004592895508,75.62249755859375,20.7039852142334,1103.9200439453125,209.1300048828125,2713.830078125,7273.009765625
+2018-02-08,,,,,,,,,,
+2018-03-01,119.43197631835938,40.170265197753906,86.7812271118164,24.33201026916504,72.36699676513672,20.402997970581055,1037.1400146484375,216.0800018310547,2640.8701171875,7063.4501953125
+2018-04-01,112.83880615234375,39.566917419433594,88.92057037353516,26.82056999206543,78.30650329589844,20.00168228149414,1018.5800170898438,221.60000610351562,2648.050048828125,7066.27001953125
+2018-05-01,109.99760437011719,44.7408332824707,93.97891998291016,23.179109573364258,81.48100280761719,22.479249954223633,1100.0,249.27999877929688,2705.27001953125,7442.1201171875
+2018-05-09,,,,,,,,,,
+2018-06-01,109.95153045654297,44.4903450012207,94.16664123535156,20.467208862304688,84.98999786376953,23.571720123291016,1129.18994140625,243.80999755859375,2718.3701171875,7510.2998046875
+2018-07-01,114.06781768798828,45.73533630371094,101.30004119873047,22.375450134277344,88.87200164794922,25.784528732299805,1227.219970703125,244.67999267578125,2816.2900390625,7671.7900390625
+2018-08-01,115.2877197265625,54.709842681884766,107.2684097290039,24.00385284423828,100.635498046875,26.8017520904541,1231.800048828125,263.510009765625,2901.52001953125,8109.5400390625
+2018-08-09,,,,,,,,,,
+2018-09-01,120.2962646484375,54.445865631103516,109.63678741455078,23.24565315246582,100.1500015258789,27.066509246826172,1207.0799560546875,269.95001220703125,2913.97998046875,8046.35009765625
+2018-10-01,91.83122253417969,52.78649139404297,102.38966369628906,24.236047744750977,79.90049743652344,25.190916061401367,1090.5799560546875,245.75999450683594,2711.739990234375,7305.89990234375
+2018-11-01,98.86396026611328,43.07142639160156,106.3008041381836,23.4099178314209,84.50849914550781,29.396371841430664,1109.6500244140625,250.88999938964844,2760.169921875,7330.5400390625
+2018-11-08,,,,,,,,,,
+2018-12-01,91.58279418945312,38.17780685424805,97.78714752197266,17.18350601196289,75.09850311279297,24.59708595275879,1044.9599609375,226.24000549316406,2506.85009765625,6635.27978515625
+2019-01-01,108.30086517333984,40.28346252441406,100.5406265258789,24.84735679626465,85.9365005493164,24.456159591674805,1125.8900146484375,247.82000732421875,2704.10009765625,7281.740234375
+2019-02-01,111.28997039794922,41.90748596191406,107.85758972167969,27.216707229614258,81.99150085449219,28.095136642456055,1126.550048828125,262.5,2784.489990234375,7532.52978515625
+2019-02-07,,,,,,,,,,
+2019-03-01,115.00740814208984,46.17075729370117,114.03240966796875,28.167972564697266,89.0374984741211,29.539655685424805,1176.8900146484375,266.489990234375,2834.39990234375,7729.31982421875
+2019-04-01,114.33090209960938,48.77644348144531,126.27296447753906,29.61660385131836,96.32599639892578,33.9285774230957,1198.9599609375,289.25,2945.830078125,8095.39013671875
+2019-05-01,103.50668334960938,42.55390930175781,119.58222198486328,27.175188064575195,88.75350189208984,29.972509384155273,1106.5,270.8999938964844,2752.06005859375,7453.14990234375
+2019-05-09,,,,,,,,,,
+2019-06-01,113.73433685302734,48.293270111083984,130.00108337402344,31.43656349182129,94.68150329589844,25.56848907470703,1082.800048828125,294.6499938964844,2941.760009765625,8006.240234375
+2019-07-01,122.26231384277344,51.98261260986328,132.24281311035156,28.701255798339844,93.33899688720703,29.061506271362305,1218.199951171875,298.8599853515625,2980.3798828125,8175.419921875
+2019-08-01,111.7796401977539,50.93339920043945,133.78579711914062,25.92054557800293,88.81449890136719,25.9359073638916,1190.530029296875,284.510009765625,2926.4599609375,7962.8798828125
+2019-08-08,,,,,,,,,,
+2019-09-01,121.34967803955078,54.85721969604492,135.37051391601562,26.743131637573242,86.79550170898438,26.10200309753418,1221.1400146484375,276.25,2976.739990234375,7999.33984375
+2019-10-01,111.59463500976562,60.929054260253906,139.59625244140625,30.58913803100586,88.83300018310547,26.620418548583984,1258.800048828125,277.92999267578125,3037.56005859375,8292.3603515625
+2019-11-01,112.19547271728516,65.45783996582031,147.39544677734375,35.09682083129883,90.04000091552734,24.40582847595215,1304.0899658203125,309.5299987792969,3140.97998046875,8665.4697265625
+2019-11-07,,,,,,,,,,
+2019-12-01,113.17443084716797,72.13994598388672,154.07154846191406,33.23965072631836,92.39199829101562,25.86544418334961,1339.3900146484375,329.80999755859375,3230.780029296875,8972.599609375
+2020-01-01,121.35601806640625,76.03622436523438,166.31326293945312,32.28398132324219,100.43599700927734,24.546754837036133,1432.780029296875,351.1400146484375,3225.52001953125,9150.9404296875
+2020-02-01,109.88997650146484,67.1553726196289,158.28240966796875,29.225305557250977,94.1875,20.364192962646484,1339.25,345.1199951171875,2954.219970703125,8567.3701171875
+2020-02-07,,,,,,,,,,
+2020-03-01,94.6399154663086,62.61878204345703,154.502197265625,17.190288543701172,97.48600006103516,19.90617561340332,1161.949951171875,318.239990234375,2584.590087890625,7700.10009765625
+2020-04-01,107.12150573730469,72.34808349609375,175.5648956298828,16.6003360748291,123.69999694824219,21.486589431762695,1346.699951171875,353.6400146484375,2912.429931640625,8889.5498046875
+2020-05-01,106.55841827392578,78.29256439208984,179.52272033691406,14.412978172302246,122.11849975585938,24.98464012145996,1433.52001953125,386.6000061035156,3044.31005859375,9489.8701171875
+2020-05-07,,,,,,,,,,
+2020-06-01,104.41674041748047,90.0749740600586,199.92588806152344,13.877481460571289,137.9409942626953,27.652219772338867,1418.050048828125,435.30999755859375,3100.2900390625,10058.76953125
+2020-07-01,106.29290008544922,104.9491958618164,201.3994598388672,15.366937637329102,158.23399353027344,30.11343765258789,1487.949951171875,444.32000732421875,3271.1201171875,10745.26953125
+2020-08-01,106.61280059814453,127.44819641113281,221.55807495117188,17.406635284423828,172.54800415039062,33.25917053222656,1629.530029296875,513.3900146484375,3500.31005859375,11775.4599609375
+2020-08-07,,,,,,,,,,
+2020-09-01,106.57222747802734,114.5876235961914,207.12527465820312,17.323570251464844,157.43649291992188,34.06950759887695,1465.5999755859375,490.42999267578125,3363.0,11167.509765625
+2020-10-01,97.80435180664062,107.71099090576172,199.38504028320312,16.259458541870117,151.8074951171875,30.329862594604492,1616.1099853515625,447.1000061035156,3269.9599609375,10911.58984375
+2020-11-01,108.19266510009766,117.79344177246094,210.8082733154297,20.478687286376953,158.40199279785156,34.74394989013672,1754.4000244140625,478.4700012207031,3621.6298828125,12198.740234375
+2020-11-09,,,,,,,,,,
+2020-12-01,111.85865020751953,131.51597595214844,219.60447692871094,21.694869995117188,162.84649658203125,36.88808059692383,1752.6400146484375,500.1199951171875,3756.070068359375,12888.2802734375
+2021-01-01,105.84272766113281,130.7924346923828,229.0237274169922,19.891191482543945,160.30999755859375,36.6867561340332,1827.3599853515625,458.7699890136719,3714.239990234375,13070.6904296875
+2021-02-01,105.68277740478516,120.18710327148438,229.4384002685547,24.100215911865234,154.64649963378906,40.80388259887695,2021.9100341796875,459.6700134277344,3811.14990234375,13192.349609375
+2021-02-09,,,,,,,,,,
+2021-03-01,119.9990005493164,121.25013732910156,233.32164001464844,22.955739974975586,154.70399475097656,44.367366790771484,2062.52001953125,475.3699951171875,3972.889892578125,13246.8701171875
+2021-04-01,127.76121520996094,130.49156188964844,249.56121826171875,23.070621490478516,173.37100219726562,49.49113082885742,2353.5,508.3399963378906,4181.169921875,13962.6796875
+2021-05-01,129.4361114501953,123.6920166015625,247.08718872070312,22.41118812561035,161.15350341796875,49.64715576171875,2356.85009765625,504.5799865722656,4204.10986328125,13748.740234375
+2021-05-07,,,,,,,,,,
+2021-06-01,133.47738647460938,136.1819610595703,268.70587158203125,22.44941520690918,172.00799560546875,50.16557312011719,2441.7900390625,585.6400146484375,4297.5,14503.9501953125
+2021-07-01,128.35101318359375,145.03138732910156,282.6023864746094,23.305561065673828,166.37950134277344,48.63045883178711,2694.530029296875,621.6300048828125,4395.259765625,14672.6796875
+2021-08-01,127.78646087646484,150.96749877929688,299.4349365234375,21.74091148376465,173.5395050048828,49.053245544433594,2893.949951171875,663.7000122070312,4522.68017578125,15259.240234375
+2021-08-09,,,,,,,,,,
+2021-09-01,127.95899963378906,140.906982421875,280.1719665527344,19.48086166381836,164.2519989013672,52.36507034301758,2673.52001953125,575.719970703125,4307.5400390625,14448.580078125
+2021-10-01,115.22111511230469,149.1721954345703,329.56378173828125,17.397972106933594,168.6215057373047,55.35980224609375,2960.919921875,650.3599853515625,4605.3798828125,15498.3896484375
+2021-11-01,112.8140869140625,164.60723876953125,328.5401611328125,18.003969192504883,175.35350036621094,56.077186584472656,2837.949951171875,669.8499755859375,4567.0,15537.6904296875
+2021-11-04,,,,,,,,,,
+2021-11-09,,,,,,,,,,
+2021-12-01,130.48629760742188,177.08387756347656,334.8461608886719,22.1286563873291,166.7169952392578,55.77927017211914,2897.0400390625,567.0599975585938,4766.18017578125,15644.9697265625
+2022-01-01,130.3984375,174.30149841308594,309.6171875,20.856517791748047,149.57350158691406,56.41482162475586,2706.070068359375,534.2999877929688,4515.5498046875,14239.8798828125
+2022-02-01,119.60104370117188,164.66795349121094,297.4805908203125,19.47332763671875,153.56300354003906,50.60551452636719,2701.139892578125,467.67999267578125,4373.93994140625,13751.400390625
+2022-02-10,,,,,,,,,,
+2022-03-01,128.46168518066406,174.3538360595703,307.59356689453125,19.927804946899414,162.99749755859375,49.84086990356445,2781.35009765625,455.6199951171875,4530.41015625,14220.51953125
+2022-04-01,130.6254425048828,157.418701171875,276.8751220703125,17.399999618530273,124.28150177001953,46.68299102783203,2282.18994140625,395.95001220703125,4131.93017578125,12334.6396484375
+2022-05-01,137.1759796142578,148.6216278076172,271.2382507324219,18.81999969482422,120.20950317382812,49.939998626708984,2275.239990234375,416.4800109863281,4132.14990234375,12081.3896484375
+2022-05-09,,,,,,,,,,
+2022-06-01,141.86000061035156,137.44000244140625,256.4800109863281,15.819999694824219,107.4000015258789,48.939998626708984,2240.14990234375,365.6300048828125,3821.550048828125,11181.5400390625
+2022-06-28,141.86000061035156,137.44000244140625,256.4800109863281,15.819999694824219,107.4000015258789,48.939998626708984,2240.14990234375,365.6300048828125,3821.550048828125,11181.5400390625
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/data_x_x2_x3.csv b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/data_x_x2_x3.csv
new file mode 100644
index 0000000000000000000000000000000000000000..521da1453204a97f4189ecd3c82e99970786f208
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/data_x_x2_x3.csv
@@ -0,0 +1,11 @@
+ 0 0 0
+ 1 1 1
+ 2 4 8
+ 3 9 27
+ 4 16 64
+ 5 25 125
+ 6 36 216
+ 7 49 343
+ 8 64 512
+ 9 81 729
+10 100 1000
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/eeg.dat b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/eeg.dat
new file mode 100644
index 0000000000000000000000000000000000000000..c666c65053bfa37d63c9bdc1e5337e18484ec688
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/eeg.dat differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/embedding_in_wx3.xrc b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/embedding_in_wx3.xrc
new file mode 100644
index 0000000000000000000000000000000000000000..220656d735346d99363d97721fe738d8fcb4feeb
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/embedding_in_wx3.xrc
@@ -0,0 +1,65 @@
+
+
+
+
\ No newline at end of file
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/grace_hopper.jpg b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/grace_hopper.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..478720d6694a56b8962630e6dde8a230fc937049
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/grace_hopper.jpg differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/logo2.png b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/logo2.png
new file mode 100644
index 0000000000000000000000000000000000000000..72843ab1febbed35a00fc5c248f308f66d38cc24
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/logo2.png differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/membrane.dat b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/membrane.dat
new file mode 100644
index 0000000000000000000000000000000000000000..68f5e6b219448dac694aacba66b819564ba33467
Binary files /dev/null and b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/membrane.dat differ
diff --git a/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/msft.csv b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/msft.csv
new file mode 100644
index 0000000000000000000000000000000000000000..727b1befe36efde44fb5e2558e02410dad75a390
--- /dev/null
+++ b/lib/python3.12/site-packages/matplotlib/mpl-data/sample_data/msft.csv
@@ -0,0 +1,66 @@
+Date,Open,High,Low,Close,Volume,Adj. Close*
+19-Sep-03,29.76,29.97,29.52,29.96,92433800,29.79
+18-Sep-03,28.49,29.51,28.42,29.50,67268096,29.34
+17-Sep-03,28.76,28.95,28.47,28.50,47221600,28.34
+16-Sep-03,28.41,28.95,28.32,28.90,52060600,28.74
+15-Sep-03,28.37,28.61,28.33,28.36,41432300,28.20
+12-Sep-03,27.48,28.40,27.45,28.34,55777200,28.18
+11-Sep-03,27.66,28.11,27.59,27.84,37813300,27.68
+10-Sep-03,28.03,28.18,27.48,27.55,54763500,27.40
+9-Sep-03,28.65,28.71,28.31,28.37,44315200,28.21
+8-Sep-03,28.39,28.92,28.34,28.84,46105300,28.68
+5-Sep-03,28.23,28.75,28.17,28.38,64024500,28.22
+4-Sep-03,28.10,28.47,27.99,28.43,59840800,28.27
+3-Sep-03,27.42,28.40,27.38,28.30,109437800,28.14
+2-Sep-03,26.70,27.30,26.47,27.26,74168896,27.11
+29-Aug-03,26.46,26.55,26.35,26.52,34503000,26.37
+28-Aug-03,26.50,26.58,26.24,26.51,46211200,26.36
+27-Aug-03,26.51,26.58,26.30,26.42,30633900,26.27
+26-Aug-03,26.31,26.67,25.96,26.57,47546000,26.42
+25-Aug-03,26.31,26.54,26.23,26.50,36132900,26.35
+22-Aug-03,26.78,26.95,26.21,26.22,65846300,26.07
+21-Aug-03,26.65,26.73,26.13,26.24,63802700,26.09
+20-Aug-03,26.30,26.53,26.00,26.45,56739300,26.30
+19-Aug-03,25.85,26.65,25.77,26.62,72952896,26.47
+18-Aug-03,25.56,25.83,25.46,25.70,45817400,25.56
+15-Aug-03,25.61,25.66,25.43,25.54,27607900,25.40
+14-Aug-03,25.66,25.71,25.52,25.63,37338300,25.49
+13-Aug-03,25.79,25.89,25.50,25.60,39636900,25.46
+12-Aug-03,25.71,25.77,25.45,25.73,38208400,25.59
+11-Aug-03,25.61,25.99,25.54,25.61,36433900,25.47
+8-Aug-03,25.88,25.98,25.50,25.58,33241400,25.44
+7-Aug-03,25.72,25.81,25.45,25.71,44258500,25.57
+6-Aug-03,25.54,26.19,25.43,25.65,56294900,25.51
+5-Aug-03,26.31,26.54,25.60,25.66,58825800,25.52
+4-Aug-03,26.15,26.41,25.75,26.18,51825600,26.03
+1-Aug-03,26.33,26.51,26.12,26.17,42649700,26.02
+31-Jul-03,26.60,26.99,26.31,26.41,64504800,26.26
+30-Jul-03,26.46,26.57,26.17,26.23,41240300,26.08
+29-Jul-03,26.88,26.90,26.24,26.47,62391100,26.32
+28-Jul-03,26.94,27.00,26.49,26.61,52658300,26.46
+25-Jul-03,26.28,26.95,26.07,26.89,54173000,26.74
+24-Jul-03,26.78,26.92,25.98,26.00,53556600,25.85
+23-Jul-03,26.42,26.65,26.14,26.45,49828200,26.30
+22-Jul-03,26.28,26.56,26.13,26.38,51791000,26.23
+21-Jul-03,26.87,26.91,26.00,26.04,48480800,25.89
+18-Jul-03,27.11,27.23,26.75,26.89,63388400,26.74
+17-Jul-03,27.14,27.27,26.54,26.69,72805000,26.54
+16-Jul-03,27.56,27.62,27.20,27.52,49838900,27.37
+15-Jul-03,27.47,27.53,27.10,27.27,53567600,27.12
+14-Jul-03,27.63,27.81,27.05,27.40,60464400,27.25
+11-Jul-03,26.95,27.45,26.89,27.31,50377300,27.16
+10-Jul-03,27.25,27.42,26.59,26.91,55350800,26.76
+9-Jul-03,27.56,27.70,27.25,27.47,62300700,27.32
+8-Jul-03,27.26,27.80,27.25,27.70,61896800,27.55
+7-Jul-03,27.02,27.55,26.95,27.42,88960800,27.27
+3-Jul-03,26.69,26.95,26.41,26.50,39440900,26.35
+2-Jul-03,26.50,26.93,26.45,26.88,94069296,26.73
+1-Jul-03,25.59,26.20,25.39,26.15,60926000,26.00
+30-Jun-03,25.94,26.12,25.50,25.64,48073100,25.50
+27-Jun-03,25.95,26.34,25.53,25.63,76040304,25.49
+26-Jun-03,25.39,26.51,25.21,25.75,51758100,25.61
+25-Jun-03,25.64,25.99,25.14,25.26,60483500,25.12
+24-Jun-03,25.65,26.04,25.52,25.70,51820300,25.56
+23-Jun-03,26.14,26.24,25.49,25.78,52584500,25.64
+20-Jun-03,26.34,26.38,26.01,26.33,86048896,26.18
+19-Jun-03,26.09,26.39,26.01,26.07,63626900,25.92
\ No newline at end of file
diff --git a/lib/python3.12/site-packages/zmq/__init__.pxd b/lib/python3.12/site-packages/zmq/__init__.pxd
new file mode 100644
index 0000000000000000000000000000000000000000..37b8362e2a07a77d41ff9f6d3b589364a5e00a05
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/__init__.pxd
@@ -0,0 +1 @@
+from zmq.backend.cython cimport Context, Frame, Socket, libzmq
diff --git a/lib/python3.12/site-packages/zmq/__init__.py b/lib/python3.12/site-packages/zmq/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1149c580d9d9f5c802286d28b19c6c76be81efe
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/__init__.py
@@ -0,0 +1,94 @@
+"""Python bindings for 0MQ"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import annotations
+
+import os
+import sys
+from contextlib import contextmanager
+
+
+@contextmanager
+def _libs_on_path():
+ """context manager for libs directory on $PATH
+
+ Works around mysterious issue where os.add_dll_directory
+ does not resolve imports (conda-forge Python >= 3.8)
+ """
+
+ if not sys.platform.startswith("win"):
+ yield
+ return
+
+ libs_dir = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__),
+ os.pardir,
+ "pyzmq.libs",
+ )
+ )
+ if not os.path.exists(libs_dir):
+ # no bundled libs
+ yield
+ return
+
+ path_before = os.environ.get("PATH")
+ try:
+ os.environ["PATH"] = os.pathsep.join([path_before or "", libs_dir])
+ yield
+ finally:
+ if path_before is None:
+ os.environ.pop("PATH")
+ else:
+ os.environ["PATH"] = path_before
+
+
+# zmq top-level imports
+
+# workaround for Windows
+with _libs_on_path():
+ from zmq import backend
+
+from . import constants # noqa
+from .constants import * # noqa
+from zmq.backend import * # noqa
+from zmq import sugar
+from zmq.sugar import * # noqa
+
+
+def get_includes():
+ """Return a list of directories to include for linking against pyzmq with cython."""
+ from os.path import abspath, dirname, exists, join, pardir
+
+ base = dirname(__file__)
+ parent = abspath(join(base, pardir))
+ includes = [parent] + [join(parent, base, subdir) for subdir in ('utils',)]
+ if exists(join(parent, base, 'include')):
+ includes.append(join(parent, base, 'include'))
+ return includes
+
+
+def get_library_dirs():
+ """Return a list of directories used to link against pyzmq's bundled libzmq."""
+ from os.path import abspath, dirname, join, pardir
+
+ base = dirname(__file__)
+ parent = abspath(join(base, pardir))
+ return [join(parent, base)]
+
+
+COPY_THRESHOLD = 65536
+DRAFT_API = backend.has("draft")
+
+__all__ = (
+ [
+ 'get_includes',
+ 'COPY_THRESHOLD',
+ 'DRAFT_API',
+ ]
+ + constants.__all__
+ + sugar.__all__
+ + backend.__all__
+)
diff --git a/lib/python3.12/site-packages/zmq/__init__.pyi b/lib/python3.12/site-packages/zmq/__init__.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..56f4bdcaf0c08c199b0da853bae063d5d392fe5a
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/__init__.pyi
@@ -0,0 +1,28 @@
+from typing import List
+
+from . import backend, sugar
+
+COPY_THRESHOLD: int
+DRAFT_API: bool
+__version__: str
+
+# mypy doesn't like overwriting symbols with * so be explicit
+# about what comes from backend, not from sugar
+# see tools/backend_imports.py to generate this list
+# note: `x as x` is required for re-export
+# see https://github.com/python/mypy/issues/2190
+from .backend import IPC_PATH_MAX_LEN as IPC_PATH_MAX_LEN
+from .backend import curve_keypair as curve_keypair
+from .backend import curve_public as curve_public
+from .backend import has as has
+from .backend import proxy as proxy
+from .backend import proxy_steerable as proxy_steerable
+from .backend import strerror as strerror
+from .backend import zmq_errno as zmq_errno
+from .backend import zmq_poll as zmq_poll
+from .constants import *
+from .error import *
+from .sugar import *
+
+def get_includes() -> list[str]: ...
+def get_library_dirs() -> list[str]: ...
diff --git a/lib/python3.12/site-packages/zmq/_future.py b/lib/python3.12/site-packages/zmq/_future.py
new file mode 100644
index 0000000000000000000000000000000000000000..e598de14f768dc8540684bfa79589c00eeeceed0
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/_future.py
@@ -0,0 +1,737 @@
+"""Future-returning APIs for coroutines."""
+
+# Copyright (c) PyZMQ Developers.
+# Distributed under the terms of the Modified BSD License.
+from __future__ import annotations
+
+import warnings
+from asyncio import Future
+from collections import deque
+from functools import partial
+from itertools import chain
+from typing import (
+ Any,
+ Awaitable,
+ Callable,
+ NamedTuple,
+ TypeVar,
+ cast,
+)
+
+import zmq as _zmq
+from zmq import EVENTS, POLLIN, POLLOUT
+
+
+class _FutureEvent(NamedTuple):
+ future: Future
+ kind: str
+ args: tuple
+ kwargs: dict
+ msg: Any
+ timer: Any
+
+
+# These are incomplete classes and need a Mixin for compatibility with an eventloop
+# defining the following attributes:
+#
+# _Future
+# _READ
+# _WRITE
+# _default_loop()
+
+
+class _Async:
+ """Mixin for common async logic"""
+
+ _current_loop: Any = None
+ _Future: type[Future]
+
+ def _get_loop(self) -> Any:
+ """Get event loop
+
+ Notice if event loop has changed,
+ and register init_io_state on activation of a new event loop
+ """
+ if self._current_loop is None:
+ self._current_loop = self._default_loop()
+ self._init_io_state(self._current_loop)
+ return self._current_loop
+ current_loop = self._default_loop()
+ if current_loop is not self._current_loop:
+ # warn? This means a socket is being used in multiple loops!
+ self._current_loop = current_loop
+ self._init_io_state(current_loop)
+ return current_loop
+
+ def _default_loop(self) -> Any:
+ raise NotImplementedError("Must be implemented in a subclass")
+
+ def _init_io_state(self, loop=None) -> None:
+ pass
+
+
+class _AsyncPoller(_Async, _zmq.Poller):
+ """Poller that returns a Future on poll, instead of blocking."""
+
+ _socket_class: type[_AsyncSocket]
+ _READ: int
+ _WRITE: int
+ raw_sockets: list[Any]
+
+ def _watch_raw_socket(self, loop: Any, socket: Any, evt: int, f: Callable) -> None:
+ """Schedule callback for a raw socket"""
+ raise NotImplementedError()
+
+ def _unwatch_raw_sockets(self, loop: Any, *sockets: Any) -> None:
+ """Unschedule callback for a raw socket"""
+ raise NotImplementedError()
+
+ def poll(self, timeout=-1) -> Awaitable[list[tuple[Any, int]]]: # type: ignore
+ """Return a Future for a poll event"""
+ future = self._Future()
+ if timeout == 0:
+ try:
+ result = super().poll(0)
+ except Exception as e:
+ future.set_exception(e)
+ else:
+ future.set_result(result)
+ return future
+
+ loop = self._get_loop()
+
+ # register Future to be called as soon as any event is available on any socket
+ watcher = self._Future()
+
+ # watch raw sockets:
+ raw_sockets: list[Any] = []
+
+ def wake_raw(*args):
+ if not watcher.done():
+ watcher.set_result(None)
+
+ watcher.add_done_callback(
+ lambda f: self._unwatch_raw_sockets(loop, *raw_sockets)
+ )
+
+ wrapped_sockets: list[_AsyncSocket] = []
+
+ def _clear_wrapper_io(f):
+ for s in wrapped_sockets:
+ s._clear_io_state()
+
+ for socket, mask in self.sockets:
+ if isinstance(socket, _zmq.Socket):
+ if not isinstance(socket, self._socket_class):
+ # it's a blocking zmq.Socket, wrap it in async
+ socket = self._socket_class.from_socket(socket)
+ wrapped_sockets.append(socket)
+ if mask & _zmq.POLLIN:
+ socket._add_recv_event('poll', future=watcher)
+ if mask & _zmq.POLLOUT:
+ socket._add_send_event('poll', future=watcher)
+ else:
+ raw_sockets.append(socket)
+ evt = 0
+ if mask & _zmq.POLLIN:
+ evt |= self._READ
+ if mask & _zmq.POLLOUT:
+ evt |= self._WRITE
+ self._watch_raw_socket(loop, socket, evt, wake_raw)
+
+ def on_poll_ready(f):
+ if future.done():
+ return
+ if watcher.cancelled():
+ try:
+ future.cancel()
+ except RuntimeError:
+ # RuntimeError may be called during teardown
+ pass
+ return
+ if watcher.exception():
+ future.set_exception(watcher.exception())
+ else:
+ try:
+ result = super(_AsyncPoller, self).poll(0)
+ except Exception as e:
+ future.set_exception(e)
+ else:
+ future.set_result(result)
+
+ watcher.add_done_callback(on_poll_ready)
+
+ if wrapped_sockets:
+ watcher.add_done_callback(_clear_wrapper_io)
+
+ if timeout is not None and timeout > 0:
+ # schedule cancel to fire on poll timeout, if any
+ def trigger_timeout():
+ if not watcher.done():
+ watcher.set_result(None)
+
+ timeout_handle = loop.call_later(1e-3 * timeout, trigger_timeout)
+
+ def cancel_timeout(f):
+ if hasattr(timeout_handle, 'cancel'):
+ timeout_handle.cancel()
+ else:
+ loop.remove_timeout(timeout_handle)
+
+ future.add_done_callback(cancel_timeout)
+
+ def cancel_watcher(f):
+ if not watcher.done():
+ watcher.cancel()
+
+ future.add_done_callback(cancel_watcher)
+
+ return future
+
+
+class _NoTimer:
+ @staticmethod
+ def cancel():
+ pass
+
+
+T = TypeVar("T", bound="_AsyncSocket")
+
+
+class _AsyncSocket(_Async, _zmq.Socket[Future]):
+ # Warning : these class variables are only here to allow to call super().__setattr__.
+ # They be overridden at instance initialization and not shared in the whole class
+ _recv_futures = None
+ _send_futures = None
+ _state = 0
+ _shadow_sock: _zmq.Socket
+ _poller_class = _AsyncPoller
+ _fd = None
+
+ def __init__(
+ self,
+ context=None,
+ socket_type=-1,
+ io_loop=None,
+ _from_socket: _zmq.Socket | None = None,
+ **kwargs,
+ ) -> None:
+ if isinstance(context, _zmq.Socket):
+ context, _from_socket = (None, context)
+ if _from_socket is not None:
+ super().__init__(shadow=_from_socket.underlying) # type: ignore
+ self._shadow_sock = _from_socket
+ else:
+ super().__init__(context, socket_type, **kwargs) # type: ignore
+ self._shadow_sock = _zmq.Socket.shadow(self.underlying)
+
+ if io_loop is not None:
+ warnings.warn(
+ f"{self.__class__.__name__}(io_loop) argument is deprecated in pyzmq 22.2."
+ " The currently active loop will always be used.",
+ DeprecationWarning,
+ stacklevel=3,
+ )
+ self._recv_futures = deque()
+ self._send_futures = deque()
+ self._state = 0
+ self._fd = self._shadow_sock.FD
+
+ @classmethod
+ def from_socket(cls: type[T], socket: _zmq.Socket, io_loop: Any = None) -> T:
+ """Create an async socket from an existing Socket"""
+ return cls(_from_socket=socket, io_loop=io_loop)
+
+ def close(self, linger: int | None = None) -> None:
+ if not self.closed and self._fd is not None:
+ event_list: list[_FutureEvent] = list(
+ chain(self._recv_futures or [], self._send_futures or [])
+ )
+ for event in event_list:
+ if not event.future.done():
+ try:
+ event.future.cancel()
+ except RuntimeError:
+ # RuntimeError may be called during teardown
+ pass
+ self._clear_io_state()
+ super().close(linger=linger)
+
+ close.__doc__ = _zmq.Socket.close.__doc__
+
+ def get(self, key):
+ result = super().get(key)
+ if key == EVENTS:
+ self._schedule_remaining_events(result)
+ return result
+
+ get.__doc__ = _zmq.Socket.get.__doc__
+
+ def recv_multipart(
+ self, flags: int = 0, copy: bool = True, track: bool = False
+ ) -> Awaitable[list[bytes] | list[_zmq.Frame]]:
+ """Receive a complete multipart zmq message.
+
+ Returns a Future whose result will be a multipart message.
+ """
+ return self._add_recv_event(
+ 'recv_multipart', kwargs=dict(flags=flags, copy=copy, track=track)
+ )
+
+ def recv( # type: ignore
+ self, flags: int = 0, copy: bool = True, track: bool = False
+ ) -> Awaitable[bytes | _zmq.Frame]:
+ """Receive a single zmq frame.
+
+ Returns a Future, whose result will be the received frame.
+
+ Recommend using recv_multipart instead.
+ """
+ return self._add_recv_event(
+ 'recv', kwargs=dict(flags=flags, copy=copy, track=track)
+ )
+
+ def recv_into( # type: ignore
+ self, buf, /, *, nbytes: int = 0, flags: int = 0
+ ) -> Awaitable[int]:
+ """Receive a single zmq frame into a pre-allocated buffer.
+
+ Returns a Future, whose result will be the number of bytes received.
+ """
+ return self._add_recv_event(
+ 'recv_into', args=(buf,), kwargs=dict(nbytes=nbytes, flags=flags)
+ )
+
+ def send_multipart( # type: ignore
+ self, msg_parts: Any, flags: int = 0, copy: bool = True, track=False, **kwargs
+ ) -> Awaitable[_zmq.MessageTracker | None]:
+ """Send a complete multipart zmq message.
+
+ Returns a Future that resolves when sending is complete.
+ """
+ kwargs['flags'] = flags
+ kwargs['copy'] = copy
+ kwargs['track'] = track
+ return self._add_send_event('send_multipart', msg=msg_parts, kwargs=kwargs)
+
+ def send( # type: ignore
+ self,
+ data: Any,
+ flags: int = 0,
+ copy: bool = True,
+ track: bool = False,
+ **kwargs: Any,
+ ) -> Awaitable[_zmq.MessageTracker | None]:
+ """Send a single zmq frame.
+
+ Returns a Future that resolves when sending is complete.
+
+ Recommend using send_multipart instead.
+ """
+ kwargs['flags'] = flags
+ kwargs['copy'] = copy
+ kwargs['track'] = track
+ kwargs.update(dict(flags=flags, copy=copy, track=track))
+ return self._add_send_event('send', msg=data, kwargs=kwargs)
+
+ def _deserialize(self, recvd, load):
+ """Deserialize with Futures"""
+ f = self._Future()
+
+ def _chain(_):
+ """Chain result through serialization to recvd"""
+ if f.done():
+ # chained future may be cancelled, which means nobody is going to get this result
+ # if it's an error, that's no big deal (probably zmq.Again),
+ # but if it's a successful recv, this is a dropped message!
+ if not recvd.cancelled() and recvd.exception() is None:
+ warnings.warn(
+ # is there a useful stacklevel?
+ # ideally, it would point to where `f.cancel()` was called
+ f"Future {f} completed while awaiting {recvd}. A message has been dropped!",
+ RuntimeWarning,
+ )
+ return
+ if recvd.exception():
+ f.set_exception(recvd.exception())
+ else:
+ buf = recvd.result()
+ try:
+ loaded = load(buf)
+ except Exception as e:
+ f.set_exception(e)
+ else:
+ f.set_result(loaded)
+
+ recvd.add_done_callback(_chain)
+
+ def _chain_cancel(_):
+ """Chain cancellation from f to recvd"""
+ if recvd.done():
+ return
+ if f.cancelled():
+ recvd.cancel()
+
+ f.add_done_callback(_chain_cancel)
+
+ return f
+
+ def poll(self, timeout=None, flags=_zmq.POLLIN) -> Awaitable[int]: # type: ignore
+ """poll the socket for events
+
+ returns a Future for the poll results.
+ """
+
+ if self.closed:
+ raise _zmq.ZMQError(_zmq.ENOTSUP)
+
+ p = self._poller_class()
+ p.register(self, flags)
+ poll_future = cast(Future, p.poll(timeout))
+
+ future = self._Future()
+
+ def unwrap_result(f):
+ if future.done():
+ return
+ if poll_future.cancelled():
+ try:
+ future.cancel()
+ except RuntimeError:
+ # RuntimeError may be called during teardown
+ pass
+ return
+ if f.exception():
+ future.set_exception(poll_future.exception())
+ else:
+ evts = dict(poll_future.result())
+ future.set_result(evts.get(self, 0))
+
+ if poll_future.done():
+ # hook up result if already done
+ unwrap_result(poll_future)
+ else:
+ poll_future.add_done_callback(unwrap_result)
+
+ def cancel_poll(future):
+ """Cancel underlying poll if request has been cancelled"""
+ if not poll_future.done():
+ try:
+ poll_future.cancel()
+ except RuntimeError:
+ # RuntimeError may be called during teardown
+ pass
+
+ future.add_done_callback(cancel_poll)
+
+ return future
+
+ def _add_timeout(self, future, timeout):
+ """Add a timeout for a send or recv Future"""
+
+ def future_timeout():
+ if future.done():
+ # future already resolved, do nothing
+ return
+
+ # raise EAGAIN
+ future.set_exception(_zmq.Again())
+
+ return self._call_later(timeout, future_timeout)
+
+ def _call_later(self, delay, callback):
+ """Schedule a function to be called later
+
+ Override for different IOLoop implementations
+
+ Tornado and asyncio happen to both have ioloop.call_later
+ with the same signature.
+ """
+ return self._get_loop().call_later(delay, callback)
+
+ @staticmethod
+ def _remove_finished_future(future, event_list, event=None):
+ """Make sure that futures are removed from the event list when they resolve
+
+ Avoids delaying cleanup until the next send/recv event,
+ which may never come.
+ """
+ # "future" instance is shared between sockets, but each socket has its own event list.
+ if not event_list:
+ return
+ # only unconsumed events (e.g. cancelled calls)
+ # will be present when this happens
+ try:
+ event_list.remove(event)
+ except ValueError:
+ # usually this will have been removed by being consumed
+ return
+
+ def _add_recv_event(
+ self,
+ kind: str,
+ *,
+ args: tuple | None = None,
+ kwargs: dict[str, Any] | None = None,
+ future: Future | None = None,
+ ) -> Future:
+ """Add a recv event, returning the corresponding Future"""
+ f = future or self._Future()
+ if args is None:
+ args = ()
+ if kwargs is None:
+ kwargs = {}
+ if kind.startswith('recv') and kwargs.get('flags', 0) & _zmq.DONTWAIT:
+ # short-circuit non-blocking calls
+ recv = getattr(self._shadow_sock, kind)
+ try:
+ r = recv(*args, **kwargs)
+ except Exception as e:
+ f.set_exception(e)
+ else:
+ f.set_result(r)
+ return f
+
+ timer = _NoTimer
+ if hasattr(_zmq, 'RCVTIMEO'):
+ timeout_ms = self._shadow_sock.rcvtimeo
+ if timeout_ms >= 0:
+ timer = self._add_timeout(f, timeout_ms * 1e-3)
+
+ # we add it to the list of futures before we add the timeout as the
+ # timeout will remove the future from recv_futures to avoid leaks
+ _future_event = _FutureEvent(
+ f, kind, args=args, kwargs=kwargs, msg=None, timer=timer
+ )
+ self._recv_futures.append(_future_event)
+
+ if self._shadow_sock.get(EVENTS) & POLLIN:
+ # recv immediately, if we can
+ self._handle_recv()
+ if self._recv_futures and _future_event in self._recv_futures:
+ # Don't let the Future sit in _recv_events after it's done
+ # no need to register this if we've already been handled
+ # (i.e. immediately-resolved recv)
+ f.add_done_callback(
+ partial(
+ self._remove_finished_future,
+ event_list=self._recv_futures,
+ event=_future_event,
+ )
+ )
+ self._add_io_state(POLLIN)
+ return f
+
+ def _add_send_event(self, kind, msg=None, kwargs=None, future=None):
+ """Add a send event, returning the corresponding Future"""
+ f = future or self._Future()
+ # attempt send with DONTWAIT if no futures are waiting
+ # short-circuit for sends that will resolve immediately
+ # only call if no send Futures are waiting
+ if kind in ('send', 'send_multipart') and not self._send_futures:
+ flags = kwargs.get('flags', 0)
+ nowait_kwargs = kwargs.copy()
+ nowait_kwargs['flags'] = flags | _zmq.DONTWAIT
+
+ # short-circuit non-blocking calls
+ send = getattr(self._shadow_sock, kind)
+ # track if the send resolved or not
+ # (EAGAIN if DONTWAIT is not set should proceed with)
+ finish_early = True
+ try:
+ r = send(msg, **nowait_kwargs)
+ except _zmq.Again as e:
+ if flags & _zmq.DONTWAIT:
+ f.set_exception(e)
+ else:
+ # EAGAIN raised and DONTWAIT not requested,
+ # proceed with async send
+ finish_early = False
+ except Exception as e:
+ f.set_exception(e)
+ else:
+ f.set_result(r)
+
+ if finish_early:
+ # short-circuit resolved, return finished Future
+ # schedule wake for recv if there are any receivers waiting
+ if self._recv_futures:
+ self._schedule_remaining_events()
+ return f
+
+ timer = _NoTimer
+ if hasattr(_zmq, 'SNDTIMEO'):
+ timeout_ms = self._shadow_sock.get(_zmq.SNDTIMEO)
+ if timeout_ms >= 0:
+ timer = self._add_timeout(f, timeout_ms * 1e-3)
+
+ # we add it to the list of futures before we add the timeout as the
+ # timeout will remove the future from recv_futures to avoid leaks
+ _future_event = _FutureEvent(
+ f, kind, args=(), kwargs=kwargs, msg=msg, timer=timer
+ )
+ self._send_futures.append(_future_event)
+ # Don't let the Future sit in _send_futures after it's done
+ f.add_done_callback(
+ partial(
+ self._remove_finished_future,
+ event_list=self._send_futures,
+ event=_future_event,
+ )
+ )
+
+ self._add_io_state(POLLOUT)
+ return f
+
+ def _handle_recv(self):
+ """Handle recv events"""
+ if not self._shadow_sock.get(EVENTS) & POLLIN:
+ # event triggered, but state may have been changed between trigger and callback
+ return
+ f = None
+ while self._recv_futures:
+ f, kind, args, kwargs, _, timer = self._recv_futures.popleft()
+ # skip any cancelled futures
+ if f.done():
+ f = None
+ else:
+ break
+
+ if not self._recv_futures:
+ self._drop_io_state(POLLIN)
+
+ if f is None:
+ return
+
+ timer.cancel()
+
+ if kind == 'poll':
+ # on poll event, just signal ready, nothing else.
+ f.set_result(None)
+ return
+ elif kind == 'recv_multipart':
+ recv = self._shadow_sock.recv_multipart
+ elif kind == 'recv':
+ recv = self._shadow_sock.recv
+ elif kind == 'recv_into':
+ recv = self._shadow_sock.recv_into
+ else:
+ raise ValueError(f"Unhandled recv event type: {kind!r}")
+
+ kwargs['flags'] |= _zmq.DONTWAIT
+ try:
+ result = recv(*args, **kwargs)
+ except Exception as e:
+ f.set_exception(e)
+ else:
+ f.set_result(result)
+
+ def _handle_send(self):
+ if not self._shadow_sock.get(EVENTS) & POLLOUT:
+ # event triggered, but state may have been changed between trigger and callback
+ return
+ f = None
+ while self._send_futures:
+ f, kind, args, kwargs, msg, timer = self._send_futures.popleft()
+ # skip any cancelled futures
+ if f.done():
+ f = None
+ else:
+ break
+
+ if not self._send_futures:
+ self._drop_io_state(POLLOUT)
+
+ if f is None:
+ return
+
+ timer.cancel()
+
+ if kind == 'poll':
+ # on poll event, just signal ready, nothing else.
+ f.set_result(None)
+ return
+ elif kind == 'send_multipart':
+ send = self._shadow_sock.send_multipart
+ elif kind == 'send':
+ send = self._shadow_sock.send
+ else:
+ raise ValueError(f"Unhandled send event type: {kind!r}")
+
+ kwargs['flags'] |= _zmq.DONTWAIT
+ try:
+ result = send(msg, **kwargs)
+ except Exception as e:
+ f.set_exception(e)
+ else:
+ f.set_result(result)
+
+ # event masking from ZMQStream
+ def _handle_events(self, fd=0, events=0):
+ """Dispatch IO events to _handle_recv, etc."""
+ if self._shadow_sock.closed:
+ return
+
+ zmq_events = self._shadow_sock.get(EVENTS)
+ if zmq_events & _zmq.POLLIN:
+ self._handle_recv()
+ if zmq_events & _zmq.POLLOUT:
+ self._handle_send()
+ self._schedule_remaining_events()
+
+ def _schedule_remaining_events(self, events=None):
+ """Schedule a call to handle_events next loop iteration
+
+ If there are still events to handle.
+ """
+ # edge-triggered handling
+ # allow passing events in, in case this is triggered by retrieving events,
+ # so we don't have to retrieve it twice.
+ if self._state == 0:
+ # not watching for anything, nothing to schedule
+ return
+ if events is None:
+ events = self._shadow_sock.get(EVENTS)
+ if events & self._state:
+ self._call_later(0, self._handle_events)
+
+ def _add_io_state(self, state):
+ """Add io_state to poller."""
+ if self._state != state:
+ state = self._state = self._state | state
+ self._update_handler(self._state)
+
+ def _drop_io_state(self, state):
+ """Stop poller from watching an io_state."""
+ if self._state & state:
+ self._state = self._state & (~state)
+ self._update_handler(self._state)
+
+ def _update_handler(self, state):
+ """Update IOLoop handler with state.
+
+ zmq FD is always read-only.
+ """
+ # ensure loop is registered and init_io has been called
+ # if there are any events to watch for
+ if state:
+ self._get_loop()
+ self._schedule_remaining_events()
+
+ def _init_io_state(self, loop=None):
+ """initialize the ioloop event handler"""
+ if loop is None:
+ loop = self._get_loop()
+ loop.add_handler(self._shadow_sock, self._handle_events, self._READ)
+ self._call_later(0, self._handle_events)
+
+ def _clear_io_state(self):
+ """unregister the ioloop event handler
+
+ called once during close
+ """
+ fd = self._shadow_sock
+ if self._shadow_sock.closed:
+ fd = self._fd
+ if self._current_loop is not None:
+ self._current_loop.remove_handler(fd)
diff --git a/lib/python3.12/site-packages/zmq/_future.pyi b/lib/python3.12/site-packages/zmq/_future.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..e22315e82598249391a3bbffbbf2ecc5dd0d12d5
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/_future.pyi
@@ -0,0 +1,95 @@
+"""type annotations for async sockets"""
+
+from __future__ import annotations
+
+from asyncio import Future
+from pickle import DEFAULT_PROTOCOL
+from typing import Any, Awaitable, Literal, Sequence, TypeVar, overload
+
+import zmq as _zmq
+
+class _AsyncPoller(_zmq.Poller):
+ _socket_class: type[_AsyncSocket]
+
+ def poll(self, timeout=-1) -> Awaitable[list[tuple[Any, int]]]: ... # type: ignore
+
+T = TypeVar("T", bound="_AsyncSocket")
+
+class _AsyncSocket(_zmq.Socket[Future]):
+ @classmethod
+ def from_socket(cls: type[T], socket: _zmq.Socket, io_loop: Any = None) -> T: ...
+ def send( # type: ignore
+ self,
+ data: Any,
+ flags: int = 0,
+ copy: bool = True,
+ track: bool = False,
+ routing_id: int | None = None,
+ group: str | None = None,
+ ) -> Awaitable[_zmq.MessageTracker | None]: ...
+ @overload # type: ignore
+ def recv(self, flags: int = 0, *, track: bool = False) -> Awaitable[bytes]: ...
+ @overload
+ def recv(
+ self, flags: int = 0, *, copy: Literal[True], track: bool = False
+ ) -> Awaitable[bytes]: ...
+ @overload
+ def recv(
+ self, flags: int = 0, *, copy: Literal[False], track: bool = False
+ ) -> Awaitable[_zmq.Frame]: ...
+ @overload
+ def recv(
+ self, flags: int = 0, copy: bool = True, track: bool = False
+ ) -> Awaitable[bytes | _zmq.Frame]: ...
+ def recv_into( # type: ignore
+ self, buffer: Any, /, *, nbytes: int = 0, flags: int = 0
+ ) -> Awaitable[int]: ...
+ def send_multipart( # type: ignore
+ self,
+ msg_parts: Sequence,
+ flags: int = 0,
+ copy: bool = True,
+ track: bool = False,
+ routing_id: int | None = None,
+ group: str | None = None,
+ ) -> Awaitable[_zmq.MessageTracker | None]: ...
+ @overload # type: ignore
+ def recv_multipart(
+ self, flags: int = 0, *, track: bool = False
+ ) -> Awaitable[list[bytes]]: ...
+ @overload
+ def recv_multipart(
+ self, flags: int = 0, *, copy: Literal[True], track: bool = False
+ ) -> Awaitable[list[bytes]]: ...
+ @overload
+ def recv_multipart(
+ self, flags: int = 0, *, copy: Literal[False], track: bool = False
+ ) -> Awaitable[list[_zmq.Frame]]: ...
+ @overload
+ def recv_multipart(
+ self, flags: int = 0, copy: bool = True, track: bool = False
+ ) -> Awaitable[list[bytes] | list[_zmq.Frame]]: ...
+
+ # serialization wrappers
+
+ def send_string( # type: ignore
+ self,
+ u: str,
+ flags: int = 0,
+ copy: bool = True,
+ *,
+ encoding: str = 'utf-8',
+ **kwargs,
+ ) -> Awaitable[_zmq.Frame | None]: ...
+ def recv_string( # type: ignore
+ self, flags: int = 0, encoding: str = 'utf-8'
+ ) -> Awaitable[str]: ...
+ def send_pyobj( # type: ignore
+ self, obj: Any, flags: int = 0, protocol: int = DEFAULT_PROTOCOL, **kwargs
+ ) -> Awaitable[_zmq.Frame | None]: ...
+ def recv_pyobj(self, flags: int = 0) -> Awaitable[Any]: ... # type: ignore
+ def send_json( # type: ignore
+ self, obj: Any, flags: int = 0, **kwargs
+ ) -> Awaitable[_zmq.Frame | None]: ...
+ def recv_json(self, flags: int = 0, **kwargs) -> Awaitable[Any]: ... # type: ignore
+ def poll(self, timeout=-1) -> Awaitable[list[tuple[Any, int]]]: ... # type: ignore
diff --git a/lib/python3.12/site-packages/zmq/_typing.py b/lib/python3.12/site-packages/zmq/_typing.py
new file mode 100644
index 0000000000000000000000000000000000000000..9833a1b9d2bc5d66207e782773c2a09d8878ebcf
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/_typing.py
@@ -0,0 +1,11 @@
+from __future__ import annotations
+
+import sys
+
+if sys.version_info >= (3, 10):
+ from typing import TypeAlias
+else:
+ try:
+ from typing_extensions import TypeAlias
+ except ImportError:
+ TypeAlias = type # type: ignore
diff --git a/lib/python3.12/site-packages/zmq/asyncio.py b/lib/python3.12/site-packages/zmq/asyncio.py
new file mode 100644
index 0000000000000000000000000000000000000000..22bbd14d423cc2fb31efa7846681ab4420a2a6fd
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/asyncio.py
@@ -0,0 +1,224 @@
+"""AsyncIO support for zmq
+
+Requires asyncio and Python 3.
+"""
+
+# Copyright (c) PyZMQ Developers.
+# Distributed under the terms of the Modified BSD License.
+from __future__ import annotations
+
+import asyncio
+import selectors
+import sys
+import warnings
+from asyncio import Future, SelectorEventLoop
+from weakref import WeakKeyDictionary
+
+import zmq as _zmq
+from zmq import _future
+
+# registry of asyncio loop : selector thread
+_selectors: WeakKeyDictionary = WeakKeyDictionary()
+
+
+class ProactorSelectorThreadWarning(RuntimeWarning):
+ """Warning class for notifying about the extra thread spawned by tornado
+
+ We automatically support proactor via tornado's AddThreadSelectorEventLoop"""
+
+
+def _get_selector_windows(
+ asyncio_loop,
+) -> asyncio.AbstractEventLoop:
+ """Get selector-compatible loop
+
+ Returns an object with ``add_reader`` family of methods,
+ either the loop itself or a SelectorThread instance.
+
+ Workaround Windows proactor removal of
+ *reader methods, which we need for zmq sockets.
+ """
+
+ if asyncio_loop in _selectors:
+ return _selectors[asyncio_loop]
+
+ # detect add_reader instead of checking for proactor?
+ if hasattr(asyncio, "ProactorEventLoop") and isinstance(
+ asyncio_loop,
+ asyncio.ProactorEventLoop, # type: ignore
+ ):
+ try:
+ from tornado.platform.asyncio import AddThreadSelectorEventLoop
+ except ImportError:
+ raise RuntimeError(
+ "Proactor event loop does not implement add_reader family of methods required for zmq."
+ " zmq will work with proactor if tornado >= 6.1 can be found."
+ " Use `asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())`"
+ " or install 'tornado>=6.1' to avoid this error."
+ )
+
+ warnings.warn(
+ "Proactor event loop does not implement add_reader family of methods required for zmq."
+ " Registering an additional selector thread for add_reader support via tornado."
+ " Use `asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())`"
+ " to avoid this warning.",
+ RuntimeWarning,
+ # stacklevel 5 matches most likely zmq.asyncio.Context().socket()
+ stacklevel=5,
+ )
+
+ selector_loop = _selectors[asyncio_loop] = AddThreadSelectorEventLoop(
+ asyncio_loop
+ ) # type: ignore
+
+ # patch loop.close to also close the selector thread
+ loop_close = asyncio_loop.close
+
+ def _close_selector_and_loop():
+ # restore original before calling selector.close,
+ # which in turn calls eventloop.close!
+ asyncio_loop.close = loop_close
+ _selectors.pop(asyncio_loop, None)
+ selector_loop.close()
+
+ asyncio_loop.close = _close_selector_and_loop # type: ignore # mypy bug - assign a function to method
+ return selector_loop
+ else:
+ return asyncio_loop
+
+
+def _get_selector_noop(loop) -> asyncio.AbstractEventLoop:
+ """no-op on non-Windows"""
+ return loop
+
+
+if sys.platform == "win32":
+ _get_selector = _get_selector_windows
+else:
+ _get_selector = _get_selector_noop
+
+
+class _AsyncIO:
+ _Future = Future
+ _WRITE = selectors.EVENT_WRITE
+ _READ = selectors.EVENT_READ
+
+ def _default_loop(self):
+ try:
+ return asyncio.get_running_loop()
+ except RuntimeError:
+ warnings.warn(
+ "No running event loop. zmq.asyncio should be used from within an asyncio loop.",
+ RuntimeWarning,
+ stacklevel=4,
+ )
+ # get_event_loop deprecated in 3.10:
+ return asyncio.get_event_loop()
+
+
+class Poller(_AsyncIO, _future._AsyncPoller):
+ """Poller returning asyncio.Future for poll results."""
+
+ def _watch_raw_socket(self, loop, socket, evt, f):
+ """Schedule callback for a raw socket"""
+ selector = _get_selector(loop)
+ if evt & self._READ:
+ selector.add_reader(socket, lambda *args: f())
+ if evt & self._WRITE:
+ selector.add_writer(socket, lambda *args: f())
+
+ def _unwatch_raw_sockets(self, loop, *sockets):
+ """Unschedule callback for a raw socket"""
+ selector = _get_selector(loop)
+ for socket in sockets:
+ selector.remove_reader(socket)
+ selector.remove_writer(socket)
+
+
+class Socket(_AsyncIO, _future._AsyncSocket):
+ """Socket returning asyncio Futures for send/recv/poll methods."""
+
+ _poller_class = Poller
+
+ def _get_selector(self, io_loop=None):
+ if io_loop is None:
+ io_loop = self._get_loop()
+ return _get_selector(io_loop)
+
+ def _init_io_state(self, io_loop=None):
+ """initialize the ioloop event handler"""
+ self._get_selector(io_loop).add_reader(
+ self._fd, lambda: self._handle_events(0, 0)
+ )
+
+ def _clear_io_state(self):
+ """clear any ioloop event handler
+
+ called once at close
+ """
+ loop = self._current_loop
+ if loop and not loop.is_closed() and self._fd != -1:
+ self._get_selector(loop).remove_reader(self._fd)
+
+
+Poller._socket_class = Socket
+
+
+class Context(_zmq.Context[Socket]):
+ """Context for creating asyncio-compatible Sockets"""
+
+ _socket_class = Socket
+
+ # avoid sharing instance with base Context class
+ _instance = None
+
+ # overload with no changes to satisfy pyright
+ def __init__(
+ self: Context,
+ io_threads: int | _zmq.Context = 1,
+ shadow: _zmq.Context | int = 0,
+ ) -> None:
+ super().__init__(io_threads, shadow) # type: ignore
+
+
+class ZMQEventLoop(SelectorEventLoop):
+ """DEPRECATED: AsyncIO eventloop using zmq_poll.
+
+ pyzmq sockets should work with any asyncio event loop as of pyzmq 17.
+ """
+
+ def __init__(self, selector=None):
+ _deprecated()
+ return super().__init__(selector)
+
+
+_loop = None
+
+
+def _deprecated():
+ if _deprecated.called: # type: ignore
+ return
+ _deprecated.called = True # type: ignore
+
+ warnings.warn(
+ "ZMQEventLoop and zmq.asyncio.install are deprecated in pyzmq 17. Special eventloop integration is no longer needed.",
+ DeprecationWarning,
+ stacklevel=3,
+ )
+
+
+_deprecated.called = False # type: ignore
+
+
+def install():
+ """DEPRECATED: No longer needed in pyzmq 17"""
+ _deprecated()
+
+
+__all__ = [
+ "Context",
+ "Socket",
+ "Poller",
+ "ZMQEventLoop",
+ "install",
+]
diff --git a/lib/python3.12/site-packages/zmq/auth/__init__.py b/lib/python3.12/site-packages/zmq/auth/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ebacab71272ab4e1b7999ea8d6a875c08c6b861b
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/auth/__init__.py
@@ -0,0 +1,13 @@
+"""Utilities for ZAP authentication.
+
+To run authentication in a background thread, see :mod:`zmq.auth.thread`.
+For integration with the asyncio event loop, see :mod:`zmq.auth.asyncio`.
+
+Authentication examples are provided in the pyzmq codebase, under
+`/examples/security/`.
+
+.. versionadded:: 14.1
+"""
+
+from .base import *
+from .certs import *
diff --git a/lib/python3.12/site-packages/zmq/auth/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/zmq/auth/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..68f932bb54432483feed3f9c9b837348396f86da
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/auth/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/auth/__pycache__/asyncio.cpython-312.pyc b/lib/python3.12/site-packages/zmq/auth/__pycache__/asyncio.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f306fff02fe301fbbed5ed778e9b5e28b77365ff
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/auth/__pycache__/asyncio.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/auth/__pycache__/base.cpython-312.pyc b/lib/python3.12/site-packages/zmq/auth/__pycache__/base.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..74802de66348f56bf93e412bc24cd169fd36d114
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/auth/__pycache__/base.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/auth/__pycache__/certs.cpython-312.pyc b/lib/python3.12/site-packages/zmq/auth/__pycache__/certs.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..497b86c48e78227a6fcd01242c01dbbcf6c6b76f
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/auth/__pycache__/certs.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/auth/__pycache__/ioloop.cpython-312.pyc b/lib/python3.12/site-packages/zmq/auth/__pycache__/ioloop.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..da31fc60e1fe2f7e54a64ec38c2889ef35821d2a
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/auth/__pycache__/ioloop.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/auth/__pycache__/thread.cpython-312.pyc b/lib/python3.12/site-packages/zmq/auth/__pycache__/thread.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..68104654294c24a01fa4802489b5098d60974f50
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/auth/__pycache__/thread.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/auth/asyncio.py b/lib/python3.12/site-packages/zmq/auth/asyncio.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b4915c12784538761dab70cee8e34395a1df066
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/auth/asyncio.py
@@ -0,0 +1,66 @@
+"""ZAP Authenticator integrated with the asyncio IO loop.
+
+.. versionadded:: 15.2
+"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import asyncio
+import warnings
+from typing import Any, Optional
+
+import zmq
+from zmq.asyncio import Poller
+
+from .base import Authenticator
+
+
+class AsyncioAuthenticator(Authenticator):
+ """ZAP authentication for use in the asyncio IO loop"""
+
+ __poller: Optional[Poller]
+ __task: Any
+
+ def __init__(
+ self,
+ context: Optional["zmq.Context"] = None,
+ loop: Any = None,
+ encoding: str = 'utf-8',
+ log: Any = None,
+ ):
+ super().__init__(context, encoding, log)
+ if loop is not None:
+ warnings.warn(
+ f"{self.__class__.__name__}(loop) is deprecated and ignored",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ self.__poller = None
+ self.__task = None
+
+ async def __handle_zap(self) -> None:
+ while self.__poller is not None:
+ events = await self.__poller.poll()
+ if self.zap_socket in dict(events):
+ msg = self.zap_socket.recv_multipart()
+ await self.handle_zap_message(msg)
+
+ def start(self) -> None:
+ """Start ZAP authentication"""
+ super().start()
+ self.__poller = Poller()
+ self.__poller.register(self.zap_socket, zmq.POLLIN)
+ self.__task = asyncio.ensure_future(self.__handle_zap())
+
+ def stop(self) -> None:
+ """Stop ZAP authentication"""
+ if self.__task:
+ self.__task.cancel()
+ if self.__poller:
+ self.__poller.unregister(self.zap_socket)
+ self.__poller = None
+ super().stop()
+
+
+__all__ = ["AsyncioAuthenticator"]
diff --git a/lib/python3.12/site-packages/zmq/auth/base.py b/lib/python3.12/site-packages/zmq/auth/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..c862b60c14f0625de1cf8c0006357760dce88d29
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/auth/base.py
@@ -0,0 +1,445 @@
+"""Base implementation of 0MQ authentication."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import logging
+import os
+from typing import Any, Awaitable, Dict, List, Optional, Set, Tuple, Union
+
+import zmq
+from zmq.error import _check_version
+from zmq.utils import z85
+
+from .certs import load_certificates
+
+CURVE_ALLOW_ANY = '*'
+VERSION = b'1.0'
+
+
+class Authenticator:
+ """Implementation of ZAP authentication for zmq connections.
+
+ This authenticator class does not register with an event loop. As a result,
+ you will need to manually call `handle_zap_message`::
+
+ auth = zmq.Authenticator()
+ auth.allow("127.0.0.1")
+ auth.start()
+ while True:
+ await auth.handle_zap_msg(auth.zap_socket.recv_multipart())
+
+ Alternatively, you can register `auth.zap_socket` with a poller.
+
+ Since many users will want to run ZAP in a way that does not block the
+ main thread, other authentication classes (such as :mod:`zmq.auth.thread`)
+ are provided.
+
+ Note:
+
+ - libzmq provides four levels of security: default NULL (which the Authenticator does
+ not see), and authenticated NULL, PLAIN, CURVE, and GSSAPI, which the Authenticator can see.
+ - until you add policies, all incoming NULL connections are allowed.
+ (classic ZeroMQ behavior), and all PLAIN and CURVE connections are denied.
+ - GSSAPI requires no configuration.
+ """
+
+ context: "zmq.Context"
+ encoding: str
+ allow_any: bool
+ credentials_providers: Dict[str, Any]
+ zap_socket: "zmq.Socket"
+ _allowed: Set[str]
+ _denied: Set[str]
+ passwords: Dict[str, Dict[str, str]]
+ certs: Dict[str, Dict[bytes, Any]]
+ log: Any
+
+ def __init__(
+ self,
+ context: Optional["zmq.Context"] = None,
+ encoding: str = 'utf-8',
+ log: Any = None,
+ ):
+ _check_version((4, 0), "security")
+ self.context = context or zmq.Context.instance()
+ self.encoding = encoding
+ self.allow_any = False
+ self.credentials_providers = {}
+ self.zap_socket = None # type: ignore
+ self._allowed = set()
+ self._denied = set()
+ # passwords is a dict keyed by domain and contains values
+ # of dicts with username:password pairs.
+ self.passwords = {}
+ # certs is dict keyed by domain and contains values
+ # of dicts keyed by the public keys from the specified location.
+ self.certs = {}
+ self.log = log or logging.getLogger('zmq.auth')
+
+ def start(self) -> None:
+ """Create and bind the ZAP socket"""
+ self.zap_socket = self.context.socket(zmq.REP, socket_class=zmq.Socket)
+ self.zap_socket.linger = 1
+ self.zap_socket.bind("inproc://zeromq.zap.01")
+ self.log.debug("Starting")
+
+ def stop(self) -> None:
+ """Close the ZAP socket"""
+ if self.zap_socket:
+ self.zap_socket.close()
+ self.zap_socket = None # type: ignore
+
+ def allow(self, *addresses: str) -> None:
+ """Allow IP address(es).
+
+ Connections from addresses not explicitly allowed will be rejected.
+
+ - For NULL, all clients from this address will be accepted.
+ - For real auth setups, they will be allowed to continue with authentication.
+
+ allow is mutually exclusive with deny.
+ """
+ if self._denied:
+ raise ValueError("Only use allow or deny, not both")
+ self.log.debug("Allowing %s", ','.join(addresses))
+ self._allowed.update(addresses)
+
+ def deny(self, *addresses: str) -> None:
+ """Deny IP address(es).
+
+ Addresses not explicitly denied will be allowed to continue with authentication.
+
+ deny is mutually exclusive with allow.
+ """
+ if self._allowed:
+ raise ValueError("Only use a allow or deny, not both")
+ self.log.debug("Denying %s", ','.join(addresses))
+ self._denied.update(addresses)
+
+ def configure_plain(
+ self, domain: str = '*', passwords: Optional[Dict[str, str]] = None
+ ) -> None:
+ """Configure PLAIN authentication for a given domain.
+
+ PLAIN authentication uses a plain-text password file.
+ To cover all domains, use "*".
+ You can modify the password file at any time; it is reloaded automatically.
+ """
+ if passwords:
+ self.passwords[domain] = passwords
+ self.log.debug("Configure plain: %s", domain)
+
+ def configure_curve(
+ self, domain: str = '*', location: Union[str, os.PathLike] = "."
+ ) -> None:
+ """Configure CURVE authentication for a given domain.
+
+ CURVE authentication uses a directory that holds all public client certificates,
+ i.e. their public keys.
+
+ To cover all domains, use "*".
+
+ You can add and remove certificates in that directory at any time. configure_curve must be called
+ every time certificates are added or removed, in order to update the Authenticator's state
+
+ To allow all client keys without checking, specify CURVE_ALLOW_ANY for the location.
+ """
+ # If location is CURVE_ALLOW_ANY then allow all clients. Otherwise
+ # treat location as a directory that holds the certificates.
+ self.log.debug("Configure curve: %s[%s]", domain, location)
+ if location == CURVE_ALLOW_ANY:
+ self.allow_any = True
+ else:
+ self.allow_any = False
+ try:
+ self.certs[domain] = load_certificates(location)
+ except Exception as e:
+ self.log.error("Failed to load CURVE certs from %s: %s", location, e)
+
+ def configure_curve_callback(
+ self, domain: str = '*', credentials_provider: Any = None
+ ) -> None:
+ """Configure CURVE authentication for a given domain.
+
+ CURVE authentication using a callback function validating
+ the client public key according to a custom mechanism, e.g. checking the
+ key against records in a db. credentials_provider is an object of a class which
+ implements a callback method accepting two parameters (domain and key), e.g.::
+
+ class CredentialsProvider(object):
+
+ def __init__(self):
+ ...e.g. db connection
+
+ def callback(self, domain, key):
+ valid = ...lookup key and/or domain in db
+ if valid:
+ logging.info('Authorizing: {0}, {1}'.format(domain, key))
+ return True
+ else:
+ logging.warning('NOT Authorizing: {0}, {1}'.format(domain, key))
+ return False
+
+ To cover all domains, use "*".
+ """
+
+ self.allow_any = False
+
+ if credentials_provider is not None:
+ self.credentials_providers[domain] = credentials_provider
+ else:
+ self.log.error("None credentials_provider provided for domain:%s", domain)
+
+ def curve_user_id(self, client_public_key: bytes) -> str:
+ """Return the User-Id corresponding to a CURVE client's public key
+
+ Default implementation uses the z85-encoding of the public key.
+
+ Override to define a custom mapping of public key : user-id
+
+ This is only called on successful authentication.
+
+ Parameters
+ ----------
+ client_public_key: bytes
+ The client public key used for the given message
+
+ Returns
+ -------
+ user_id: unicode
+ The user ID as text
+ """
+ return z85.encode(client_public_key).decode('ascii')
+
+ def configure_gssapi(
+ self, domain: str = '*', location: Optional[str] = None
+ ) -> None:
+ """Configure GSSAPI authentication
+
+ Currently this is a no-op because there is nothing to configure with GSSAPI.
+ """
+
+ async def handle_zap_message(self, msg: List[bytes]):
+ """Perform ZAP authentication"""
+ if len(msg) < 6:
+ self.log.error("Invalid ZAP message, not enough frames: %r", msg)
+ if len(msg) < 2:
+ self.log.error("Not enough information to reply")
+ else:
+ self._send_zap_reply(msg[1], b"400", b"Not enough frames")
+ return
+
+ version, request_id, domain, address, identity, mechanism = msg[:6]
+ credentials = msg[6:]
+
+ domain = domain.decode(self.encoding, 'replace')
+ address = address.decode(self.encoding, 'replace')
+
+ if version != VERSION:
+ self.log.error("Invalid ZAP version: %r", msg)
+ self._send_zap_reply(request_id, b"400", b"Invalid version")
+ return
+
+ self.log.debug(
+ "version: %r, request_id: %r, domain: %r,"
+ " address: %r, identity: %r, mechanism: %r",
+ version,
+ request_id,
+ domain,
+ address,
+ identity,
+ mechanism,
+ )
+
+ # Is address is explicitly allowed or _denied?
+ allowed = False
+ denied = False
+ reason = b"NO ACCESS"
+
+ if self._allowed:
+ if address in self._allowed:
+ allowed = True
+ self.log.debug("PASSED (allowed) address=%s", address)
+ else:
+ denied = True
+ reason = b"Address not allowed"
+ self.log.debug("DENIED (not allowed) address=%s", address)
+
+ elif self._denied:
+ if address in self._denied:
+ denied = True
+ reason = b"Address denied"
+ self.log.debug("DENIED (denied) address=%s", address)
+ else:
+ allowed = True
+ self.log.debug("PASSED (not denied) address=%s", address)
+
+ # Perform authentication mechanism-specific checks if necessary
+ username = "anonymous"
+ if not denied:
+ if mechanism == b'NULL' and not allowed:
+ # For NULL, we allow if the address wasn't denied
+ self.log.debug("ALLOWED (NULL)")
+ allowed = True
+
+ elif mechanism == b'PLAIN':
+ # For PLAIN, even a _alloweded address must authenticate
+ if len(credentials) != 2:
+ self.log.error("Invalid PLAIN credentials: %r", credentials)
+ self._send_zap_reply(request_id, b"400", b"Invalid credentials")
+ return
+ username, password = (
+ c.decode(self.encoding, 'replace') for c in credentials
+ )
+ allowed, reason = self._authenticate_plain(domain, username, password)
+
+ elif mechanism == b'CURVE':
+ # For CURVE, even a _alloweded address must authenticate
+ if len(credentials) != 1:
+ self.log.error("Invalid CURVE credentials: %r", credentials)
+ self._send_zap_reply(request_id, b"400", b"Invalid credentials")
+ return
+ key = credentials[0]
+ allowed, reason = await self._authenticate_curve(domain, key)
+ if allowed:
+ username = self.curve_user_id(key)
+
+ elif mechanism == b'GSSAPI':
+ if len(credentials) != 1:
+ self.log.error("Invalid GSSAPI credentials: %r", credentials)
+ self._send_zap_reply(request_id, b"400", b"Invalid credentials")
+ return
+ # use principal as user-id for now
+ principal = credentials[0]
+ username = principal.decode("utf8")
+ allowed, reason = self._authenticate_gssapi(domain, principal)
+
+ if allowed:
+ self._send_zap_reply(request_id, b"200", b"OK", username)
+ else:
+ self._send_zap_reply(request_id, b"400", reason)
+
+ def _authenticate_plain(
+ self, domain: str, username: str, password: str
+ ) -> Tuple[bool, bytes]:
+ """PLAIN ZAP authentication"""
+ allowed = False
+ reason = b""
+ if self.passwords:
+ # If no domain is not specified then use the default domain
+ if not domain:
+ domain = '*'
+
+ if domain in self.passwords:
+ if username in self.passwords[domain]:
+ if password == self.passwords[domain][username]:
+ allowed = True
+ else:
+ reason = b"Invalid password"
+ else:
+ reason = b"Invalid username"
+ else:
+ reason = b"Invalid domain"
+
+ if allowed:
+ self.log.debug(
+ "ALLOWED (PLAIN) domain=%s username=%s password=%s",
+ domain,
+ username,
+ password,
+ )
+ else:
+ self.log.debug("DENIED %s", reason)
+
+ else:
+ reason = b"No passwords defined"
+ self.log.debug("DENIED (PLAIN) %s", reason)
+
+ return allowed, reason
+
+ async def _authenticate_curve(
+ self, domain: str, client_key: bytes
+ ) -> Tuple[bool, bytes]:
+ """CURVE ZAP authentication"""
+ allowed = False
+ reason = b""
+ if self.allow_any:
+ allowed = True
+ reason = b"OK"
+ self.log.debug("ALLOWED (CURVE allow any client)")
+ elif self.credentials_providers != {}:
+ # If no explicit domain is specified then use the default domain
+ if not domain:
+ domain = '*'
+
+ if domain in self.credentials_providers:
+ z85_client_key = z85.encode(client_key)
+ # Callback to check if key is Allowed
+ r = self.credentials_providers[domain].callback(domain, z85_client_key)
+ if isinstance(r, Awaitable):
+ r = await r
+ if r:
+ allowed = True
+ reason = b"OK"
+ else:
+ reason = b"Unknown key"
+
+ status = "ALLOWED" if allowed else "DENIED"
+ self.log.debug(
+ "%s (CURVE auth_callback) domain=%s client_key=%s",
+ status,
+ domain,
+ z85_client_key,
+ )
+ else:
+ reason = b"Unknown domain"
+ else:
+ # If no explicit domain is specified then use the default domain
+ if not domain:
+ domain = '*'
+
+ if domain in self.certs:
+ # The certs dict stores keys in z85 format, convert binary key to z85 bytes
+ z85_client_key = z85.encode(client_key)
+ if self.certs[domain].get(z85_client_key):
+ allowed = True
+ reason = b"OK"
+ else:
+ reason = b"Unknown key"
+
+ status = "ALLOWED" if allowed else "DENIED"
+ self.log.debug(
+ "%s (CURVE) domain=%s client_key=%s",
+ status,
+ domain,
+ z85_client_key,
+ )
+ else:
+ reason = b"Unknown domain"
+
+ return allowed, reason
+
+ def _authenticate_gssapi(self, domain: str, principal: bytes) -> Tuple[bool, bytes]:
+ """Nothing to do for GSSAPI, which has already been handled by an external service."""
+ self.log.debug("ALLOWED (GSSAPI) domain=%s principal=%s", domain, principal)
+ return True, b'OK'
+
+ def _send_zap_reply(
+ self,
+ request_id: bytes,
+ status_code: bytes,
+ status_text: bytes,
+ user_id: str = 'anonymous',
+ ) -> None:
+ """Send a ZAP reply to finish the authentication."""
+ user_id = user_id if status_code == b'200' else b''
+ if isinstance(user_id, str):
+ user_id = user_id.encode(self.encoding, 'replace')
+ metadata = b'' # not currently used
+ self.log.debug("ZAP reply code=%s text=%s", status_code, status_text)
+ reply = [VERSION, request_id, status_code, status_text, user_id, metadata]
+ self.zap_socket.send_multipart(reply)
+
+
+__all__ = ['Authenticator', 'CURVE_ALLOW_ANY']
diff --git a/lib/python3.12/site-packages/zmq/auth/certs.py b/lib/python3.12/site-packages/zmq/auth/certs.py
new file mode 100644
index 0000000000000000000000000000000000000000..d60ae005dc111da80576adcdc378390ba6026527
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/auth/certs.py
@@ -0,0 +1,140 @@
+"""0MQ authentication related functions and classes."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import datetime
+import glob
+import os
+from typing import Dict, Optional, Tuple, Union
+
+import zmq
+
+_cert_secret_banner = """# **** Generated on {0} by pyzmq ****
+# ZeroMQ CURVE **Secret** Certificate
+# DO NOT PROVIDE THIS FILE TO OTHER USERS nor change its permissions.
+
+"""
+
+
+_cert_public_banner = """# **** Generated on {0} by pyzmq ****
+# ZeroMQ CURVE Public Certificate
+# Exchange securely, or use a secure mechanism to verify the contents
+# of this file after exchange. Store public certificates in your home
+# directory, in the .curve subdirectory.
+
+"""
+
+
+def _write_key_file(
+ key_filename: Union[str, os.PathLike],
+ banner: str,
+ public_key: Union[str, bytes],
+ secret_key: Optional[Union[str, bytes]] = None,
+ metadata: Optional[Dict[str, str]] = None,
+ encoding: str = 'utf-8',
+) -> None:
+ """Create a certificate file"""
+ if isinstance(public_key, bytes):
+ public_key = public_key.decode(encoding)
+ if isinstance(secret_key, bytes):
+ secret_key = secret_key.decode(encoding)
+ with open(key_filename, 'w', encoding='utf8') as f:
+ f.write(banner.format(datetime.datetime.now()))
+
+ f.write('metadata\n')
+ if metadata:
+ for k, v in metadata.items():
+ if isinstance(k, bytes):
+ k = k.decode(encoding)
+ if isinstance(v, bytes):
+ v = v.decode(encoding)
+ f.write(f" {k} = {v}\n")
+
+ f.write('curve\n')
+ f.write(f" public-key = \"{public_key}\"\n")
+
+ if secret_key:
+ f.write(f" secret-key = \"{secret_key}\"\n")
+
+
+def create_certificates(
+ key_dir: Union[str, os.PathLike],
+ name: str,
+ metadata: Optional[Dict[str, str]] = None,
+) -> Tuple[str, str]:
+ """Create zmq certificates.
+
+ Returns the file paths to the public and secret certificate files.
+ """
+ public_key, secret_key = zmq.curve_keypair()
+ base_filename = os.path.join(key_dir, name)
+ secret_key_file = f"{base_filename}.key_secret"
+ public_key_file = f"{base_filename}.key"
+ now = datetime.datetime.now()
+
+ _write_key_file(public_key_file, _cert_public_banner.format(now), public_key)
+
+ _write_key_file(
+ secret_key_file,
+ _cert_secret_banner.format(now),
+ public_key,
+ secret_key=secret_key,
+ metadata=metadata,
+ )
+
+ return public_key_file, secret_key_file
+
+
+def load_certificate(
+ filename: Union[str, os.PathLike],
+) -> Tuple[bytes, Optional[bytes]]:
+ """Load public and secret key from a zmq certificate.
+
+ Returns (public_key, secret_key)
+
+ If the certificate file only contains the public key,
+ secret_key will be None.
+
+ If there is no public key found in the file, ValueError will be raised.
+ """
+ public_key = None
+ secret_key = None
+ if not os.path.exists(filename):
+ raise OSError(f"Invalid certificate file: {filename}")
+
+ with open(filename, 'rb') as f:
+ for line in f:
+ line = line.strip()
+ if line.startswith(b'#'):
+ continue
+ if line.startswith(b'public-key'):
+ public_key = line.split(b"=", 1)[1].strip(b' \t\'"')
+ if line.startswith(b'secret-key'):
+ secret_key = line.split(b"=", 1)[1].strip(b' \t\'"')
+ if public_key and secret_key:
+ break
+
+ if public_key is None:
+ raise ValueError(f"No public key found in {filename}")
+
+ return public_key, secret_key
+
+
+def load_certificates(directory: Union[str, os.PathLike] = '.') -> Dict[bytes, bool]:
+ """Load public keys from all certificates in a directory"""
+ certs = {}
+ if not os.path.isdir(directory):
+ raise OSError(f"Invalid certificate directory: {directory}")
+ # Follow czmq pattern of public keys stored in *.key files.
+ glob_string = os.path.join(directory, "*.key")
+
+ cert_files = glob.glob(glob_string)
+ for cert_file in cert_files:
+ public_key, _ = load_certificate(cert_file)
+ if public_key:
+ certs[public_key] = True
+ return certs
+
+
+__all__ = ['create_certificates', 'load_certificate', 'load_certificates']
diff --git a/lib/python3.12/site-packages/zmq/auth/ioloop.py b/lib/python3.12/site-packages/zmq/auth/ioloop.py
new file mode 100644
index 0000000000000000000000000000000000000000..f87f068e7ef92004a7bbad93e77e445a607e0093
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/auth/ioloop.py
@@ -0,0 +1,48 @@
+"""ZAP Authenticator integrated with the tornado IOLoop.
+
+.. versionadded:: 14.1
+.. deprecated:: 25
+ Use asyncio.AsyncioAuthenticator instead.
+ Since tornado runs on asyncio, the asyncio authenticator
+ offers the same functionality in tornado.
+"""
+
+import warnings
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+from typing import Any, Optional
+
+import zmq
+
+from .asyncio import AsyncioAuthenticator
+
+warnings.warn(
+ "zmq.auth.ioloop.IOLoopAuthenticator is deprecated. Use zmq.auth.asyncio.AsyncioAuthenticator",
+ DeprecationWarning,
+ stacklevel=2,
+)
+
+
+class IOLoopAuthenticator(AsyncioAuthenticator):
+ """ZAP authentication for use in the tornado IOLoop"""
+
+ def __init__(
+ self,
+ context: Optional["zmq.Context"] = None,
+ encoding: str = 'utf-8',
+ log: Any = None,
+ io_loop: Any = None,
+ ):
+ loop = None
+ if io_loop is not None:
+ warnings.warn(
+ f"{self.__class__.__name__}(io_loop) is deprecated and ignored",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ loop = io_loop.asyncio_loop
+ super().__init__(context=context, encoding=encoding, log=log, loop=loop)
+
+
+__all__ = ['IOLoopAuthenticator']
diff --git a/lib/python3.12/site-packages/zmq/auth/thread.py b/lib/python3.12/site-packages/zmq/auth/thread.py
new file mode 100644
index 0000000000000000000000000000000000000000..a227c4bd5974f95e3b5b844a1ce0de34f9454f10
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/auth/thread.py
@@ -0,0 +1,139 @@
+"""ZAP Authenticator in a Python Thread.
+
+.. versionadded:: 14.1
+"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import asyncio
+from threading import Event, Thread
+from typing import Any, List, Optional
+
+import zmq
+import zmq.asyncio
+
+from .base import Authenticator
+
+
+class AuthenticationThread(Thread):
+ """A Thread for running a zmq Authenticator
+
+ This is run in the background by ThreadAuthenticator
+ """
+
+ pipe: zmq.Socket
+ loop: asyncio.AbstractEventLoop
+ authenticator: Authenticator
+ poller: Optional[zmq.asyncio.Poller] = None
+
+ def __init__(
+ self,
+ authenticator: Authenticator,
+ pipe: zmq.Socket,
+ ) -> None:
+ super().__init__(daemon=True)
+ self.authenticator = authenticator
+ self.log = authenticator.log
+ self.pipe = pipe
+
+ self.started = Event()
+
+ def run(self) -> None:
+ """Start the Authentication Agent thread task"""
+
+ loop = asyncio.new_event_loop()
+ try:
+ loop.run_until_complete(self._run())
+ finally:
+ if self.pipe:
+ self.pipe.close()
+ self.pipe = None # type: ignore
+
+ loop.close()
+
+ async def _run(self):
+ self.poller = zmq.asyncio.Poller()
+ self.poller.register(self.pipe, zmq.POLLIN)
+ self.poller.register(self.authenticator.zap_socket, zmq.POLLIN)
+ self.started.set()
+
+ while True:
+ events = dict(await self.poller.poll())
+ if self.pipe in events:
+ msg = self.pipe.recv_multipart()
+ if self._handle_pipe_message(msg):
+ return
+ if self.authenticator.zap_socket in events:
+ msg = self.authenticator.zap_socket.recv_multipart()
+ await self.authenticator.handle_zap_message(msg)
+
+ def _handle_pipe_message(self, msg: List[bytes]) -> bool:
+ command = msg[0]
+ self.log.debug("auth received API command %r", command)
+
+ if command == b'TERMINATE':
+ return True
+
+ else:
+ self.log.error("Invalid auth command from API: %r", command)
+ self.pipe.send(b'ERROR')
+
+ return False
+
+
+class ThreadAuthenticator(Authenticator):
+ """Run ZAP authentication in a background thread"""
+
+ pipe: "zmq.Socket"
+ pipe_endpoint: str = ''
+ thread: AuthenticationThread
+
+ def __init__(
+ self,
+ context: Optional["zmq.Context"] = None,
+ encoding: str = 'utf-8',
+ log: Any = None,
+ ):
+ super().__init__(context=context, encoding=encoding, log=log)
+ self.pipe = None # type: ignore
+ self.pipe_endpoint = f"inproc://{id(self)}.inproc"
+ self.thread = None # type: ignore
+
+ def start(self) -> None:
+ """Start the authentication thread"""
+ # start the Authenticator
+ super().start()
+
+ # create a socket pair to communicate with auth thread.
+ self.pipe = self.context.socket(zmq.PAIR, socket_class=zmq.Socket)
+ self.pipe.linger = 1
+ self.pipe.bind(self.pipe_endpoint)
+ thread_pipe = self.context.socket(zmq.PAIR, socket_class=zmq.Socket)
+ thread_pipe.linger = 1
+ thread_pipe.connect(self.pipe_endpoint)
+ self.thread = AuthenticationThread(authenticator=self, pipe=thread_pipe)
+ self.thread.start()
+ if not self.thread.started.wait(timeout=10):
+ raise RuntimeError("Authenticator thread failed to start")
+
+ def stop(self) -> None:
+ """Stop the authentication thread"""
+ if self.pipe:
+ self.pipe.send(b'TERMINATE')
+ if self.is_alive():
+ self.thread.join()
+ self.thread = None # type: ignore
+ self.pipe.close()
+ self.pipe = None # type: ignore
+ super().stop()
+
+ def is_alive(self) -> bool:
+ """Is the ZAP thread currently running?"""
+ return bool(self.thread and self.thread.is_alive())
+
+ def __del__(self) -> None:
+ self.stop()
+
+
+__all__ = ['ThreadAuthenticator']
diff --git a/lib/python3.12/site-packages/zmq/backend/__init__.py b/lib/python3.12/site-packages/zmq/backend/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..15f696d9d3ad5275443eb3145b9c4087f1e6609f
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/backend/__init__.py
@@ -0,0 +1,34 @@
+"""Import basic exposure of libzmq C API as a backend"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import os
+import platform
+
+from .select import public_api, select_backend
+
+if 'PYZMQ_BACKEND' in os.environ:
+ backend = os.environ['PYZMQ_BACKEND']
+ if backend in ('cython', 'cffi'):
+ backend = f'zmq.backend.{backend}'
+ _ns = select_backend(backend)
+else:
+ # default to cython, fallback to cffi
+ # (reverse on PyPy)
+ if platform.python_implementation() == 'PyPy':
+ first, second = ('zmq.backend.cffi', 'zmq.backend.cython')
+ else:
+ first, second = ('zmq.backend.cython', 'zmq.backend.cffi')
+
+ try:
+ _ns = select_backend(first)
+ except Exception as original_error:
+ try:
+ _ns = select_backend(second)
+ except ImportError:
+ raise original_error from None
+
+globals().update(_ns)
+
+__all__ = public_api
diff --git a/lib/python3.12/site-packages/zmq/backend/__init__.pyi b/lib/python3.12/site-packages/zmq/backend/__init__.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..72b6e802b63eabf6e5f9b3727086798e027abc76
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/backend/__init__.pyi
@@ -0,0 +1,122 @@
+from typing import Any, Callable, List, Optional, Set, Tuple, TypeVar, Union, overload
+
+from typing_extensions import Literal
+
+import zmq
+
+from .select import select_backend
+
+# avoid collision in Frame.bytes
+_bytestr = bytes
+
+T = TypeVar("T")
+
+class Frame:
+ buffer: Any
+ bytes: bytes
+ more: bool
+ tracker: Any
+ def __init__(
+ self,
+ data: Any = None,
+ track: bool = False,
+ copy: bool | None = None,
+ copy_threshold: int | None = None,
+ ): ...
+ def copy_fast(self: T) -> T: ...
+ def get(self, option: int) -> int | _bytestr | str: ...
+ def set(self, option: int, value: int | _bytestr | str) -> None: ...
+
+class Socket:
+ underlying: int
+ context: zmq.Context
+ copy_threshold: int
+
+ # specific option types
+ FD: int
+
+ def __init__(
+ self,
+ context: Context | None = None,
+ socket_type: int = 0,
+ shadow: int = 0,
+ copy_threshold: int | None = zmq.COPY_THRESHOLD,
+ ) -> None: ...
+ def close(self, linger: int | None = ...) -> None: ...
+ def get(self, option: int) -> int | bytes | str: ...
+ def set(self, option: int, value: int | bytes | str) -> None: ...
+ def connect(self, url: str): ...
+ def disconnect(self, url: str) -> None: ...
+ def bind(self, url: str): ...
+ def unbind(self, url: str) -> None: ...
+ def send(
+ self,
+ data: Any,
+ flags: int = ...,
+ copy: bool = ...,
+ track: bool = ...,
+ ) -> zmq.MessageTracker | None: ...
+ @overload
+ def recv(
+ self,
+ flags: int = ...,
+ *,
+ copy: Literal[False],
+ track: bool = ...,
+ ) -> zmq.Frame: ...
+ @overload
+ def recv(
+ self,
+ flags: int = ...,
+ *,
+ copy: Literal[True],
+ track: bool = ...,
+ ) -> bytes: ...
+ @overload
+ def recv(
+ self,
+ flags: int = ...,
+ track: bool = False,
+ ) -> bytes: ...
+ @overload
+ def recv(
+ self,
+ flags: int | None = ...,
+ copy: bool = ...,
+ track: bool | None = False,
+ ) -> zmq.Frame | bytes: ...
+ def recv_into(self, buf, /, *, nbytes: int = 0, flags: int = 0) -> int: ...
+ def monitor(self, addr: str | None, events: int) -> None: ...
+ # draft methods
+ def join(self, group: str) -> None: ...
+ def leave(self, group: str) -> None: ...
+
+class Context:
+ underlying: int
+ def __init__(self, io_threads: int = 1, shadow: int = 0): ...
+ def get(self, option: int) -> int | bytes | str: ...
+ def set(self, option: int, value: int | bytes | str) -> None: ...
+ def socket(self, socket_type: int) -> Socket: ...
+ def term(self) -> None: ...
+
+IPC_PATH_MAX_LEN: int
+
+def has(capability: str) -> bool: ...
+def curve_keypair() -> tuple[bytes, bytes]: ...
+def curve_public(secret_key: bytes) -> bytes: ...
+def strerror(errno: int | None = ...) -> str: ...
+def zmq_errno() -> int: ...
+def zmq_version() -> str: ...
+def zmq_version_info() -> tuple[int, int, int]: ...
+def zmq_poll(
+ sockets: list[Any], timeout: int | None = ...
+) -> list[tuple[Socket, int]]: ...
+def proxy(frontend: Socket, backend: Socket, capture: Socket | None = None) -> int: ...
+def proxy_steerable(
+ frontend: Socket,
+ backend: Socket,
+ capture: Socket | None = ...,
+ control: Socket | None = ...,
+) -> int: ...
+
+monitored_queue = Callable | None
diff --git a/lib/python3.12/site-packages/zmq/backend/cffi/__init__.py b/lib/python3.12/site-packages/zmq/backend/cffi/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce929a7a6a6bbe84c6a1cdd873095152837cd44c
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/backend/cffi/__init__.py
@@ -0,0 +1,38 @@
+"""CFFI backend (for PyPy)"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+# for clearer error message on missing cffi
+import cffi # noqa
+
+from zmq.backend.cffi import _poll, context, devices, error, message, socket, utils
+
+from ._cffi import ffi
+from ._cffi import lib as C
+
+
+def zmq_version_info():
+ """Get libzmq version as tuple of ints"""
+ major = ffi.new('int*')
+ minor = ffi.new('int*')
+ patch = ffi.new('int*')
+
+ C.zmq_version(major, minor, patch)
+
+ return (int(major[0]), int(minor[0]), int(patch[0]))
+
+
+__all__ = ["zmq_version_info"]
+for submod in (error, message, context, socket, _poll, devices, utils):
+ __all__.extend(submod.__all__)
+
+from ._poll import *
+from .context import *
+from .devices import *
+from .error import *
+from .message import *
+from .socket import *
+from .utils import *
+
+monitored_queue = None
diff --git a/lib/python3.12/site-packages/zmq/backend/cffi/_cdefs.h b/lib/python3.12/site-packages/zmq/backend/cffi/_cdefs.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a06b1519096453656d0b154b9d7936c9160baf6
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/backend/cffi/_cdefs.h
@@ -0,0 +1,90 @@
+void zmq_version(int *major, int *minor, int *patch);
+
+void* zmq_socket(void *context, int type);
+int zmq_close(void *socket);
+
+int zmq_bind(void *socket, const char *endpoint);
+int zmq_connect(void *socket, const char *endpoint);
+
+int zmq_errno(void);
+const char * zmq_strerror(int errnum);
+
+int zmq_unbind(void *socket, const char *endpoint);
+int zmq_disconnect(void *socket, const char *endpoint);
+void* zmq_ctx_new();
+int zmq_ctx_destroy(void *context);
+int zmq_ctx_get(void *context, int opt);
+int zmq_ctx_set(void *context, int opt, int optval);
+int zmq_proxy(void *frontend, void *backend, void *capture);
+int zmq_proxy_steerable(void *frontend,
+ void *backend,
+ void *capture,
+ void *control);
+int zmq_socket_monitor(void *socket, const char *addr, int events);
+
+int zmq_curve_keypair (char *z85_public_key, char *z85_secret_key);
+int zmq_curve_public (char *z85_public_key, char *z85_secret_key);
+int zmq_has (const char *capability);
+
+typedef struct { ...; } zmq_msg_t;
+typedef ... zmq_free_fn;
+
+int zmq_msg_init(zmq_msg_t *msg);
+int zmq_msg_init_size(zmq_msg_t *msg, size_t size);
+int zmq_msg_init_data(zmq_msg_t *msg,
+ void *data,
+ size_t size,
+ zmq_free_fn *ffn,
+ void *hint);
+
+size_t zmq_msg_size(zmq_msg_t *msg);
+void *zmq_msg_data(zmq_msg_t *msg);
+int zmq_msg_close(zmq_msg_t *msg);
+
+int zmq_msg_copy(zmq_msg_t *dst, zmq_msg_t *src);
+int zmq_msg_send(zmq_msg_t *msg, void *socket, int flags);
+int zmq_msg_recv(zmq_msg_t *msg, void *socket, int flags);
+int zmq_recv(void *socket, void *buf, int nbytes, int flags);
+
+int zmq_getsockopt(void *socket,
+ int option_name,
+ void *option_value,
+ size_t *option_len);
+
+int zmq_setsockopt(void *socket,
+ int option_name,
+ const void *option_value,
+ size_t option_len);
+
+typedef int... ZMQ_FD_T;
+
+typedef struct
+{
+ void *socket;
+ ZMQ_FD_T fd;
+ short events;
+ short revents;
+} zmq_pollitem_t;
+
+int zmq_poll(zmq_pollitem_t *items, int nitems, long timeout);
+
+// miscellany
+void * memcpy(void *restrict s1, const void *restrict s2, size_t n);
+void * malloc(size_t sz);
+void free(void *p);
+int get_ipc_path_max_len(void);
+
+typedef struct { ...; } mutex_t;
+
+typedef struct _zhint {
+ void *sock;
+ mutex_t *mutex;
+ size_t id;
+} zhint;
+
+mutex_t* mutex_allocate();
+
+int zmq_wrap_msg_init_data(zmq_msg_t *msg,
+ void *data,
+ size_t size,
+ void *hint);
diff --git a/lib/python3.12/site-packages/zmq/backend/cffi/_cffi_src.c b/lib/python3.12/site-packages/zmq/backend/cffi/_cffi_src.c
new file mode 100644
index 0000000000000000000000000000000000000000..691be3c782d3d8209dc67e12d241e749e08440cb
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/backend/cffi/_cffi_src.c
@@ -0,0 +1,50 @@
+#include
+#include
+
+#include "pyversion_compat.h"
+#include "mutex.h"
+#include "ipcmaxlen.h"
+#include "zmq_compat.h"
+#include
+
+typedef struct _zhint {
+ void *sock;
+ mutex_t *mutex;
+ size_t id;
+} zhint;
+
+void free_python_msg(void *data, void *vhint) {
+ zmq_msg_t msg;
+ zhint *hint = (zhint *)vhint;
+ int rc;
+ if (hint != NULL) {
+ zmq_msg_init_size(&msg, sizeof(size_t));
+ memcpy(zmq_msg_data(&msg), &hint->id, sizeof(size_t));
+ rc = mutex_lock(hint->mutex);
+ if (rc != 0) {
+ fprintf(stderr, "pyzmq-gc mutex lock failed rc=%d\n", rc);
+ }
+ rc = zmq_msg_send(&msg, hint->sock, 0);
+ if (rc < 0) {
+ /*
+ * gc socket could have been closed, e.g. during process teardown.
+ * If so, ignore the failure because there's nothing to do.
+ */
+ if (zmq_errno() != ENOTSOCK) {
+ fprintf(stderr, "pyzmq-gc send failed: %s\n",
+ zmq_strerror(zmq_errno()));
+ }
+ }
+ rc = mutex_unlock(hint->mutex);
+ if (rc != 0) {
+ fprintf(stderr, "pyzmq-gc mutex unlock failed rc=%d\n", rc);
+ }
+ zmq_msg_close(&msg);
+ free(hint);
+ }
+}
+
+int zmq_wrap_msg_init_data(zmq_msg_t *msg, void *data, size_t size,
+ void *hint) {
+ return zmq_msg_init_data(msg, data, size, free_python_msg, hint);
+}
diff --git a/lib/python3.12/site-packages/zmq/backend/cffi/devices.py b/lib/python3.12/site-packages/zmq/backend/cffi/devices.py
new file mode 100644
index 0000000000000000000000000000000000000000..a906be6e8709ce1e16ffedd231d2aff872978ba2
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/backend/cffi/devices.py
@@ -0,0 +1,59 @@
+"""zmq device functions"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from ._cffi import ffi
+from ._cffi import lib as C
+from .socket import Socket
+from .utils import _retry_sys_call
+
+
+def proxy(frontend, backend, capture=None):
+ if isinstance(capture, Socket):
+ capture = capture._zmq_socket
+ else:
+ capture = ffi.NULL
+
+ _retry_sys_call(C.zmq_proxy, frontend._zmq_socket, backend._zmq_socket, capture)
+
+
+def proxy_steerable(frontend, backend, capture=None, control=None):
+ """proxy_steerable(frontend, backend, capture, control)
+
+ Start a zeromq proxy with control flow.
+
+ .. versionadded:: libzmq-4.1
+ .. versionadded:: 18.0
+
+ Parameters
+ ----------
+ frontend : Socket
+ The Socket instance for the incoming traffic.
+ backend : Socket
+ The Socket instance for the outbound traffic.
+ capture : Socket (optional)
+ The Socket instance for capturing traffic.
+ control : Socket (optional)
+ The Socket instance for control flow.
+ """
+ if isinstance(capture, Socket):
+ capture = capture._zmq_socket
+ else:
+ capture = ffi.NULL
+
+ if isinstance(control, Socket):
+ control = control._zmq_socket
+ else:
+ control = ffi.NULL
+
+ _retry_sys_call(
+ C.zmq_proxy_steerable,
+ frontend._zmq_socket,
+ backend._zmq_socket,
+ capture,
+ control,
+ )
+
+
+__all__ = ['proxy', 'proxy_steerable']
diff --git a/lib/python3.12/site-packages/zmq/backend/cffi/message.py b/lib/python3.12/site-packages/zmq/backend/cffi/message.py
new file mode 100644
index 0000000000000000000000000000000000000000..94bb8c96fc7c2a79cd0b20dda2abca5643599c4b
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/backend/cffi/message.py
@@ -0,0 +1,225 @@
+"""Dummy Frame object"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import errno
+from threading import Event
+
+import zmq
+import zmq.error
+from zmq.constants import ETERM
+
+from ._cffi import ffi
+from ._cffi import lib as C
+
+zmq_gc = None
+
+try:
+ from __pypy__.bufferable import bufferable as maybe_bufferable
+except ImportError:
+ maybe_bufferable = object
+
+
+def _content(obj):
+ """Return content of obj as bytes"""
+ if type(obj) is bytes:
+ return obj
+ if not isinstance(obj, memoryview):
+ obj = memoryview(obj)
+ return obj.tobytes()
+
+
+def _check_rc(rc):
+ err = C.zmq_errno()
+ if rc == -1:
+ if err == errno.EINTR:
+ raise zmq.error.InterrruptedSystemCall(err)
+ elif err == errno.EAGAIN:
+ raise zmq.error.Again(errno)
+ elif err == ETERM:
+ raise zmq.error.ContextTerminated(err)
+ else:
+ raise zmq.error.ZMQError(err)
+ return 0
+
+
+class Frame(maybe_bufferable):
+ _data = None
+ tracker = None
+ closed = False
+ more = False
+ _buffer = None
+ _bytes = None
+ _failed_init = False
+ tracker_event = None
+ zmq_msg = None
+
+ def __init__(self, data=None, track=False, copy=None, copy_threshold=None):
+ self._failed_init = True
+
+ self.zmq_msg = ffi.cast('zmq_msg_t[1]', C.malloc(ffi.sizeof("zmq_msg_t")))
+
+ # self.tracker should start finished
+ # except in the case where we are sharing memory with libzmq
+ if track:
+ self.tracker = zmq._FINISHED_TRACKER
+
+ if isinstance(data, str):
+ raise TypeError(
+ "Unicode strings are not allowed. Only: bytes, buffer interfaces."
+ )
+
+ if data is None:
+ rc = C.zmq_msg_init(self.zmq_msg)
+ _check_rc(rc)
+ self._failed_init = False
+ return
+
+ self._data = data
+ if type(data) is bytes:
+ # avoid unnecessary copy on .bytes access
+ self._bytes = data
+
+ self._buffer = memoryview(data)
+ if not self._buffer.contiguous:
+ raise BufferError("memoryview: underlying buffer is not contiguous")
+ # from_buffer silently copies if memory is not contiguous
+ c_data = ffi.from_buffer(self._buffer)
+ data_len_c = self._buffer.nbytes
+
+ if copy is None:
+ if copy_threshold and data_len_c < copy_threshold:
+ copy = True
+ else:
+ copy = False
+
+ if copy:
+ # copy message data instead of sharing memory
+ rc = C.zmq_msg_init_size(self.zmq_msg, data_len_c)
+ _check_rc(rc)
+ ffi.buffer(C.zmq_msg_data(self.zmq_msg), data_len_c)[:] = self._buffer
+ self._failed_init = False
+ return
+
+ # Getting here means that we are doing a true zero-copy Frame,
+ # where libzmq and Python are sharing memory.
+ # Hook up garbage collection with MessageTracker and zmq_free_fn
+
+ # Event and MessageTracker for monitoring when zmq is done with data:
+ if track:
+ evt = Event()
+ self.tracker_event = evt
+ self.tracker = zmq.MessageTracker(evt)
+ # create the hint for zmq_free_fn
+ # two pointers: the zmq_gc context and a message to be sent to the zmq_gc PULL socket
+ # allows libzmq to signal to Python when it is done with Python-owned memory.
+ global zmq_gc
+ if zmq_gc is None:
+ from zmq.utils.garbage import gc as zmq_gc
+ # can't use ffi.new because it will be freed at the wrong time!
+ hint = ffi.cast("zhint[1]", C.malloc(ffi.sizeof("zhint")))
+ hint[0].id = zmq_gc.store(data, self.tracker_event)
+ if not zmq_gc._push_mutex:
+ zmq_gc._push_mutex = C.mutex_allocate()
+
+ hint[0].mutex = ffi.cast("mutex_t*", zmq_gc._push_mutex)
+ hint[0].sock = ffi.cast("void*", zmq_gc._push_socket.underlying)
+
+ # calls zmq_wrap_msg_init_data with the C.free_python_msg callback
+ rc = C.zmq_wrap_msg_init_data(
+ self.zmq_msg,
+ c_data,
+ data_len_c,
+ hint,
+ )
+ if rc != 0:
+ C.free(hint)
+ C.free(self.zmq_msg)
+ _check_rc(rc)
+ self._failed_init = False
+
+ def __del__(self):
+ if not self.closed and not self._failed_init:
+ self.close()
+
+ def close(self):
+ if self.closed or self._failed_init or self.zmq_msg is None:
+ return
+ self.closed = True
+ rc = C.zmq_msg_close(self.zmq_msg)
+ C.free(self.zmq_msg)
+ self.zmq_msg = None
+ if rc != 0:
+ _check_rc(rc)
+
+ def _buffer_from_zmq_msg(self):
+ """one-time extract buffer from zmq_msg
+
+ for Frames created by recv
+ """
+ if self._data is None:
+ self._data = ffi.buffer(
+ C.zmq_msg_data(self.zmq_msg), C.zmq_msg_size(self.zmq_msg)
+ )
+ if self._buffer is None:
+ self._buffer = memoryview(self._data)
+
+ @property
+ def buffer(self):
+ if self._buffer is None:
+ self._buffer_from_zmq_msg()
+ return self._buffer
+
+ @property
+ def bytes(self):
+ if self._bytes is None:
+ self._bytes = self.buffer.tobytes()
+ return self._bytes
+
+ def __len__(self):
+ return self.buffer.nbytes
+
+ def __eq__(self, other):
+ return self.bytes == _content(other)
+
+ @property
+ def done(self):
+ return self.tracker.done()
+
+ def __buffer__(self, flags):
+ return self.buffer
+
+ def __copy__(self):
+ """Create a shallow copy of the message.
+
+ This does not copy the contents of the Frame, just the pointer.
+ This will increment the 0MQ ref count of the message, but not
+ the ref count of the Python object. That is only done once when
+ the Python is first turned into a 0MQ message.
+ """
+ return self.fast_copy()
+
+ def fast_copy(self):
+ """Fast shallow copy of the Frame.
+
+ Does not copy underlying data.
+ """
+ new_msg = Frame()
+ # This does not copy the contents, but just increases the ref-count
+ # of the zmq_msg by one.
+ C.zmq_msg_copy(new_msg.zmq_msg, self.zmq_msg)
+ # Copy the ref to underlying data
+ new_msg._data = self._data
+ new_msg._buffer = self._buffer
+
+ # Frame copies share the tracker and tracker_event
+ new_msg.tracker_event = self.tracker_event
+ new_msg.tracker = self.tracker
+
+ return new_msg
+
+
+Message = Frame
+
+__all__ = ['Frame', 'Message']
diff --git a/lib/python3.12/site-packages/zmq/backend/cffi/utils.py b/lib/python3.12/site-packages/zmq/backend/cffi/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ebc8dea2ea7f6d82c88c15fe53632bde97393a5
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/backend/cffi/utils.py
@@ -0,0 +1,78 @@
+"""miscellaneous zmq_utils wrapping"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from zmq.error import InterruptedSystemCall, _check_rc, _check_version
+
+from ._cffi import ffi
+from ._cffi import lib as C
+
+
+def has(capability):
+ """Check for zmq capability by name (e.g. 'ipc', 'curve')
+
+ .. versionadded:: libzmq-4.1
+ .. versionadded:: 14.1
+ """
+ _check_version((4, 1), 'zmq.has')
+ if isinstance(capability, str):
+ capability = capability.encode('utf8')
+ return bool(C.zmq_has(capability))
+
+
+def curve_keypair():
+ """generate a Z85 key pair for use with zmq.CURVE security
+
+ Requires libzmq (≥ 4.0) to have been built with CURVE support.
+
+ Returns
+ -------
+ (public, secret) : two bytestrings
+ The public and private key pair as 40 byte z85-encoded bytestrings.
+ """
+ public = ffi.new('char[64]')
+ private = ffi.new('char[64]')
+ rc = C.zmq_curve_keypair(public, private)
+ _check_rc(rc)
+ return ffi.buffer(public)[:40], ffi.buffer(private)[:40]
+
+
+def curve_public(private):
+ """Compute the public key corresponding to a private key for use
+ with zmq.CURVE security
+
+ Requires libzmq (≥ 4.2) to have been built with CURVE support.
+
+ Parameters
+ ----------
+ private
+ The private key as a 40 byte z85-encoded bytestring
+ Returns
+ -------
+ bytestring
+ The public key as a 40 byte z85-encoded bytestring.
+ """
+ if isinstance(private, str):
+ private = private.encode('utf8')
+ _check_version((4, 2), "curve_public")
+ public = ffi.new('char[64]')
+ rc = C.zmq_curve_public(public, private)
+ _check_rc(rc)
+ return ffi.buffer(public)[:40]
+
+
+def _retry_sys_call(f, *args, **kwargs):
+ """make a call, retrying if interrupted with EINTR"""
+ while True:
+ rc = f(*args)
+ try:
+ _check_rc(rc)
+ except InterruptedSystemCall:
+ continue
+ else:
+ break
+ return rc
+
+
+__all__ = ['has', 'curve_keypair', 'curve_public']
diff --git a/lib/python3.12/site-packages/zmq/backend/select.py b/lib/python3.12/site-packages/zmq/backend/select.py
new file mode 100644
index 0000000000000000000000000000000000000000..ebc8b485200e921c348ae3f3ce0f3883256613d6
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/backend/select.py
@@ -0,0 +1,40 @@
+"""Import basic exposure of libzmq C API as a backend"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from importlib import import_module
+from typing import Dict
+
+public_api = [
+ 'Context',
+ 'Socket',
+ 'Frame',
+ 'Message',
+ 'proxy',
+ 'proxy_steerable',
+ 'zmq_poll',
+ 'strerror',
+ 'zmq_errno',
+ 'has',
+ 'curve_keypair',
+ 'curve_public',
+ 'zmq_version_info',
+ 'IPC_PATH_MAX_LEN',
+]
+
+
+def select_backend(name: str) -> Dict:
+ """Select the pyzmq backend"""
+ try:
+ mod = import_module(name)
+ except ImportError:
+ raise
+ except Exception as e:
+ raise ImportError(f"Importing {name} failed with {e}") from e
+ ns = {
+ # private API
+ 'monitored_queue': mod.monitored_queue,
+ }
+ ns.update({key: getattr(mod, key) for key in public_api})
+ return ns
diff --git a/lib/python3.12/site-packages/zmq/constants.py b/lib/python3.12/site-packages/zmq/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..cddf433a15d9102b9162425f8d8a54aa125f783e
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/constants.py
@@ -0,0 +1,974 @@
+"""zmq constants as enums"""
+
+from __future__ import annotations
+
+import errno
+import sys
+from enum import Enum, IntEnum, IntFlag
+
+_HAUSNUMERO = 156384712
+
+
+class Errno(IntEnum):
+ """libzmq error codes
+
+ .. versionadded:: 23
+ """
+
+ EAGAIN = errno.EAGAIN
+ EFAULT = errno.EFAULT
+ EINVAL = errno.EINVAL
+
+ if sys.platform.startswith("win"):
+ # Windows: libzmq uses errno.h
+ # while Python errno prefers WSA* variants
+ # many of these were introduced to errno.h in vs2010
+ # ref: https://github.com/python/cpython/blob/3.9/Modules/errnomodule.c#L10-L37
+ # source: https://docs.microsoft.com/en-us/cpp/c-runtime-library/errno-constants
+ ENOTSUP = 129
+ EPROTONOSUPPORT = 135
+ ENOBUFS = 119
+ ENETDOWN = 116
+ EADDRINUSE = 100
+ EADDRNOTAVAIL = 101
+ ECONNREFUSED = 107
+ EINPROGRESS = 112
+ ENOTSOCK = 128
+ EMSGSIZE = 115
+ EAFNOSUPPORT = 102
+ ENETUNREACH = 118
+ ECONNABORTED = 106
+ ECONNRESET = 108
+ ENOTCONN = 126
+ ETIMEDOUT = 138
+ EHOSTUNREACH = 110
+ ENETRESET = 117
+
+ else:
+ ENOTSUP = getattr(errno, "ENOTSUP", _HAUSNUMERO + 1)
+ EPROTONOSUPPORT = getattr(errno, "EPROTONOSUPPORT", _HAUSNUMERO + 2)
+ ENOBUFS = getattr(errno, "ENOBUFS", _HAUSNUMERO + 3)
+ ENETDOWN = getattr(errno, "ENETDOWN", _HAUSNUMERO + 4)
+ EADDRINUSE = getattr(errno, "EADDRINUSE", _HAUSNUMERO + 5)
+ EADDRNOTAVAIL = getattr(errno, "EADDRNOTAVAIL", _HAUSNUMERO + 6)
+ ECONNREFUSED = getattr(errno, "ECONNREFUSED", _HAUSNUMERO + 7)
+ EINPROGRESS = getattr(errno, "EINPROGRESS", _HAUSNUMERO + 8)
+ ENOTSOCK = getattr(errno, "ENOTSOCK", _HAUSNUMERO + 9)
+ EMSGSIZE = getattr(errno, "EMSGSIZE", _HAUSNUMERO + 10)
+ EAFNOSUPPORT = getattr(errno, "EAFNOSUPPORT", _HAUSNUMERO + 11)
+ ENETUNREACH = getattr(errno, "ENETUNREACH", _HAUSNUMERO + 12)
+ ECONNABORTED = getattr(errno, "ECONNABORTED", _HAUSNUMERO + 13)
+ ECONNRESET = getattr(errno, "ECONNRESET", _HAUSNUMERO + 14)
+ ENOTCONN = getattr(errno, "ENOTCONN", _HAUSNUMERO + 15)
+ ETIMEDOUT = getattr(errno, "ETIMEDOUT", _HAUSNUMERO + 16)
+ EHOSTUNREACH = getattr(errno, "EHOSTUNREACH", _HAUSNUMERO + 17)
+ ENETRESET = getattr(errno, "ENETRESET", _HAUSNUMERO + 18)
+
+ # Native 0MQ error codes
+ EFSM = _HAUSNUMERO + 51
+ ENOCOMPATPROTO = _HAUSNUMERO + 52
+ ETERM = _HAUSNUMERO + 53
+ EMTHREAD = _HAUSNUMERO + 54
+
+
+class ContextOption(IntEnum):
+ """Options for Context.get/set
+
+ .. versionadded:: 23
+ """
+
+ IO_THREADS = 1
+ MAX_SOCKETS = 2
+ SOCKET_LIMIT = 3
+ THREAD_PRIORITY = 3
+ THREAD_SCHED_POLICY = 4
+ MAX_MSGSZ = 5
+ MSG_T_SIZE = 6
+ THREAD_AFFINITY_CPU_ADD = 7
+ THREAD_AFFINITY_CPU_REMOVE = 8
+ THREAD_NAME_PREFIX = 9
+
+
+class SocketType(IntEnum):
+ """zmq socket types
+
+ .. versionadded:: 23
+ """
+
+ PAIR = 0
+ PUB = 1
+ SUB = 2
+ REQ = 3
+ REP = 4
+ DEALER = 5
+ ROUTER = 6
+ PULL = 7
+ PUSH = 8
+ XPUB = 9
+ XSUB = 10
+ STREAM = 11
+
+ # deprecated aliases
+ XREQ = DEALER
+ XREP = ROUTER
+
+ # DRAFT socket types
+ SERVER = 12
+ CLIENT = 13
+ RADIO = 14
+ DISH = 15
+ GATHER = 16
+ SCATTER = 17
+ DGRAM = 18
+ PEER = 19
+ CHANNEL = 20
+
+
+class _OptType(Enum):
+ int = 'int'
+ int64 = 'int64'
+ bytes = 'bytes'
+ fd = 'fd'
+
+
+class SocketOption(IntEnum):
+ """Options for Socket.get/set
+
+ .. versionadded:: 23
+ """
+
+ _opt_type: _OptType
+
+ def __new__(cls, value: int, opt_type: _OptType = _OptType.int):
+ """Attach option type as `._opt_type`"""
+ obj = int.__new__(cls, value)
+ obj._value_ = value
+ obj._opt_type = opt_type
+ return obj
+
+ HWM = 1
+ AFFINITY = 4, _OptType.int64
+ ROUTING_ID = 5, _OptType.bytes
+ SUBSCRIBE = 6, _OptType.bytes
+ UNSUBSCRIBE = 7, _OptType.bytes
+ RATE = 8
+ RECOVERY_IVL = 9
+ SNDBUF = 11
+ RCVBUF = 12
+ RCVMORE = 13
+ FD = 14, _OptType.fd
+ EVENTS = 15
+ TYPE = 16
+ LINGER = 17
+ RECONNECT_IVL = 18
+ BACKLOG = 19
+ RECONNECT_IVL_MAX = 21
+ MAXMSGSIZE = 22, _OptType.int64
+ SNDHWM = 23
+ RCVHWM = 24
+ MULTICAST_HOPS = 25
+ RCVTIMEO = 27
+ SNDTIMEO = 28
+ LAST_ENDPOINT = 32, _OptType.bytes
+ ROUTER_MANDATORY = 33
+ TCP_KEEPALIVE = 34
+ TCP_KEEPALIVE_CNT = 35
+ TCP_KEEPALIVE_IDLE = 36
+ TCP_KEEPALIVE_INTVL = 37
+ IMMEDIATE = 39
+ XPUB_VERBOSE = 40
+ ROUTER_RAW = 41
+ IPV6 = 42
+ MECHANISM = 43
+ PLAIN_SERVER = 44
+ PLAIN_USERNAME = 45, _OptType.bytes
+ PLAIN_PASSWORD = 46, _OptType.bytes
+ CURVE_SERVER = 47
+ CURVE_PUBLICKEY = 48, _OptType.bytes
+ CURVE_SECRETKEY = 49, _OptType.bytes
+ CURVE_SERVERKEY = 50, _OptType.bytes
+ PROBE_ROUTER = 51
+ REQ_CORRELATE = 52
+ REQ_RELAXED = 53
+ CONFLATE = 54
+ ZAP_DOMAIN = 55, _OptType.bytes
+ ROUTER_HANDOVER = 56
+ TOS = 57
+ CONNECT_ROUTING_ID = 61, _OptType.bytes
+ GSSAPI_SERVER = 62
+ GSSAPI_PRINCIPAL = 63, _OptType.bytes
+ GSSAPI_SERVICE_PRINCIPAL = 64, _OptType.bytes
+ GSSAPI_PLAINTEXT = 65
+ HANDSHAKE_IVL = 66
+ SOCKS_PROXY = 68, _OptType.bytes
+ XPUB_NODROP = 69
+ BLOCKY = 70
+ XPUB_MANUAL = 71
+ XPUB_WELCOME_MSG = 72, _OptType.bytes
+ STREAM_NOTIFY = 73
+ INVERT_MATCHING = 74
+ HEARTBEAT_IVL = 75
+ HEARTBEAT_TTL = 76
+ HEARTBEAT_TIMEOUT = 77
+ XPUB_VERBOSER = 78
+ CONNECT_TIMEOUT = 79
+ TCP_MAXRT = 80
+ THREAD_SAFE = 81
+ MULTICAST_MAXTPDU = 84
+ VMCI_BUFFER_SIZE = 85, _OptType.int64
+ VMCI_BUFFER_MIN_SIZE = 86, _OptType.int64
+ VMCI_BUFFER_MAX_SIZE = 87, _OptType.int64
+ VMCI_CONNECT_TIMEOUT = 88
+ USE_FD = 89
+ GSSAPI_PRINCIPAL_NAMETYPE = 90
+ GSSAPI_SERVICE_PRINCIPAL_NAMETYPE = 91
+ BINDTODEVICE = 92, _OptType.bytes
+
+ # Deprecated options and aliases
+ # must not use name-assignment, must have the same value
+ IDENTITY = ROUTING_ID
+ CONNECT_RID = CONNECT_ROUTING_ID
+ TCP_ACCEPT_FILTER = 38, _OptType.bytes
+ IPC_FILTER_PID = 58
+ IPC_FILTER_UID = 59
+ IPC_FILTER_GID = 60
+ IPV4ONLY = 31
+ DELAY_ATTACH_ON_CONNECT = IMMEDIATE
+ FAIL_UNROUTABLE = ROUTER_MANDATORY
+ ROUTER_BEHAVIOR = ROUTER_MANDATORY
+
+ # Draft socket options
+ ZAP_ENFORCE_DOMAIN = 93
+ LOOPBACK_FASTPATH = 94
+ METADATA = 95, _OptType.bytes
+ MULTICAST_LOOP = 96
+ ROUTER_NOTIFY = 97
+ XPUB_MANUAL_LAST_VALUE = 98
+ SOCKS_USERNAME = 99, _OptType.bytes
+ SOCKS_PASSWORD = 100, _OptType.bytes
+ IN_BATCH_SIZE = 101
+ OUT_BATCH_SIZE = 102
+ WSS_KEY_PEM = 103, _OptType.bytes
+ WSS_CERT_PEM = 104, _OptType.bytes
+ WSS_TRUST_PEM = 105, _OptType.bytes
+ WSS_HOSTNAME = 106, _OptType.bytes
+ WSS_TRUST_SYSTEM = 107
+ ONLY_FIRST_SUBSCRIBE = 108
+ RECONNECT_STOP = 109
+ HELLO_MSG = 110, _OptType.bytes
+ DISCONNECT_MSG = 111, _OptType.bytes
+ PRIORITY = 112
+ # 4.3.5
+ BUSY_POLL = 113
+ HICCUP_MSG = 114, _OptType.bytes
+ XSUB_VERBOSE_UNSUBSCRIBE = 115
+ TOPICS_COUNT = 116
+ NORM_MODE = 117
+ NORM_UNICAST_NACK = 118
+ NORM_BUFFER_SIZE = 119
+ NORM_SEGMENT_SIZE = 120
+ NORM_BLOCK_SIZE = 121
+ NORM_NUM_PARITY = 122
+ NORM_NUM_AUTOPARITY = 123
+ NORM_PUSH = 124
+
+
+class MessageOption(IntEnum):
+ """Options on zmq.Frame objects
+
+ .. versionadded:: 23
+ """
+
+ MORE = 1
+ SHARED = 3
+ # Deprecated message options
+ SRCFD = 2
+
+
+class Flag(IntFlag):
+ """Send/recv flags
+
+ .. versionadded:: 23
+ """
+
+ DONTWAIT = 1
+ SNDMORE = 2
+ NOBLOCK = DONTWAIT
+
+
+class RouterNotify(IntEnum):
+ """Values for zmq.ROUTER_NOTIFY socket option
+
+ .. versionadded:: 26
+ .. versionadded:: libzmq-4.3.0 (draft)
+ """
+
+ @staticmethod
+ def _global_name(name):
+ return f"NOTIFY_{name}"
+
+ CONNECT = 1
+ DISCONNECT = 2
+
+
+class NormMode(IntEnum):
+ """Values for zmq.NORM_MODE socket option
+
+ .. versionadded:: 26
+ .. versionadded:: libzmq-4.3.5 (draft)
+ """
+
+ @staticmethod
+ def _global_name(name):
+ return f"NORM_{name}"
+
+ FIXED = 0
+ CC = 1
+ CCL = 2
+ CCE = 3
+ CCE_ECNONLY = 4
+
+
+class SecurityMechanism(IntEnum):
+ """Security mechanisms (as returned by ``socket.get(zmq.MECHANISM)``)
+
+ .. versionadded:: 23
+ """
+
+ NULL = 0
+ PLAIN = 1
+ CURVE = 2
+ GSSAPI = 3
+
+
+class ReconnectStop(IntEnum):
+ """Select behavior for socket.reconnect_stop
+
+ .. versionadded:: 25
+ """
+
+ @staticmethod
+ def _global_name(name):
+ return f"RECONNECT_STOP_{name}"
+
+ CONN_REFUSED = 0x1
+ HANDSHAKE_FAILED = 0x2
+ AFTER_DISCONNECT = 0x4
+
+
+class Event(IntFlag):
+ """Socket monitoring events
+
+ .. versionadded:: 23
+ """
+
+ @staticmethod
+ def _global_name(name):
+ if name.startswith("PROTOCOL_ERROR_"):
+ return name
+ else:
+ # add EVENT_ prefix
+ return "EVENT_" + name
+
+ PROTOCOL_ERROR_WS_UNSPECIFIED = 0x30000000
+ PROTOCOL_ERROR_ZMTP_UNSPECIFIED = 0x10000000
+ PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND = 0x10000001
+ PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE = 0x10000002
+ PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE = 0x10000003
+ PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED = 0x10000011
+ PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE = 0x10000012
+ PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO = 0x10000013
+ PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE = 0x10000014
+ PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR = 0x10000015
+ PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY = 0x10000016
+ PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME = 0x10000017
+ PROTOCOL_ERROR_ZMTP_INVALID_METADATA = 0x10000018
+
+ PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC = 0x11000001
+ PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH = 0x11000002
+ PROTOCOL_ERROR_ZAP_UNSPECIFIED = 0x20000000
+ PROTOCOL_ERROR_ZAP_MALFORMED_REPLY = 0x20000001
+ PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID = 0x20000002
+ PROTOCOL_ERROR_ZAP_BAD_VERSION = 0x20000003
+ PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE = 0x20000004
+ PROTOCOL_ERROR_ZAP_INVALID_METADATA = 0x20000005
+
+ # define event types _after_ overlapping protocol error masks
+ CONNECTED = 0x0001
+ CONNECT_DELAYED = 0x0002
+ CONNECT_RETRIED = 0x0004
+ LISTENING = 0x0008
+ BIND_FAILED = 0x0010
+ ACCEPTED = 0x0020
+ ACCEPT_FAILED = 0x0040
+ CLOSED = 0x0080
+ CLOSE_FAILED = 0x0100
+ DISCONNECTED = 0x0200
+ MONITOR_STOPPED = 0x0400
+
+ HANDSHAKE_FAILED_NO_DETAIL = 0x0800
+ HANDSHAKE_SUCCEEDED = 0x1000
+ HANDSHAKE_FAILED_PROTOCOL = 0x2000
+ HANDSHAKE_FAILED_AUTH = 0x4000
+
+ ALL_V1 = 0xFFFF
+ ALL = ALL_V1
+
+ # DRAFT Socket monitoring events
+ PIPES_STATS = 0x10000
+ ALL_V2 = ALL_V1 | PIPES_STATS
+
+
+class PollEvent(IntFlag):
+ """Which events to poll for in poll methods
+
+ .. versionadded: 23
+ """
+
+ POLLIN = 1
+ POLLOUT = 2
+ POLLERR = 4
+ POLLPRI = 8
+
+
+class DeviceType(IntEnum):
+ """Device type constants for zmq.device
+
+ .. versionadded: 23
+ """
+
+ STREAMER = 1
+ FORWARDER = 2
+ QUEUE = 3
+
+
+# AUTOGENERATED_BELOW_HERE
+
+
+IO_THREADS: int = ContextOption.IO_THREADS
+MAX_SOCKETS: int = ContextOption.MAX_SOCKETS
+SOCKET_LIMIT: int = ContextOption.SOCKET_LIMIT
+THREAD_PRIORITY: int = ContextOption.THREAD_PRIORITY
+THREAD_SCHED_POLICY: int = ContextOption.THREAD_SCHED_POLICY
+MAX_MSGSZ: int = ContextOption.MAX_MSGSZ
+MSG_T_SIZE: int = ContextOption.MSG_T_SIZE
+THREAD_AFFINITY_CPU_ADD: int = ContextOption.THREAD_AFFINITY_CPU_ADD
+THREAD_AFFINITY_CPU_REMOVE: int = ContextOption.THREAD_AFFINITY_CPU_REMOVE
+THREAD_NAME_PREFIX: int = ContextOption.THREAD_NAME_PREFIX
+STREAMER: int = DeviceType.STREAMER
+FORWARDER: int = DeviceType.FORWARDER
+QUEUE: int = DeviceType.QUEUE
+EAGAIN: int = Errno.EAGAIN
+EFAULT: int = Errno.EFAULT
+EINVAL: int = Errno.EINVAL
+ENOTSUP: int = Errno.ENOTSUP
+EPROTONOSUPPORT: int = Errno.EPROTONOSUPPORT
+ENOBUFS: int = Errno.ENOBUFS
+ENETDOWN: int = Errno.ENETDOWN
+EADDRINUSE: int = Errno.EADDRINUSE
+EADDRNOTAVAIL: int = Errno.EADDRNOTAVAIL
+ECONNREFUSED: int = Errno.ECONNREFUSED
+EINPROGRESS: int = Errno.EINPROGRESS
+ENOTSOCK: int = Errno.ENOTSOCK
+EMSGSIZE: int = Errno.EMSGSIZE
+EAFNOSUPPORT: int = Errno.EAFNOSUPPORT
+ENETUNREACH: int = Errno.ENETUNREACH
+ECONNABORTED: int = Errno.ECONNABORTED
+ECONNRESET: int = Errno.ECONNRESET
+ENOTCONN: int = Errno.ENOTCONN
+ETIMEDOUT: int = Errno.ETIMEDOUT
+EHOSTUNREACH: int = Errno.EHOSTUNREACH
+ENETRESET: int = Errno.ENETRESET
+EFSM: int = Errno.EFSM
+ENOCOMPATPROTO: int = Errno.ENOCOMPATPROTO
+ETERM: int = Errno.ETERM
+EMTHREAD: int = Errno.EMTHREAD
+PROTOCOL_ERROR_WS_UNSPECIFIED: int = Event.PROTOCOL_ERROR_WS_UNSPECIFIED
+PROTOCOL_ERROR_ZMTP_UNSPECIFIED: int = Event.PROTOCOL_ERROR_ZMTP_UNSPECIFIED
+PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND: int = (
+ Event.PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND
+)
+PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE: int = Event.PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE
+PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE: int = Event.PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE
+PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED: int = (
+ Event.PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED
+)
+PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE: int = (
+ Event.PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE
+)
+PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO: int = (
+ Event.PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO
+)
+PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE: int = (
+ Event.PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE
+)
+PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR: int = (
+ Event.PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR
+)
+PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY: int = (
+ Event.PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY
+)
+PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME: int = (
+ Event.PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME
+)
+PROTOCOL_ERROR_ZMTP_INVALID_METADATA: int = Event.PROTOCOL_ERROR_ZMTP_INVALID_METADATA
+PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC: int = Event.PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC
+PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH: int = (
+ Event.PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH
+)
+PROTOCOL_ERROR_ZAP_UNSPECIFIED: int = Event.PROTOCOL_ERROR_ZAP_UNSPECIFIED
+PROTOCOL_ERROR_ZAP_MALFORMED_REPLY: int = Event.PROTOCOL_ERROR_ZAP_MALFORMED_REPLY
+PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID: int = Event.PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID
+PROTOCOL_ERROR_ZAP_BAD_VERSION: int = Event.PROTOCOL_ERROR_ZAP_BAD_VERSION
+PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE: int = (
+ Event.PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE
+)
+PROTOCOL_ERROR_ZAP_INVALID_METADATA: int = Event.PROTOCOL_ERROR_ZAP_INVALID_METADATA
+EVENT_CONNECTED: int = Event.CONNECTED
+EVENT_CONNECT_DELAYED: int = Event.CONNECT_DELAYED
+EVENT_CONNECT_RETRIED: int = Event.CONNECT_RETRIED
+EVENT_LISTENING: int = Event.LISTENING
+EVENT_BIND_FAILED: int = Event.BIND_FAILED
+EVENT_ACCEPTED: int = Event.ACCEPTED
+EVENT_ACCEPT_FAILED: int = Event.ACCEPT_FAILED
+EVENT_CLOSED: int = Event.CLOSED
+EVENT_CLOSE_FAILED: int = Event.CLOSE_FAILED
+EVENT_DISCONNECTED: int = Event.DISCONNECTED
+EVENT_MONITOR_STOPPED: int = Event.MONITOR_STOPPED
+EVENT_HANDSHAKE_FAILED_NO_DETAIL: int = Event.HANDSHAKE_FAILED_NO_DETAIL
+EVENT_HANDSHAKE_SUCCEEDED: int = Event.HANDSHAKE_SUCCEEDED
+EVENT_HANDSHAKE_FAILED_PROTOCOL: int = Event.HANDSHAKE_FAILED_PROTOCOL
+EVENT_HANDSHAKE_FAILED_AUTH: int = Event.HANDSHAKE_FAILED_AUTH
+EVENT_ALL_V1: int = Event.ALL_V1
+EVENT_ALL: int = Event.ALL
+EVENT_PIPES_STATS: int = Event.PIPES_STATS
+EVENT_ALL_V2: int = Event.ALL_V2
+DONTWAIT: int = Flag.DONTWAIT
+SNDMORE: int = Flag.SNDMORE
+NOBLOCK: int = Flag.NOBLOCK
+MORE: int = MessageOption.MORE
+SHARED: int = MessageOption.SHARED
+SRCFD: int = MessageOption.SRCFD
+NORM_FIXED: int = NormMode.FIXED
+NORM_CC: int = NormMode.CC
+NORM_CCL: int = NormMode.CCL
+NORM_CCE: int = NormMode.CCE
+NORM_CCE_ECNONLY: int = NormMode.CCE_ECNONLY
+POLLIN: int = PollEvent.POLLIN
+POLLOUT: int = PollEvent.POLLOUT
+POLLERR: int = PollEvent.POLLERR
+POLLPRI: int = PollEvent.POLLPRI
+RECONNECT_STOP_CONN_REFUSED: int = ReconnectStop.CONN_REFUSED
+RECONNECT_STOP_HANDSHAKE_FAILED: int = ReconnectStop.HANDSHAKE_FAILED
+RECONNECT_STOP_AFTER_DISCONNECT: int = ReconnectStop.AFTER_DISCONNECT
+NOTIFY_CONNECT: int = RouterNotify.CONNECT
+NOTIFY_DISCONNECT: int = RouterNotify.DISCONNECT
+NULL: int = SecurityMechanism.NULL
+PLAIN: int = SecurityMechanism.PLAIN
+CURVE: int = SecurityMechanism.CURVE
+GSSAPI: int = SecurityMechanism.GSSAPI
+HWM: int = SocketOption.HWM
+AFFINITY: int = SocketOption.AFFINITY
+ROUTING_ID: int = SocketOption.ROUTING_ID
+SUBSCRIBE: int = SocketOption.SUBSCRIBE
+UNSUBSCRIBE: int = SocketOption.UNSUBSCRIBE
+RATE: int = SocketOption.RATE
+RECOVERY_IVL: int = SocketOption.RECOVERY_IVL
+SNDBUF: int = SocketOption.SNDBUF
+RCVBUF: int = SocketOption.RCVBUF
+RCVMORE: int = SocketOption.RCVMORE
+FD: int = SocketOption.FD
+EVENTS: int = SocketOption.EVENTS
+TYPE: int = SocketOption.TYPE
+LINGER: int = SocketOption.LINGER
+RECONNECT_IVL: int = SocketOption.RECONNECT_IVL
+BACKLOG: int = SocketOption.BACKLOG
+RECONNECT_IVL_MAX: int = SocketOption.RECONNECT_IVL_MAX
+MAXMSGSIZE: int = SocketOption.MAXMSGSIZE
+SNDHWM: int = SocketOption.SNDHWM
+RCVHWM: int = SocketOption.RCVHWM
+MULTICAST_HOPS: int = SocketOption.MULTICAST_HOPS
+RCVTIMEO: int = SocketOption.RCVTIMEO
+SNDTIMEO: int = SocketOption.SNDTIMEO
+LAST_ENDPOINT: int = SocketOption.LAST_ENDPOINT
+ROUTER_MANDATORY: int = SocketOption.ROUTER_MANDATORY
+TCP_KEEPALIVE: int = SocketOption.TCP_KEEPALIVE
+TCP_KEEPALIVE_CNT: int = SocketOption.TCP_KEEPALIVE_CNT
+TCP_KEEPALIVE_IDLE: int = SocketOption.TCP_KEEPALIVE_IDLE
+TCP_KEEPALIVE_INTVL: int = SocketOption.TCP_KEEPALIVE_INTVL
+IMMEDIATE: int = SocketOption.IMMEDIATE
+XPUB_VERBOSE: int = SocketOption.XPUB_VERBOSE
+ROUTER_RAW: int = SocketOption.ROUTER_RAW
+IPV6: int = SocketOption.IPV6
+MECHANISM: int = SocketOption.MECHANISM
+PLAIN_SERVER: int = SocketOption.PLAIN_SERVER
+PLAIN_USERNAME: int = SocketOption.PLAIN_USERNAME
+PLAIN_PASSWORD: int = SocketOption.PLAIN_PASSWORD
+CURVE_SERVER: int = SocketOption.CURVE_SERVER
+CURVE_PUBLICKEY: int = SocketOption.CURVE_PUBLICKEY
+CURVE_SECRETKEY: int = SocketOption.CURVE_SECRETKEY
+CURVE_SERVERKEY: int = SocketOption.CURVE_SERVERKEY
+PROBE_ROUTER: int = SocketOption.PROBE_ROUTER
+REQ_CORRELATE: int = SocketOption.REQ_CORRELATE
+REQ_RELAXED: int = SocketOption.REQ_RELAXED
+CONFLATE: int = SocketOption.CONFLATE
+ZAP_DOMAIN: int = SocketOption.ZAP_DOMAIN
+ROUTER_HANDOVER: int = SocketOption.ROUTER_HANDOVER
+TOS: int = SocketOption.TOS
+CONNECT_ROUTING_ID: int = SocketOption.CONNECT_ROUTING_ID
+GSSAPI_SERVER: int = SocketOption.GSSAPI_SERVER
+GSSAPI_PRINCIPAL: int = SocketOption.GSSAPI_PRINCIPAL
+GSSAPI_SERVICE_PRINCIPAL: int = SocketOption.GSSAPI_SERVICE_PRINCIPAL
+GSSAPI_PLAINTEXT: int = SocketOption.GSSAPI_PLAINTEXT
+HANDSHAKE_IVL: int = SocketOption.HANDSHAKE_IVL
+SOCKS_PROXY: int = SocketOption.SOCKS_PROXY
+XPUB_NODROP: int = SocketOption.XPUB_NODROP
+BLOCKY: int = SocketOption.BLOCKY
+XPUB_MANUAL: int = SocketOption.XPUB_MANUAL
+XPUB_WELCOME_MSG: int = SocketOption.XPUB_WELCOME_MSG
+STREAM_NOTIFY: int = SocketOption.STREAM_NOTIFY
+INVERT_MATCHING: int = SocketOption.INVERT_MATCHING
+HEARTBEAT_IVL: int = SocketOption.HEARTBEAT_IVL
+HEARTBEAT_TTL: int = SocketOption.HEARTBEAT_TTL
+HEARTBEAT_TIMEOUT: int = SocketOption.HEARTBEAT_TIMEOUT
+XPUB_VERBOSER: int = SocketOption.XPUB_VERBOSER
+CONNECT_TIMEOUT: int = SocketOption.CONNECT_TIMEOUT
+TCP_MAXRT: int = SocketOption.TCP_MAXRT
+THREAD_SAFE: int = SocketOption.THREAD_SAFE
+MULTICAST_MAXTPDU: int = SocketOption.MULTICAST_MAXTPDU
+VMCI_BUFFER_SIZE: int = SocketOption.VMCI_BUFFER_SIZE
+VMCI_BUFFER_MIN_SIZE: int = SocketOption.VMCI_BUFFER_MIN_SIZE
+VMCI_BUFFER_MAX_SIZE: int = SocketOption.VMCI_BUFFER_MAX_SIZE
+VMCI_CONNECT_TIMEOUT: int = SocketOption.VMCI_CONNECT_TIMEOUT
+USE_FD: int = SocketOption.USE_FD
+GSSAPI_PRINCIPAL_NAMETYPE: int = SocketOption.GSSAPI_PRINCIPAL_NAMETYPE
+GSSAPI_SERVICE_PRINCIPAL_NAMETYPE: int = SocketOption.GSSAPI_SERVICE_PRINCIPAL_NAMETYPE
+BINDTODEVICE: int = SocketOption.BINDTODEVICE
+IDENTITY: int = SocketOption.IDENTITY
+CONNECT_RID: int = SocketOption.CONNECT_RID
+TCP_ACCEPT_FILTER: int = SocketOption.TCP_ACCEPT_FILTER
+IPC_FILTER_PID: int = SocketOption.IPC_FILTER_PID
+IPC_FILTER_UID: int = SocketOption.IPC_FILTER_UID
+IPC_FILTER_GID: int = SocketOption.IPC_FILTER_GID
+IPV4ONLY: int = SocketOption.IPV4ONLY
+DELAY_ATTACH_ON_CONNECT: int = SocketOption.DELAY_ATTACH_ON_CONNECT
+FAIL_UNROUTABLE: int = SocketOption.FAIL_UNROUTABLE
+ROUTER_BEHAVIOR: int = SocketOption.ROUTER_BEHAVIOR
+ZAP_ENFORCE_DOMAIN: int = SocketOption.ZAP_ENFORCE_DOMAIN
+LOOPBACK_FASTPATH: int = SocketOption.LOOPBACK_FASTPATH
+METADATA: int = SocketOption.METADATA
+MULTICAST_LOOP: int = SocketOption.MULTICAST_LOOP
+ROUTER_NOTIFY: int = SocketOption.ROUTER_NOTIFY
+XPUB_MANUAL_LAST_VALUE: int = SocketOption.XPUB_MANUAL_LAST_VALUE
+SOCKS_USERNAME: int = SocketOption.SOCKS_USERNAME
+SOCKS_PASSWORD: int = SocketOption.SOCKS_PASSWORD
+IN_BATCH_SIZE: int = SocketOption.IN_BATCH_SIZE
+OUT_BATCH_SIZE: int = SocketOption.OUT_BATCH_SIZE
+WSS_KEY_PEM: int = SocketOption.WSS_KEY_PEM
+WSS_CERT_PEM: int = SocketOption.WSS_CERT_PEM
+WSS_TRUST_PEM: int = SocketOption.WSS_TRUST_PEM
+WSS_HOSTNAME: int = SocketOption.WSS_HOSTNAME
+WSS_TRUST_SYSTEM: int = SocketOption.WSS_TRUST_SYSTEM
+ONLY_FIRST_SUBSCRIBE: int = SocketOption.ONLY_FIRST_SUBSCRIBE
+RECONNECT_STOP: int = SocketOption.RECONNECT_STOP
+HELLO_MSG: int = SocketOption.HELLO_MSG
+DISCONNECT_MSG: int = SocketOption.DISCONNECT_MSG
+PRIORITY: int = SocketOption.PRIORITY
+BUSY_POLL: int = SocketOption.BUSY_POLL
+HICCUP_MSG: int = SocketOption.HICCUP_MSG
+XSUB_VERBOSE_UNSUBSCRIBE: int = SocketOption.XSUB_VERBOSE_UNSUBSCRIBE
+TOPICS_COUNT: int = SocketOption.TOPICS_COUNT
+NORM_MODE: int = SocketOption.NORM_MODE
+NORM_UNICAST_NACK: int = SocketOption.NORM_UNICAST_NACK
+NORM_BUFFER_SIZE: int = SocketOption.NORM_BUFFER_SIZE
+NORM_SEGMENT_SIZE: int = SocketOption.NORM_SEGMENT_SIZE
+NORM_BLOCK_SIZE: int = SocketOption.NORM_BLOCK_SIZE
+NORM_NUM_PARITY: int = SocketOption.NORM_NUM_PARITY
+NORM_NUM_AUTOPARITY: int = SocketOption.NORM_NUM_AUTOPARITY
+NORM_PUSH: int = SocketOption.NORM_PUSH
+PAIR: int = SocketType.PAIR
+PUB: int = SocketType.PUB
+SUB: int = SocketType.SUB
+REQ: int = SocketType.REQ
+REP: int = SocketType.REP
+DEALER: int = SocketType.DEALER
+ROUTER: int = SocketType.ROUTER
+PULL: int = SocketType.PULL
+PUSH: int = SocketType.PUSH
+XPUB: int = SocketType.XPUB
+XSUB: int = SocketType.XSUB
+STREAM: int = SocketType.STREAM
+XREQ: int = SocketType.XREQ
+XREP: int = SocketType.XREP
+SERVER: int = SocketType.SERVER
+CLIENT: int = SocketType.CLIENT
+RADIO: int = SocketType.RADIO
+DISH: int = SocketType.DISH
+GATHER: int = SocketType.GATHER
+SCATTER: int = SocketType.SCATTER
+DGRAM: int = SocketType.DGRAM
+PEER: int = SocketType.PEER
+CHANNEL: int = SocketType.CHANNEL
+
+__all__: list[str] = [
+ "ContextOption",
+ "IO_THREADS",
+ "MAX_SOCKETS",
+ "SOCKET_LIMIT",
+ "THREAD_PRIORITY",
+ "THREAD_SCHED_POLICY",
+ "MAX_MSGSZ",
+ "MSG_T_SIZE",
+ "THREAD_AFFINITY_CPU_ADD",
+ "THREAD_AFFINITY_CPU_REMOVE",
+ "THREAD_NAME_PREFIX",
+ "DeviceType",
+ "STREAMER",
+ "FORWARDER",
+ "QUEUE",
+ "Enum",
+ "Errno",
+ "EAGAIN",
+ "EFAULT",
+ "EINVAL",
+ "ENOTSUP",
+ "EPROTONOSUPPORT",
+ "ENOBUFS",
+ "ENETDOWN",
+ "EADDRINUSE",
+ "EADDRNOTAVAIL",
+ "ECONNREFUSED",
+ "EINPROGRESS",
+ "ENOTSOCK",
+ "EMSGSIZE",
+ "EAFNOSUPPORT",
+ "ENETUNREACH",
+ "ECONNABORTED",
+ "ECONNRESET",
+ "ENOTCONN",
+ "ETIMEDOUT",
+ "EHOSTUNREACH",
+ "ENETRESET",
+ "EFSM",
+ "ENOCOMPATPROTO",
+ "ETERM",
+ "EMTHREAD",
+ "Event",
+ "PROTOCOL_ERROR_WS_UNSPECIFIED",
+ "PROTOCOL_ERROR_ZMTP_UNSPECIFIED",
+ "PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND",
+ "PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE",
+ "PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE",
+ "PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED",
+ "PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE",
+ "PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO",
+ "PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE",
+ "PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR",
+ "PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY",
+ "PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME",
+ "PROTOCOL_ERROR_ZMTP_INVALID_METADATA",
+ "PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC",
+ "PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH",
+ "PROTOCOL_ERROR_ZAP_UNSPECIFIED",
+ "PROTOCOL_ERROR_ZAP_MALFORMED_REPLY",
+ "PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID",
+ "PROTOCOL_ERROR_ZAP_BAD_VERSION",
+ "PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE",
+ "PROTOCOL_ERROR_ZAP_INVALID_METADATA",
+ "EVENT_CONNECTED",
+ "EVENT_CONNECT_DELAYED",
+ "EVENT_CONNECT_RETRIED",
+ "EVENT_LISTENING",
+ "EVENT_BIND_FAILED",
+ "EVENT_ACCEPTED",
+ "EVENT_ACCEPT_FAILED",
+ "EVENT_CLOSED",
+ "EVENT_CLOSE_FAILED",
+ "EVENT_DISCONNECTED",
+ "EVENT_MONITOR_STOPPED",
+ "EVENT_HANDSHAKE_FAILED_NO_DETAIL",
+ "EVENT_HANDSHAKE_SUCCEEDED",
+ "EVENT_HANDSHAKE_FAILED_PROTOCOL",
+ "EVENT_HANDSHAKE_FAILED_AUTH",
+ "EVENT_ALL_V1",
+ "EVENT_ALL",
+ "EVENT_PIPES_STATS",
+ "EVENT_ALL_V2",
+ "Flag",
+ "DONTWAIT",
+ "SNDMORE",
+ "NOBLOCK",
+ "IntEnum",
+ "IntFlag",
+ "MessageOption",
+ "MORE",
+ "SHARED",
+ "SRCFD",
+ "NormMode",
+ "NORM_FIXED",
+ "NORM_CC",
+ "NORM_CCL",
+ "NORM_CCE",
+ "NORM_CCE_ECNONLY",
+ "PollEvent",
+ "POLLIN",
+ "POLLOUT",
+ "POLLERR",
+ "POLLPRI",
+ "ReconnectStop",
+ "RECONNECT_STOP_CONN_REFUSED",
+ "RECONNECT_STOP_HANDSHAKE_FAILED",
+ "RECONNECT_STOP_AFTER_DISCONNECT",
+ "RouterNotify",
+ "NOTIFY_CONNECT",
+ "NOTIFY_DISCONNECT",
+ "SecurityMechanism",
+ "NULL",
+ "PLAIN",
+ "CURVE",
+ "GSSAPI",
+ "SocketOption",
+ "HWM",
+ "AFFINITY",
+ "ROUTING_ID",
+ "SUBSCRIBE",
+ "UNSUBSCRIBE",
+ "RATE",
+ "RECOVERY_IVL",
+ "SNDBUF",
+ "RCVBUF",
+ "RCVMORE",
+ "FD",
+ "EVENTS",
+ "TYPE",
+ "LINGER",
+ "RECONNECT_IVL",
+ "BACKLOG",
+ "RECONNECT_IVL_MAX",
+ "MAXMSGSIZE",
+ "SNDHWM",
+ "RCVHWM",
+ "MULTICAST_HOPS",
+ "RCVTIMEO",
+ "SNDTIMEO",
+ "LAST_ENDPOINT",
+ "ROUTER_MANDATORY",
+ "TCP_KEEPALIVE",
+ "TCP_KEEPALIVE_CNT",
+ "TCP_KEEPALIVE_IDLE",
+ "TCP_KEEPALIVE_INTVL",
+ "IMMEDIATE",
+ "XPUB_VERBOSE",
+ "ROUTER_RAW",
+ "IPV6",
+ "MECHANISM",
+ "PLAIN_SERVER",
+ "PLAIN_USERNAME",
+ "PLAIN_PASSWORD",
+ "CURVE_SERVER",
+ "CURVE_PUBLICKEY",
+ "CURVE_SECRETKEY",
+ "CURVE_SERVERKEY",
+ "PROBE_ROUTER",
+ "REQ_CORRELATE",
+ "REQ_RELAXED",
+ "CONFLATE",
+ "ZAP_DOMAIN",
+ "ROUTER_HANDOVER",
+ "TOS",
+ "CONNECT_ROUTING_ID",
+ "GSSAPI_SERVER",
+ "GSSAPI_PRINCIPAL",
+ "GSSAPI_SERVICE_PRINCIPAL",
+ "GSSAPI_PLAINTEXT",
+ "HANDSHAKE_IVL",
+ "SOCKS_PROXY",
+ "XPUB_NODROP",
+ "BLOCKY",
+ "XPUB_MANUAL",
+ "XPUB_WELCOME_MSG",
+ "STREAM_NOTIFY",
+ "INVERT_MATCHING",
+ "HEARTBEAT_IVL",
+ "HEARTBEAT_TTL",
+ "HEARTBEAT_TIMEOUT",
+ "XPUB_VERBOSER",
+ "CONNECT_TIMEOUT",
+ "TCP_MAXRT",
+ "THREAD_SAFE",
+ "MULTICAST_MAXTPDU",
+ "VMCI_BUFFER_SIZE",
+ "VMCI_BUFFER_MIN_SIZE",
+ "VMCI_BUFFER_MAX_SIZE",
+ "VMCI_CONNECT_TIMEOUT",
+ "USE_FD",
+ "GSSAPI_PRINCIPAL_NAMETYPE",
+ "GSSAPI_SERVICE_PRINCIPAL_NAMETYPE",
+ "BINDTODEVICE",
+ "IDENTITY",
+ "CONNECT_RID",
+ "TCP_ACCEPT_FILTER",
+ "IPC_FILTER_PID",
+ "IPC_FILTER_UID",
+ "IPC_FILTER_GID",
+ "IPV4ONLY",
+ "DELAY_ATTACH_ON_CONNECT",
+ "FAIL_UNROUTABLE",
+ "ROUTER_BEHAVIOR",
+ "ZAP_ENFORCE_DOMAIN",
+ "LOOPBACK_FASTPATH",
+ "METADATA",
+ "MULTICAST_LOOP",
+ "ROUTER_NOTIFY",
+ "XPUB_MANUAL_LAST_VALUE",
+ "SOCKS_USERNAME",
+ "SOCKS_PASSWORD",
+ "IN_BATCH_SIZE",
+ "OUT_BATCH_SIZE",
+ "WSS_KEY_PEM",
+ "WSS_CERT_PEM",
+ "WSS_TRUST_PEM",
+ "WSS_HOSTNAME",
+ "WSS_TRUST_SYSTEM",
+ "ONLY_FIRST_SUBSCRIBE",
+ "RECONNECT_STOP",
+ "HELLO_MSG",
+ "DISCONNECT_MSG",
+ "PRIORITY",
+ "BUSY_POLL",
+ "HICCUP_MSG",
+ "XSUB_VERBOSE_UNSUBSCRIBE",
+ "TOPICS_COUNT",
+ "NORM_MODE",
+ "NORM_UNICAST_NACK",
+ "NORM_BUFFER_SIZE",
+ "NORM_SEGMENT_SIZE",
+ "NORM_BLOCK_SIZE",
+ "NORM_NUM_PARITY",
+ "NORM_NUM_AUTOPARITY",
+ "NORM_PUSH",
+ "SocketType",
+ "PAIR",
+ "PUB",
+ "SUB",
+ "REQ",
+ "REP",
+ "DEALER",
+ "ROUTER",
+ "PULL",
+ "PUSH",
+ "XPUB",
+ "XSUB",
+ "STREAM",
+ "XREQ",
+ "XREP",
+ "SERVER",
+ "CLIENT",
+ "RADIO",
+ "DISH",
+ "GATHER",
+ "SCATTER",
+ "DGRAM",
+ "PEER",
+ "CHANNEL",
+]
diff --git a/lib/python3.12/site-packages/zmq/decorators.py b/lib/python3.12/site-packages/zmq/decorators.py
new file mode 100644
index 0000000000000000000000000000000000000000..7cd80ebc764fb0b83146821ea85af3ce4aad8196
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/decorators.py
@@ -0,0 +1,190 @@
+"""Decorators for running functions with context/sockets.
+
+.. versionadded:: 15.3
+
+Like using Contexts and Sockets as context managers, but with decorator syntax.
+Context and sockets are closed at the end of the function.
+
+For example::
+
+ from zmq.decorators import context, socket
+
+ @context()
+ @socket(zmq.PUSH)
+ def work(ctx, push):
+ ...
+"""
+
+from __future__ import annotations
+
+# Copyright (c) PyZMQ Developers.
+# Distributed under the terms of the Modified BSD License.
+
+__all__ = (
+ 'context',
+ 'socket',
+)
+
+from functools import wraps
+
+import zmq
+
+
+class _Decorator:
+ '''The mini decorator factory'''
+
+ def __init__(self, target=None):
+ self._target = target
+
+ def __call__(self, *dec_args, **dec_kwargs):
+ """
+ The main logic of decorator
+
+ Here is how those arguments works::
+
+ @out_decorator(*dec_args, *dec_kwargs)
+ def func(*wrap_args, **wrap_kwargs):
+ ...
+
+ And in the ``wrapper``, we simply create ``self.target`` instance via
+ ``with``::
+
+ target = self.get_target(*args, **kwargs)
+ with target(*dec_args, **dec_kwargs) as obj:
+ ...
+
+ """
+ kw_name, dec_args, dec_kwargs = self.process_decorator_args(
+ *dec_args, **dec_kwargs
+ )
+
+ def decorator(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ target = self.get_target(*args, **kwargs)
+
+ with target(*dec_args, **dec_kwargs) as obj:
+ # insert our object into args
+ if kw_name and kw_name not in kwargs:
+ kwargs[kw_name] = obj
+ elif kw_name and kw_name in kwargs:
+ raise TypeError(
+ f"{func.__name__}() got multiple values for"
+ f" argument '{kw_name}'"
+ )
+ else:
+ args = args + (obj,)
+
+ return func(*args, **kwargs)
+
+ return wrapper
+
+ return decorator
+
+ def get_target(self, *args, **kwargs):
+ """Return the target function
+
+ Allows modifying args/kwargs to be passed.
+ """
+ return self._target
+
+ def process_decorator_args(self, *args, **kwargs):
+ """Process args passed to the decorator.
+
+ args not consumed by the decorator will be passed to the target factory
+ (Context/Socket constructor).
+ """
+ kw_name = None
+
+ if isinstance(kwargs.get('name'), str):
+ kw_name = kwargs.pop('name')
+ elif len(args) >= 1 and isinstance(args[0], str):
+ kw_name = args[0]
+ args = args[1:]
+
+ return kw_name, args, kwargs
+
+
+class _ContextDecorator(_Decorator):
+ """Decorator subclass for Contexts"""
+
+ def __init__(self):
+ super().__init__(zmq.Context)
+
+
+class _SocketDecorator(_Decorator):
+ """Decorator subclass for sockets
+
+ Gets the context from other args.
+ """
+
+ def process_decorator_args(self, *args, **kwargs):
+ """Also grab context_name out of kwargs"""
+ kw_name, args, kwargs = super().process_decorator_args(*args, **kwargs)
+ self.context_name = kwargs.pop('context_name', 'context')
+ return kw_name, args, kwargs
+
+ def get_target(self, *args, **kwargs):
+ """Get context, based on call-time args"""
+ context = self._get_context(*args, **kwargs)
+ return context.socket
+
+ def _get_context(self, *args, **kwargs):
+ """
+ Find the ``zmq.Context`` from ``args`` and ``kwargs`` at call time.
+
+ First, if there is an keyword argument named ``context`` and it is a
+ ``zmq.Context`` instance , we will take it.
+
+ Second, we check all the ``args``, take the first ``zmq.Context``
+ instance.
+
+ Finally, we will provide default Context -- ``zmq.Context.instance``
+
+ :return: a ``zmq.Context`` instance
+ """
+ if self.context_name in kwargs:
+ ctx = kwargs[self.context_name]
+
+ if isinstance(ctx, zmq.Context):
+ return ctx
+
+ for arg in args:
+ if isinstance(arg, zmq.Context):
+ return arg
+ # not specified by any decorator
+ return zmq.Context.instance()
+
+
+def context(*args, **kwargs):
+ """Decorator for adding a Context to a function.
+
+ Usage::
+
+ @context()
+ def foo(ctx):
+ ...
+
+ .. versionadded:: 15.3
+
+ :param str name: the keyword argument passed to decorated function
+ """
+ return _ContextDecorator()(*args, **kwargs)
+
+
+def socket(*args, **kwargs):
+ """Decorator for adding a socket to a function.
+
+ Usage::
+
+ @socket(zmq.PUSH)
+ def foo(push):
+ ...
+
+ .. versionadded:: 15.3
+
+ :param str name: the keyword argument passed to decorated function
+ :param str context_name: the keyword only argument to identify context
+ object
+ """
+ return _SocketDecorator()(*args, **kwargs)
diff --git a/lib/python3.12/site-packages/zmq/error.py b/lib/python3.12/site-packages/zmq/error.py
new file mode 100644
index 0000000000000000000000000000000000000000..ddfa46353d27c38708addc95c438d40f00bed3a7
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/error.py
@@ -0,0 +1,212 @@
+"""0MQ Error classes and functions."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+from __future__ import annotations
+
+from errno import EINTR
+
+
+class ZMQBaseError(Exception):
+ """Base exception class for 0MQ errors in Python."""
+
+
+class ZMQError(ZMQBaseError):
+ """Wrap an errno style error.
+
+ Parameters
+ ----------
+ errno : int
+ The ZMQ errno or None. If None, then ``zmq_errno()`` is called and
+ used.
+ msg : str
+ Description of the error or None.
+ """
+
+ errno: int | None = None
+ strerror: str
+
+ def __init__(self, errno: int | None = None, msg: str | None = None):
+ """Wrap an errno style error.
+
+ Parameters
+ ----------
+ errno : int
+ The ZMQ errno or None. If None, then ``zmq_errno()`` is called and
+ used.
+ msg : string
+ Description of the error or None.
+ """
+ from zmq.backend import strerror, zmq_errno
+
+ if errno is None:
+ errno = zmq_errno()
+ if isinstance(errno, int):
+ self.errno = errno
+ if msg is None:
+ self.strerror = strerror(errno)
+ else:
+ self.strerror = msg
+ else:
+ if msg is None:
+ self.strerror = str(errno)
+ else:
+ self.strerror = msg
+ # flush signals, because there could be a SIGINT
+ # waiting to pounce, resulting in uncaught exceptions.
+ # Doing this here means getting SIGINT during a blocking
+ # libzmq call will raise a *catchable* KeyboardInterrupt
+ # PyErr_CheckSignals()
+
+ def __str__(self) -> str:
+ return self.strerror
+
+ def __repr__(self) -> str:
+ return f"{self.__class__.__name__}('{str(self)}')"
+
+
+class ZMQBindError(ZMQBaseError):
+ """An error for ``Socket.bind_to_random_port()``.
+
+ See Also
+ --------
+ .Socket.bind_to_random_port
+ """
+
+
+class NotDone(ZMQBaseError):
+ """Raised when timeout is reached while waiting for 0MQ to finish with a Message
+
+ See Also
+ --------
+ .MessageTracker.wait : object for tracking when ZeroMQ is done
+ """
+
+
+class ContextTerminated(ZMQError):
+ """Wrapper for zmq.ETERM
+
+ .. versionadded:: 13.0
+ """
+
+ def __init__(self, errno="ignored", msg="ignored"):
+ from zmq import ETERM
+
+ super().__init__(ETERM)
+
+
+class Again(ZMQError):
+ """Wrapper for zmq.EAGAIN
+
+ .. versionadded:: 13.0
+ """
+
+ def __init__(self, errno="ignored", msg="ignored"):
+ from zmq import EAGAIN
+
+ super().__init__(EAGAIN)
+
+
+class InterruptedSystemCall(ZMQError, InterruptedError):
+ """Wrapper for EINTR
+
+ This exception should be caught internally in pyzmq
+ to retry system calls, and not propagate to the user.
+
+ .. versionadded:: 14.7
+ """
+
+ errno = EINTR
+ strerror: str
+
+ def __init__(self, errno="ignored", msg="ignored"):
+ super().__init__(EINTR)
+
+ def __str__(self):
+ s = super().__str__()
+ return s + ": This call should have been retried. Please report this to pyzmq."
+
+
+def _check_rc(rc, errno=None, error_without_errno=True):
+ """internal utility for checking zmq return condition
+
+ and raising the appropriate Exception class
+ """
+ if rc == -1:
+ if errno is None:
+ from zmq.backend import zmq_errno
+
+ errno = zmq_errno()
+ if errno == 0 and not error_without_errno:
+ return
+ from zmq import EAGAIN, ETERM
+
+ if errno == EINTR:
+ raise InterruptedSystemCall(errno)
+ elif errno == EAGAIN:
+ raise Again(errno)
+ elif errno == ETERM:
+ raise ContextTerminated(errno)
+ else:
+ raise ZMQError(errno)
+
+
+_zmq_version_info = None
+_zmq_version = None
+
+
+class ZMQVersionError(NotImplementedError):
+ """Raised when a feature is not provided by the linked version of libzmq.
+
+ .. versionadded:: 14.2
+ """
+
+ min_version = None
+
+ def __init__(self, min_version: str, msg: str = "Feature"):
+ global _zmq_version
+ if _zmq_version is None:
+ from zmq import zmq_version
+
+ _zmq_version = zmq_version()
+ self.msg = msg
+ self.min_version = min_version
+ self.version = _zmq_version
+
+ def __repr__(self):
+ return f"ZMQVersionError('{str(self)}')"
+
+ def __str__(self):
+ return f"{self.msg} requires libzmq >= {self.min_version}, have {self.version}"
+
+
+def _check_version(
+ min_version_info: tuple[int] | tuple[int, int] | tuple[int, int, int],
+ msg: str = "Feature",
+):
+ """Check for libzmq
+
+ raises ZMQVersionError if current zmq version is not at least min_version
+
+ min_version_info is a tuple of integers, and will be compared against zmq.zmq_version_info().
+ """
+ global _zmq_version_info
+ if _zmq_version_info is None:
+ from zmq import zmq_version_info
+
+ _zmq_version_info = zmq_version_info()
+ if _zmq_version_info < min_version_info:
+ min_version = ".".join(str(v) for v in min_version_info)
+ raise ZMQVersionError(min_version, msg)
+
+
+__all__ = [
+ "ZMQBaseError",
+ "ZMQBindError",
+ "ZMQError",
+ "NotDone",
+ "ContextTerminated",
+ "InterruptedSystemCall",
+ "Again",
+ "ZMQVersionError",
+]
diff --git a/lib/python3.12/site-packages/zmq/log/__init__.py b/lib/python3.12/site-packages/zmq/log/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lib/python3.12/site-packages/zmq/log/__main__.py b/lib/python3.12/site-packages/zmq/log/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..98c6b97c47381d413fa861702a926e1d57d39a8a
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/log/__main__.py
@@ -0,0 +1,135 @@
+"""pyzmq log watcher.
+
+Easily view log messages published by the PUBHandler in zmq.log.handlers
+
+Designed to be run as an executable module - try this to see options:
+ python -m zmq.log -h
+
+Subscribes to the '' (empty string) topic by default which means it will work
+out-of-the-box with a PUBHandler object instantiated with default settings.
+If you change the root topic with PUBHandler.setRootTopic() you must pass
+the value to this script with the --topic argument.
+
+Note that the default formats for the PUBHandler object selectively include
+the log level in the message. This creates redundancy in this script as it
+always prints the topic of the message, which includes the log level.
+Consider overriding the default formats with PUBHandler.setFormat() to
+avoid this issue.
+
+"""
+
+# encoding: utf-8
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import argparse
+from datetime import datetime
+from typing import Dict
+
+import zmq
+
+parser = argparse.ArgumentParser('ZMQ Log Watcher')
+parser.add_argument('zmq_pub_url', type=str, help='URL to a ZMQ publisher socket.')
+parser.add_argument(
+ '-t',
+ '--topic',
+ type=str,
+ default='',
+ help='Only receive messages that start with this topic.',
+)
+parser.add_argument(
+ '--timestamp', action='store_true', help='Append local time to the log messages.'
+)
+parser.add_argument(
+ '--separator',
+ type=str,
+ default=' | ',
+ help='String to print between topic and message.',
+)
+parser.add_argument(
+ '--dateformat',
+ type=str,
+ default='%Y-%d-%m %H:%M',
+ help='Set alternative date format for use with --timestamp.',
+)
+parser.add_argument(
+ '--align',
+ action='store_true',
+ default=False,
+ help='Try to align messages by the width of their topics.',
+)
+parser.add_argument(
+ '--color',
+ action='store_true',
+ default=False,
+ help='Color the output based on the error level. Requires the colorama module.',
+)
+args = parser.parse_args()
+
+
+if args.color:
+ import colorama
+
+ colorama.init()
+ colors = {
+ 'DEBUG': colorama.Fore.LIGHTCYAN_EX,
+ 'INFO': colorama.Fore.LIGHTWHITE_EX,
+ 'WARNING': colorama.Fore.YELLOW,
+ 'ERROR': colorama.Fore.LIGHTRED_EX,
+ 'CRITICAL': colorama.Fore.LIGHTRED_EX,
+ '__RESET__': colorama.Fore.RESET,
+ }
+else:
+ colors = {}
+
+
+ctx = zmq.Context()
+sub = ctx.socket(zmq.SUB)
+sub.subscribe(args.topic.encode("utf8"))
+sub.connect(args.zmq_pub_url)
+
+topic_widths: Dict[int, int] = {}
+
+while True:
+ try:
+ if sub.poll(10, zmq.POLLIN):
+ topic, msg = sub.recv_multipart()
+ topics = topic.decode('utf8').strip().split('.')
+
+ if args.align:
+ topics.extend(' ' for extra in range(len(topics), len(topic_widths)))
+ aligned_parts = []
+ for key, part in enumerate(topics):
+ topic_widths[key] = max(len(part), topic_widths.get(key, 0))
+ fmt = ''.join(('{:<', str(topic_widths[key]), '}'))
+ aligned_parts.append(fmt.format(part))
+
+ if len(topics) == 1:
+ level = topics[0]
+ else:
+ level = topics[1]
+
+ fields = {
+ 'msg': msg.decode('utf8').strip(),
+ 'ts': (
+ datetime.now().strftime(args.dateformat) + ' '
+ if args.timestamp
+ else ''
+ ),
+ 'aligned': (
+ '.'.join(aligned_parts)
+ if args.align
+ else topic.decode('utf8').strip()
+ ),
+ 'color': colors.get(level, ''),
+ 'color_rst': colors.get('__RESET__', ''),
+ 'sep': args.separator,
+ }
+ print('{ts}{color}{aligned}{sep}{msg}{color_rst}'.format(**fields))
+ except KeyboardInterrupt:
+ break
+
+sub.disconnect(args.zmq_pub_url)
+if args.color:
+ print(colorama.Fore.RESET)
diff --git a/lib/python3.12/site-packages/zmq/log/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/zmq/log/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7cd3933984bd6c1bcdadbe34a4c2013f98442fbc
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/log/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/log/__pycache__/__main__.cpython-312.pyc b/lib/python3.12/site-packages/zmq/log/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e0a15893724bc7ce50e9710b8d12699eb943640b
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/log/__pycache__/__main__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/log/__pycache__/handlers.cpython-312.pyc b/lib/python3.12/site-packages/zmq/log/__pycache__/handlers.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..12c9d31926ebdda19540cb97a0a10d18fe4de593
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/log/__pycache__/handlers.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/log/handlers.py b/lib/python3.12/site-packages/zmq/log/handlers.py
new file mode 100644
index 0000000000000000000000000000000000000000..8138d2eed96c676261688f74ba9fe91dbc29d9c0
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/log/handlers.py
@@ -0,0 +1,232 @@
+"""pyzmq logging handlers.
+
+This mainly defines the PUBHandler object for publishing logging messages over
+a zmq.PUB socket.
+
+The PUBHandler can be used with the regular logging module, as in::
+
+ >>> import logging
+ >>> handler = PUBHandler('tcp://127.0.0.1:12345')
+ >>> handler.root_topic = 'foo'
+ >>> logger = logging.getLogger('foobar')
+ >>> logger.setLevel(logging.DEBUG)
+ >>> logger.addHandler(handler)
+
+Or using ``dictConfig``, as in::
+
+ >>> from logging.config import dictConfig
+ >>> socket = Context.instance().socket(PUB)
+ >>> socket.connect('tcp://127.0.0.1:12345')
+ >>> dictConfig({
+ >>> 'version': 1,
+ >>> 'handlers': {
+ >>> 'zmq': {
+ >>> 'class': 'zmq.log.handlers.PUBHandler',
+ >>> 'level': logging.DEBUG,
+ >>> 'root_topic': 'foo',
+ >>> 'interface_or_socket': socket
+ >>> }
+ >>> },
+ >>> 'root': {
+ >>> 'level': 'DEBUG',
+ >>> 'handlers': ['zmq'],
+ >>> }
+ >>> })
+
+
+After this point, all messages logged by ``logger`` will be published on the
+PUB socket.
+
+Code adapted from StarCluster:
+
+ https://github.com/jtriley/StarCluster/blob/StarCluster-0.91/starcluster/logger.py
+"""
+
+from __future__ import annotations
+
+import logging
+from copy import copy
+
+import zmq
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+
+TOPIC_DELIM = "::" # delimiter for splitting topics on the receiving end.
+
+
+class PUBHandler(logging.Handler):
+ """A basic logging handler that emits log messages through a PUB socket.
+
+ Takes a PUB socket already bound to interfaces or an interface to bind to.
+
+ Example::
+
+ sock = context.socket(zmq.PUB)
+ sock.bind('inproc://log')
+ handler = PUBHandler(sock)
+
+ Or::
+
+ handler = PUBHandler('inproc://loc')
+
+ These are equivalent.
+
+ Log messages handled by this handler are broadcast with ZMQ topics
+ ``this.root_topic`` comes first, followed by the log level
+ (DEBUG,INFO,etc.), followed by any additional subtopics specified in the
+ message by: log.debug("subtopic.subsub::the real message")
+ """
+
+ ctx: zmq.Context
+ socket: zmq.Socket
+
+ def __init__(
+ self,
+ interface_or_socket: str | zmq.Socket,
+ context: zmq.Context | None = None,
+ root_topic: str = '',
+ ) -> None:
+ logging.Handler.__init__(self)
+ self.root_topic = root_topic
+ self.formatters = {
+ logging.DEBUG: logging.Formatter(
+ "%(levelname)s %(filename)s:%(lineno)d - %(message)s\n"
+ ),
+ logging.INFO: logging.Formatter("%(message)s\n"),
+ logging.WARN: logging.Formatter(
+ "%(levelname)s %(filename)s:%(lineno)d - %(message)s\n"
+ ),
+ logging.ERROR: logging.Formatter(
+ "%(levelname)s %(filename)s:%(lineno)d - %(message)s - %(exc_info)s\n"
+ ),
+ logging.CRITICAL: logging.Formatter(
+ "%(levelname)s %(filename)s:%(lineno)d - %(message)s\n"
+ ),
+ }
+ if isinstance(interface_or_socket, zmq.Socket):
+ self.socket = interface_or_socket
+ self.ctx = self.socket.context
+ else:
+ self.ctx = context or zmq.Context()
+ self.socket = self.ctx.socket(zmq.PUB)
+ self.socket.bind(interface_or_socket)
+
+ @property
+ def root_topic(self) -> str:
+ return self._root_topic
+
+ @root_topic.setter
+ def root_topic(self, value: str):
+ self.setRootTopic(value)
+
+ def setRootTopic(self, root_topic: str):
+ """Set the root topic for this handler.
+
+ This value is prepended to all messages published by this handler, and it
+ defaults to the empty string ''. When you subscribe to this socket, you must
+ set your subscription to an empty string, or to at least the first letter of
+ the binary representation of this string to ensure you receive any messages
+ from this handler.
+
+ If you use the default empty string root topic, messages will begin with
+ the binary representation of the log level string (INFO, WARN, etc.).
+ Note that ZMQ SUB sockets can have multiple subscriptions.
+ """
+ if isinstance(root_topic, bytes):
+ root_topic = root_topic.decode("utf8")
+ self._root_topic = root_topic
+
+ def setFormatter(self, fmt, level=logging.NOTSET):
+ """Set the Formatter for this handler.
+
+ If no level is provided, the same format is used for all levels. This
+ will overwrite all selective formatters set in the object constructor.
+ """
+ if level == logging.NOTSET:
+ for fmt_level in self.formatters.keys():
+ self.formatters[fmt_level] = fmt
+ else:
+ self.formatters[level] = fmt
+
+ def format(self, record):
+ """Format a record."""
+ return self.formatters[record.levelno].format(record)
+
+ def emit(self, record):
+ """Emit a log message on my socket."""
+
+ # LogRecord.getMessage explicitly allows msg to be anything _castable_ to a str
+ try:
+ topic, msg = str(record.msg).split(TOPIC_DELIM, 1)
+ except ValueError:
+ topic = ""
+ else:
+ # copy to avoid mutating LogRecord in-place
+ record = copy(record)
+ record.msg = msg
+
+ try:
+ bmsg = self.format(record).encode("utf8")
+ except Exception:
+ self.handleError(record)
+ return
+
+ topic_list = []
+
+ if self.root_topic:
+ topic_list.append(self.root_topic)
+
+ topic_list.append(record.levelname)
+
+ if topic:
+ topic_list.append(topic)
+
+ btopic = '.'.join(topic_list).encode("utf8", "replace")
+
+ self.socket.send_multipart([btopic, bmsg])
+
+
+class TopicLogger(logging.Logger):
+ """A simple wrapper that takes an additional argument to log methods.
+
+ All the regular methods exist, but instead of one msg argument, two
+ arguments: topic, msg are passed.
+
+ That is::
+
+ logger.debug('msg')
+
+ Would become::
+
+ logger.debug('topic.sub', 'msg')
+ """
+
+ def log(self, level, topic, msg, *args, **kwargs):
+ """Log 'msg % args' with level and topic.
+
+ To pass exception information, use the keyword argument exc_info
+ with a True value::
+
+ logger.log(level, "zmq.fun", "We have a %s",
+ "mysterious problem", exc_info=1)
+ """
+ logging.Logger.log(self, level, f'{topic}{TOPIC_DELIM}{msg}', *args, **kwargs)
+
+
+# Generate the methods of TopicLogger, since they are just adding a
+# topic prefix to a message.
+for name in "debug warn warning error critical fatal".split():
+ try:
+ meth = getattr(logging.Logger, name)
+ except AttributeError:
+ # some methods are missing, e.g. Logger.warn was removed from Python 3.13
+ continue
+ setattr(
+ TopicLogger,
+ name,
+ lambda self, level, topic, msg, *args, **kwargs: meth(
+ self, level, topic + TOPIC_DELIM + msg, *args, **kwargs
+ ),
+ )
diff --git a/lib/python3.12/site-packages/zmq/py.typed b/lib/python3.12/site-packages/zmq/py.typed
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lib/python3.12/site-packages/zmq/ssh/__init__.py b/lib/python3.12/site-packages/zmq/ssh/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..57f09568223c48babf9c9d7745218f7e33290eaa
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/ssh/__init__.py
@@ -0,0 +1 @@
+from zmq.ssh.tunnel import *
diff --git a/lib/python3.12/site-packages/zmq/ssh/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/zmq/ssh/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6990e0577a473bd48edd9eaae8ae9b280569d96e
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/ssh/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/ssh/__pycache__/forward.cpython-312.pyc b/lib/python3.12/site-packages/zmq/ssh/__pycache__/forward.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5f477d2e6d755d490e9cef935a2462ae98b19e10
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/ssh/__pycache__/forward.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/ssh/__pycache__/tunnel.cpython-312.pyc b/lib/python3.12/site-packages/zmq/ssh/__pycache__/tunnel.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a210c78576b8d3d552b1a7f573d34d16813d2de7
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/ssh/__pycache__/tunnel.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/ssh/forward.py b/lib/python3.12/site-packages/zmq/ssh/forward.py
new file mode 100644
index 0000000000000000000000000000000000000000..2115137ae847e3c5cfaf059a7b11c3cfadca1313
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/ssh/forward.py
@@ -0,0 +1,96 @@
+#
+# This file is adapted from a paramiko demo, and thus licensed under LGPL 2.1.
+# Original Copyright (C) 2003-2007 Robey Pointer
+# Edits Copyright (C) 2010 The IPython Team
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA.
+
+"""
+Sample script showing how to do local port forwarding over paramiko.
+
+This script connects to the requested SSH server and sets up local port
+forwarding (the openssh -L option) from a local port through a tunneled
+connection to a destination reachable from the SSH server machine.
+"""
+
+import logging
+import select
+import socketserver
+
+logger = logging.getLogger('ssh')
+
+
+class ForwardServer(socketserver.ThreadingTCPServer):
+ daemon_threads = True
+ allow_reuse_address = True
+
+
+class Handler(socketserver.BaseRequestHandler):
+ def handle(self):
+ try:
+ chan = self.ssh_transport.open_channel(
+ 'direct-tcpip',
+ (self.chain_host, self.chain_port),
+ self.request.getpeername(),
+ )
+ except Exception as e:
+ logger.debug(
+ 'Incoming request to %s:%d failed: %r',
+ self.chain_host,
+ self.chain_port,
+ e,
+ )
+ return
+ if chan is None:
+ logger.debug(
+ 'Incoming request to %s:%d was rejected by the SSH server.',
+ self.chain_host,
+ self.chain_port,
+ )
+ return
+
+ logger.debug(
+ f'Connected! Tunnel open {self.request.getpeername()!r} -> {chan.getpeername()!r} -> {(self.chain_host, self.chain_port)!r}'
+ )
+ while True:
+ r, w, x = select.select([self.request, chan], [], [])
+ if self.request in r:
+ data = self.request.recv(1024)
+ if len(data) == 0:
+ break
+ chan.send(data)
+ if chan in r:
+ data = chan.recv(1024)
+ if len(data) == 0:
+ break
+ self.request.send(data)
+ chan.close()
+ self.request.close()
+ logger.debug('Tunnel closed ')
+
+
+def forward_tunnel(local_port, remote_host, remote_port, transport):
+ # this is a little convoluted, but lets me configure things for the Handler
+ # object. (SocketServer doesn't give Handlers any way to access the outer
+ # server normally.)
+ class SubHander(Handler):
+ chain_host = remote_host
+ chain_port = remote_port
+ ssh_transport = transport
+
+ ForwardServer(('127.0.0.1', local_port), SubHander).serve_forever()
+
+
+__all__ = ['forward_tunnel']
diff --git a/lib/python3.12/site-packages/zmq/ssh/tunnel.py b/lib/python3.12/site-packages/zmq/ssh/tunnel.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e9c88e8a1acd88ad3aca4495d9632591cb7d336
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/ssh/tunnel.py
@@ -0,0 +1,430 @@
+"""Basic ssh tunnel utilities, and convenience functions for tunneling
+zeromq connections.
+"""
+
+# Copyright (C) 2010-2011 IPython Development Team
+# Copyright (C) 2011- PyZMQ Developers
+#
+# Redistributed from IPython under the terms of the BSD License.
+
+import atexit
+import os
+import re
+import signal
+import socket
+import sys
+import warnings
+from getpass import getpass, getuser
+from multiprocessing import Process
+
+try:
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', DeprecationWarning)
+ import paramiko
+
+ SSHException = paramiko.ssh_exception.SSHException
+except ImportError:
+ paramiko = None # type: ignore
+
+ class SSHException(Exception): # type: ignore
+ pass
+
+else:
+ from .forward import forward_tunnel
+
+try:
+ import pexpect
+except ImportError:
+ pexpect = None
+
+
+class MaxRetryExceeded(Exception):
+ pass
+
+
+def select_random_ports(n):
+ """Select and return n random ports that are available."""
+ ports = []
+ sockets = []
+ for i in range(n):
+ sock = socket.socket()
+ sock.bind(('', 0))
+ ports.append(sock.getsockname()[1])
+ sockets.append(sock)
+ for sock in sockets:
+ sock.close()
+ return ports
+
+
+# -----------------------------------------------------------------------------
+# Check for passwordless login
+# -----------------------------------------------------------------------------
+_password_pat = re.compile(rb'pass(word|phrase)', re.IGNORECASE)
+
+
+def try_passwordless_ssh(server, keyfile, paramiko=None):
+ """Attempt to make an ssh connection without a password.
+ This is mainly used for requiring password input only once
+ when many tunnels may be connected to the same server.
+
+ If paramiko is None, the default for the platform is chosen.
+ """
+ if paramiko is None:
+ paramiko = sys.platform == 'win32'
+ if not paramiko:
+ f = _try_passwordless_openssh
+ else:
+ f = _try_passwordless_paramiko
+ return f(server, keyfile)
+
+
+def _try_passwordless_openssh(server, keyfile):
+ """Try passwordless login with shell ssh command."""
+ if pexpect is None:
+ raise ImportError("pexpect unavailable, use paramiko")
+ cmd = 'ssh -f ' + server
+ if keyfile:
+ cmd += ' -i ' + keyfile
+ cmd += ' exit'
+
+ # pop SSH_ASKPASS from env
+ env = os.environ.copy()
+ env.pop('SSH_ASKPASS', None)
+
+ ssh_newkey = 'Are you sure you want to continue connecting'
+ p = pexpect.spawn(cmd, env=env)
+
+ MAX_RETRY = 10
+
+ for _ in range(MAX_RETRY):
+ try:
+ i = p.expect([ssh_newkey, _password_pat], timeout=0.1)
+ if i == 0:
+ raise SSHException(
+ 'The authenticity of the host can\'t be established.'
+ )
+ except pexpect.TIMEOUT:
+ continue
+ except pexpect.EOF:
+ return True
+ else:
+ return False
+
+ raise MaxRetryExceeded(f"Failed after {MAX_RETRY} attempts")
+
+
+def _try_passwordless_paramiko(server, keyfile):
+ """Try passwordless login with paramiko."""
+ if paramiko is None:
+ msg = "Paramiko unavailable, "
+ if sys.platform == 'win32':
+ msg += "Paramiko is required for ssh tunneled connections on Windows."
+ else:
+ msg += "use OpenSSH."
+ raise ImportError(msg)
+ username, server, port = _split_server(server)
+ client = paramiko.SSHClient()
+ known_hosts = os.path.expanduser("~/.ssh/known_hosts")
+ try:
+ client.load_host_keys(known_hosts)
+ except FileNotFoundError:
+ pass
+
+ policy_name = os.environ.get("PYZMQ_PARAMIKO_HOST_KEY_POLICY", None)
+ if policy_name:
+ policy = getattr(paramiko, f"{policy_name}Policy")
+ client.set_missing_host_key_policy(policy())
+ try:
+ client.connect(
+ server, port, username=username, key_filename=keyfile, look_for_keys=True
+ )
+ except paramiko.AuthenticationException:
+ return False
+ else:
+ client.close()
+ return True
+
+
+def tunnel_connection(
+ socket, addr, server, keyfile=None, password=None, paramiko=None, timeout=60
+):
+ """Connect a socket to an address via an ssh tunnel.
+
+ This is a wrapper for socket.connect(addr), when addr is not accessible
+ from the local machine. It simply creates an ssh tunnel using the remaining args,
+ and calls socket.connect('tcp://localhost:lport') where lport is the randomly
+ selected local port of the tunnel.
+
+ """
+ new_url, tunnel = open_tunnel(
+ addr,
+ server,
+ keyfile=keyfile,
+ password=password,
+ paramiko=paramiko,
+ timeout=timeout,
+ )
+ socket.connect(new_url)
+ return tunnel
+
+
+def open_tunnel(addr, server, keyfile=None, password=None, paramiko=None, timeout=60):
+ """Open a tunneled connection from a 0MQ url.
+
+ For use inside tunnel_connection.
+
+ Returns
+ -------
+
+ (url, tunnel) : (str, object)
+ The 0MQ url that has been forwarded, and the tunnel object
+ """
+
+ lport = select_random_ports(1)[0]
+ transport, addr = addr.split('://')
+ ip, rport = addr.split(':')
+ rport = int(rport)
+ if paramiko is None:
+ paramiko = sys.platform == 'win32'
+ if paramiko:
+ tunnelf = paramiko_tunnel
+ else:
+ tunnelf = openssh_tunnel
+
+ tunnel = tunnelf(
+ lport,
+ rport,
+ server,
+ remoteip=ip,
+ keyfile=keyfile,
+ password=password,
+ timeout=timeout,
+ )
+ return f'tcp://127.0.0.1:{lport}', tunnel
+
+
+def openssh_tunnel(
+ lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60
+):
+ """Create an ssh tunnel using command-line ssh that connects port lport
+ on this machine to localhost:rport on server. The tunnel
+ will automatically close when not in use, remaining open
+ for a minimum of timeout seconds for an initial connection.
+
+ This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
+ as seen from `server`.
+
+ keyfile and password may be specified, but ssh config is checked for defaults.
+
+ Parameters
+ ----------
+
+ lport : int
+ local port for connecting to the tunnel from this machine.
+ rport : int
+ port on the remote machine to connect to.
+ server : str
+ The ssh server to connect to. The full ssh server string will be parsed.
+ user@server:port
+ remoteip : str [Default: 127.0.0.1]
+ The remote ip, specifying the destination of the tunnel.
+ Default is localhost, which means that the tunnel would redirect
+ localhost:lport on this machine to localhost:rport on the *server*.
+
+ keyfile : str; path to private key file
+ This specifies a key to be used in ssh login, default None.
+ Regular default ssh keys will be used without specifying this argument.
+ password : str;
+ Your ssh password to the ssh server. Note that if this is left None,
+ you will be prompted for it if passwordless key based login is unavailable.
+ timeout : int [default: 60]
+ The time (in seconds) after which no activity will result in the tunnel
+ closing. This prevents orphaned tunnels from running forever.
+ """
+ if pexpect is None:
+ raise ImportError("pexpect unavailable, use paramiko_tunnel")
+ ssh = "ssh "
+ if keyfile:
+ ssh += "-i " + keyfile
+
+ if ':' in server:
+ server, port = server.split(':')
+ ssh += f" -p {port}"
+
+ cmd = f"{ssh} -O check {server}"
+ (output, exitstatus) = pexpect.run(cmd, withexitstatus=True)
+ if not exitstatus:
+ pid = int(output[output.find(b"(pid=") + 5 : output.find(b")")])
+ cmd = f"{ssh} -O forward -L 127.0.0.1:{lport}:{remoteip}:{rport} {server}"
+ (output, exitstatus) = pexpect.run(cmd, withexitstatus=True)
+ if not exitstatus:
+ atexit.register(_stop_tunnel, cmd.replace("-O forward", "-O cancel", 1))
+ return pid
+ cmd = f"{ssh} -f -S none -L 127.0.0.1:{lport}:{remoteip}:{rport} {server} sleep {timeout}"
+
+ # pop SSH_ASKPASS from env
+ env = os.environ.copy()
+ env.pop('SSH_ASKPASS', None)
+
+ ssh_newkey = 'Are you sure you want to continue connecting'
+ tunnel = pexpect.spawn(cmd, env=env)
+ failed = False
+ MAX_RETRY = 10
+ for _ in range(MAX_RETRY):
+ try:
+ i = tunnel.expect([ssh_newkey, _password_pat], timeout=0.1)
+ if i == 0:
+ raise SSHException(
+ 'The authenticity of the host can\'t be established.'
+ )
+ except pexpect.TIMEOUT:
+ continue
+ except pexpect.EOF:
+ if tunnel.exitstatus:
+ print(tunnel.exitstatus)
+ print(tunnel.before)
+ print(tunnel.after)
+ raise RuntimeError(f"tunnel '{cmd}' failed to start")
+ else:
+ return tunnel.pid
+ else:
+ if failed:
+ print("Password rejected, try again")
+ password = None
+ if password is None:
+ password = getpass(f"{server}'s password: ")
+ tunnel.sendline(password)
+ failed = True
+ raise MaxRetryExceeded(f"Failed after {MAX_RETRY} attempts")
+
+
+def _stop_tunnel(cmd):
+ pexpect.run(cmd)
+
+
+def _split_server(server):
+ if '@' in server:
+ username, server = server.split('@', 1)
+ else:
+ username = getuser()
+ if ':' in server:
+ server, port = server.split(':')
+ port = int(port)
+ else:
+ port = 22
+ return username, server, port
+
+
+def paramiko_tunnel(
+ lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=60
+):
+ """launch a tunner with paramiko in a subprocess. This should only be used
+ when shell ssh is unavailable (e.g. Windows).
+
+ This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`,
+ as seen from `server`.
+
+ If you are familiar with ssh tunnels, this creates the tunnel:
+
+ ssh server -L localhost:lport:remoteip:rport
+
+ keyfile and password may be specified, but ssh config is checked for defaults.
+
+
+ Parameters
+ ----------
+
+ lport : int
+ local port for connecting to the tunnel from this machine.
+ rport : int
+ port on the remote machine to connect to.
+ server : str
+ The ssh server to connect to. The full ssh server string will be parsed.
+ user@server:port
+ remoteip : str [Default: 127.0.0.1]
+ The remote ip, specifying the destination of the tunnel.
+ Default is localhost, which means that the tunnel would redirect
+ localhost:lport on this machine to localhost:rport on the *server*.
+
+ keyfile : str; path to private key file
+ This specifies a key to be used in ssh login, default None.
+ Regular default ssh keys will be used without specifying this argument.
+ password : str;
+ Your ssh password to the ssh server. Note that if this is left None,
+ you will be prompted for it if passwordless key based login is unavailable.
+ timeout : int [default: 60]
+ The time (in seconds) after which no activity will result in the tunnel
+ closing. This prevents orphaned tunnels from running forever.
+
+ """
+ if paramiko is None:
+ raise ImportError("Paramiko not available")
+
+ if password is None:
+ if not _try_passwordless_paramiko(server, keyfile):
+ password = getpass(f"{server}'s password: ")
+
+ p = Process(
+ target=_paramiko_tunnel,
+ args=(lport, rport, server, remoteip),
+ kwargs=dict(keyfile=keyfile, password=password),
+ )
+ p.daemon = True
+ p.start()
+ return p
+
+
+def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None):
+ """Function for actually starting a paramiko tunnel, to be passed
+ to multiprocessing.Process(target=this), and not called directly.
+ """
+ username, server, port = _split_server(server)
+ client = paramiko.SSHClient()
+ client.load_system_host_keys()
+ client.set_missing_host_key_policy(paramiko.WarningPolicy())
+
+ try:
+ client.connect(
+ server,
+ port,
+ username=username,
+ key_filename=keyfile,
+ look_for_keys=True,
+ password=password,
+ )
+ # except paramiko.AuthenticationException:
+ # if password is None:
+ # password = getpass("%s@%s's password: "%(username, server))
+ # client.connect(server, port, username=username, password=password)
+ # else:
+ # raise
+ except Exception as e:
+ print(f'*** Failed to connect to {server}:{port}: {e!r}')
+ sys.exit(1)
+
+ # Don't let SIGINT kill the tunnel subprocess
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+ try:
+ forward_tunnel(lport, remoteip, rport, client.get_transport())
+ except KeyboardInterrupt:
+ print('SIGINT: Port forwarding stopped cleanly')
+ sys.exit(0)
+ except Exception as e:
+ print(f"Port forwarding stopped uncleanly: {e}")
+ sys.exit(255)
+
+
+if sys.platform == 'win32':
+ ssh_tunnel = paramiko_tunnel
+else:
+ ssh_tunnel = openssh_tunnel
+
+
+__all__ = [
+ 'tunnel_connection',
+ 'ssh_tunnel',
+ 'openssh_tunnel',
+ 'paramiko_tunnel',
+ 'try_passwordless_ssh',
+]
diff --git a/lib/python3.12/site-packages/zmq/sugar/__init__.py b/lib/python3.12/site-packages/zmq/sugar/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..88e755682c1096272830c01e37ef71d822ec1b05
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/__init__.py
@@ -0,0 +1,39 @@
+"""pure-Python sugar wrappers for core 0MQ objects."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import annotations
+
+from zmq import error
+from zmq.backend import proxy
+from zmq.constants import DeviceType
+from zmq.sugar import context, frame, poll, socket, tracker, version
+
+
+def device(device_type: DeviceType, frontend: socket.Socket, backend: socket.Socket):
+ """Deprecated alias for zmq.proxy
+
+ .. deprecated:: libzmq-3.2
+ .. deprecated:: 13.0
+ """
+
+ return proxy(frontend, backend)
+
+
+__all__ = ["device"]
+for submod in (context, error, frame, poll, socket, tracker, version):
+ __all__.extend(submod.__all__)
+
+from zmq.error import * # noqa
+from zmq.sugar.context import * # noqa
+from zmq.sugar.frame import * # noqa
+from zmq.sugar.poll import * # noqa
+from zmq.sugar.socket import * # noqa
+
+# deprecated:
+from zmq.sugar.stopwatch import Stopwatch # noqa
+from zmq.sugar.tracker import * # noqa
+from zmq.sugar.version import * # noqa
+
+__all__.append('Stopwatch')
diff --git a/lib/python3.12/site-packages/zmq/sugar/__init__.pyi b/lib/python3.12/site-packages/zmq/sugar/__init__.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..732f605017a063e9b66403921623a468f1ea0abe
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/__init__.pyi
@@ -0,0 +1,10 @@
+from zmq.error import *
+
+from . import constants as constants
+from .constants import *
+from .context import *
+from .frame import *
+from .poll import *
+from .socket import *
+from .tracker import *
+from .version import *
diff --git a/lib/python3.12/site-packages/zmq/sugar/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/zmq/sugar/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3d04bf3182efeac2e55740b35224f9155881b99b
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/sugar/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/sugar/__pycache__/attrsettr.cpython-312.pyc b/lib/python3.12/site-packages/zmq/sugar/__pycache__/attrsettr.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7c31bb6536a5b360d17f13cf130f10616524d688
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/sugar/__pycache__/attrsettr.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/sugar/__pycache__/context.cpython-312.pyc b/lib/python3.12/site-packages/zmq/sugar/__pycache__/context.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3f5d0173d6147633213a3ea9f314cdf345ed1470
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/sugar/__pycache__/context.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/sugar/__pycache__/frame.cpython-312.pyc b/lib/python3.12/site-packages/zmq/sugar/__pycache__/frame.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..80ca186bc3bf2b564302b710a1832a340cf33317
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/sugar/__pycache__/frame.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/sugar/__pycache__/poll.cpython-312.pyc b/lib/python3.12/site-packages/zmq/sugar/__pycache__/poll.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b0f77ea0ea0f5c0c683f3e0b48058c25683cc264
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/sugar/__pycache__/poll.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/sugar/__pycache__/socket.cpython-312.pyc b/lib/python3.12/site-packages/zmq/sugar/__pycache__/socket.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0c6ef3e34656e51f8a1e5d2c5c323d0a73e7d0b8
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/sugar/__pycache__/socket.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/sugar/__pycache__/stopwatch.cpython-312.pyc b/lib/python3.12/site-packages/zmq/sugar/__pycache__/stopwatch.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fac2407d19b59b8aaec85249aec9492a597597fb
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/sugar/__pycache__/stopwatch.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/sugar/__pycache__/tracker.cpython-312.pyc b/lib/python3.12/site-packages/zmq/sugar/__pycache__/tracker.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..021670c20b7250cd29e1fa36d73daaf3081b22b8
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/sugar/__pycache__/tracker.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/sugar/__pycache__/version.cpython-312.pyc b/lib/python3.12/site-packages/zmq/sugar/__pycache__/version.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..12a3918f9cb91073fd2e2222352fb6c98b475d43
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/sugar/__pycache__/version.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/sugar/attrsettr.py b/lib/python3.12/site-packages/zmq/sugar/attrsettr.py
new file mode 100644
index 0000000000000000000000000000000000000000..844fce606e53def45930d2e3a19873b4e8b4aeac
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/attrsettr.py
@@ -0,0 +1,79 @@
+"""Mixin for mapping set/getattr to self.set/get"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+from __future__ import annotations
+
+import errno
+from typing import TypeVar, Union
+
+from .. import constants
+
+T = TypeVar("T")
+OptValT = Union[str, bytes, int]
+
+
+class AttributeSetter:
+ def __setattr__(self, key: str, value: OptValT) -> None:
+ """set zmq options by attribute"""
+
+ if key in self.__dict__:
+ object.__setattr__(self, key, value)
+ return
+ # regular setattr only allowed for class-defined attributes
+ for cls in self.__class__.mro():
+ if key in cls.__dict__ or key in getattr(cls, "__annotations__", {}):
+ object.__setattr__(self, key, value)
+ return
+
+ upper_key = key.upper()
+ try:
+ opt = getattr(constants, upper_key)
+ except AttributeError:
+ raise AttributeError(
+ f"{self.__class__.__name__} has no such option: {upper_key}"
+ )
+ else:
+ self._set_attr_opt(upper_key, opt, value)
+
+ def _set_attr_opt(self, name: str, opt: int, value: OptValT) -> None:
+ """override if setattr should do something other than call self.set"""
+ self.set(opt, value)
+
+ def __getattr__(self, key: str) -> OptValT:
+ """get zmq options by attribute"""
+ upper_key = key.upper()
+ try:
+ opt = getattr(constants, upper_key)
+ except AttributeError:
+ raise AttributeError(
+ f"{self.__class__.__name__} has no such option: {upper_key}"
+ ) from None
+ else:
+ from zmq import ZMQError
+
+ try:
+ return self._get_attr_opt(upper_key, opt)
+ except ZMQError as e:
+ # EINVAL will be raised on access for write-only attributes.
+ # Turn that into an AttributeError
+ # necessary for mocking
+ if e.errno in {errno.EINVAL, errno.EFAULT}:
+ raise AttributeError(f"{key} attribute is write-only")
+ else:
+ raise
+
+ def _get_attr_opt(self, name, opt) -> OptValT:
+ """override if getattr should do something other than call self.get"""
+ return self.get(opt)
+
+ def get(self, opt: int) -> OptValT:
+ """Override in subclass"""
+ raise NotImplementedError("override in subclass")
+
+ def set(self, opt: int, val: OptValT) -> None:
+ """Override in subclass"""
+ raise NotImplementedError("override in subclass")
+
+
+__all__ = ['AttributeSetter']
diff --git a/lib/python3.12/site-packages/zmq/sugar/context.py b/lib/python3.12/site-packages/zmq/sugar/context.py
new file mode 100644
index 0000000000000000000000000000000000000000..21bc2cdda9d9319b03a6a0b45d791a6c5e2c3e8a
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/context.py
@@ -0,0 +1,420 @@
+"""Python bindings for 0MQ."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import annotations
+
+import atexit
+import os
+from threading import Lock
+from typing import Any, Callable, Generic, TypeVar, overload
+from warnings import warn
+from weakref import WeakSet
+
+import zmq
+from zmq._typing import TypeAlias
+from zmq.backend import Context as ContextBase
+from zmq.constants import ContextOption, Errno, SocketOption
+from zmq.error import ZMQError
+from zmq.utils.interop import cast_int_addr
+
+from .attrsettr import AttributeSetter, OptValT
+from .socket import Socket, SyncSocket
+
+# notice when exiting, to avoid triggering term on exit
+_exiting = False
+
+
+def _notice_atexit() -> None:
+ global _exiting
+ _exiting = True
+
+
+atexit.register(_notice_atexit)
+
+_ContextType = TypeVar('_ContextType', bound='Context')
+_SocketType = TypeVar('_SocketType', bound='Socket', covariant=True)
+
+
+class Context(ContextBase, AttributeSetter, Generic[_SocketType]):
+ """Create a zmq Context
+
+ A zmq Context creates sockets via its ``ctx.socket`` method.
+
+ .. versionchanged:: 24
+
+ When using a Context as a context manager (``with zmq.Context()``),
+ or deleting a context without closing it first,
+ ``ctx.destroy()`` is called,
+ closing any leftover sockets,
+ instead of `ctx.term()` which requires sockets to be closed first.
+
+ This prevents hangs caused by `ctx.term()` if sockets are left open,
+ but means that unclean destruction of contexts
+ (with sockets left open) is not safe
+ if sockets are managed in other threads.
+
+ .. versionadded:: 25
+
+ Contexts can now be shadowed by passing another Context.
+ This helps in creating an async copy of a sync context or vice versa::
+
+ ctx = zmq.Context(async_ctx)
+
+ Which previously had to be::
+
+ ctx = zmq.Context.shadow(async_ctx.underlying)
+ """
+
+ sockopts: dict[int, Any]
+ _instance: Any = None
+ _instance_lock = Lock()
+ _instance_pid: int | None = None
+ _shadow = False
+ _shadow_obj = None
+ _warn_destroy_close = False
+ _sockets: WeakSet
+ # mypy doesn't like a default value here
+ _socket_class: type[_SocketType] = Socket # type: ignore
+
+ @overload
+ def __init__(self: SyncContext, io_threads: int = 1): ...
+
+ @overload
+ def __init__(self: SyncContext, io_threads: Context, /): ...
+
+ @overload
+ def __init__(self: SyncContext, *, shadow: Context | int): ...
+
+ def __init__(
+ self: SyncContext,
+ io_threads: int | Context = 1,
+ shadow: Context | int = 0,
+ ) -> None:
+ if isinstance(io_threads, Context):
+ # allow positional shadow `zmq.Context(zmq.asyncio.Context())`
+ # this s
+ shadow = io_threads
+ io_threads = 1
+
+ shadow_address: int = 0
+ if shadow:
+ self._shadow = True
+ # hold a reference to the shadow object
+ self._shadow_obj = shadow
+ if not isinstance(shadow, int):
+ try:
+ shadow = shadow.underlying
+ except AttributeError:
+ pass
+ shadow_address = cast_int_addr(shadow)
+ else:
+ self._shadow = False
+ super().__init__(io_threads=io_threads, shadow=shadow_address)
+ self.sockopts = {}
+ self._sockets = WeakSet()
+
+ def __del__(self) -> None:
+ """Deleting a Context without closing it destroys it and all sockets.
+
+ .. versionchanged:: 24
+ Switch from threadsafe `term()` which hangs in the event of open sockets
+ to less safe `destroy()` which
+ warns about any leftover sockets and closes them.
+ """
+
+ # Calling locals() here conceals issue #1167 on Windows CPython 3.5.4.
+ locals()
+
+ if not self._shadow and not _exiting and not self.closed:
+ self._warn_destroy_close = True
+ if warn is not None and getattr(self, "_sockets", None) is not None:
+ # warn can be None during process teardown
+ warn(
+ f"Unclosed context {self}",
+ ResourceWarning,
+ stacklevel=2,
+ source=self,
+ )
+ self.destroy()
+
+ _repr_cls = "zmq.Context"
+
+ def __repr__(self) -> str:
+ cls = self.__class__
+ # look up _repr_cls on exact class, not inherited
+ _repr_cls = cls.__dict__.get("_repr_cls", None)
+ if _repr_cls is None:
+ _repr_cls = f"{cls.__module__}.{cls.__name__}"
+
+ closed = ' closed' if self.closed else ''
+ if getattr(self, "_sockets", None):
+ n_sockets = len(self._sockets)
+ s = 's' if n_sockets > 1 else ''
+ sockets = f"{n_sockets} socket{s}"
+ else:
+ sockets = ""
+ return f"<{_repr_cls}({sockets}) at {hex(id(self))}{closed}>"
+
+ def __enter__(self: _ContextType) -> _ContextType:
+ return self
+
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
+ # warn about any leftover sockets before closing them
+ self._warn_destroy_close = True
+ self.destroy()
+
+ def __copy__(self: _ContextType, memo: Any = None) -> _ContextType:
+ """Copying a Context creates a shadow copy"""
+ return self.__class__.shadow(self.underlying)
+
+ __deepcopy__ = __copy__
+
+ @classmethod
+ def shadow(cls: type[_ContextType], address: int | zmq.Context) -> _ContextType:
+ """Shadow an existing libzmq context
+
+ address is a zmq.Context or an integer (or FFI pointer)
+ representing the address of the libzmq context.
+
+ .. versionadded:: 14.1
+
+ .. versionadded:: 25
+ Support for shadowing `zmq.Context` objects,
+ instead of just integer addresses.
+ """
+ return cls(shadow=address)
+
+ @classmethod
+ def shadow_pyczmq(cls: type[_ContextType], ctx: Any) -> _ContextType:
+ """Shadow an existing pyczmq context
+
+ ctx is the FFI `zctx_t *` pointer
+
+ .. versionadded:: 14.1
+ """
+ from pyczmq import zctx # type: ignore
+
+ from zmq.utils.interop import cast_int_addr
+
+ underlying = zctx.underlying(ctx)
+ address = cast_int_addr(underlying)
+ return cls(shadow=address)
+
+ # static method copied from tornado IOLoop.instance
+ @classmethod
+ def instance(cls: type[_ContextType], io_threads: int = 1) -> _ContextType:
+ """Returns a global Context instance.
+
+ Most single-process applications have a single, global Context.
+ Use this method instead of passing around Context instances
+ throughout your code.
+
+ A common pattern for classes that depend on Contexts is to use
+ a default argument to enable programs with multiple Contexts
+ but not require the argument for simpler applications::
+
+ class MyClass(object):
+ def __init__(self, context=None):
+ self.context = context or Context.instance()
+
+ .. versionchanged:: 18.1
+
+ When called in a subprocess after forking,
+ a new global instance is created instead of inheriting
+ a Context that won't work from the parent process.
+ """
+ if (
+ cls._instance is None
+ or cls._instance_pid != os.getpid()
+ or cls._instance.closed
+ ):
+ with cls._instance_lock:
+ if (
+ cls._instance is None
+ or cls._instance_pid != os.getpid()
+ or cls._instance.closed
+ ):
+ cls._instance = cls(io_threads=io_threads)
+ cls._instance_pid = os.getpid()
+ return cls._instance
+
+ def term(self) -> None:
+ """Close or terminate the context.
+
+ Context termination is performed in the following steps:
+
+ - Any blocking operations currently in progress on sockets open within context shall
+ raise :class:`zmq.ContextTerminated`.
+ With the exception of socket.close(), any further operations on sockets open within this context
+ shall raise :class:`zmq.ContextTerminated`.
+ - After interrupting all blocking calls, term shall block until the following conditions are satisfied:
+ - All sockets open within context have been closed.
+ - For each socket within context, all messages sent on the socket have either been
+ physically transferred to a network peer,
+ or the socket's linger period set with the zmq.LINGER socket option has expired.
+
+ For further details regarding socket linger behaviour refer to libzmq documentation for ZMQ_LINGER.
+
+ This can be called to close the context by hand. If this is not called,
+ the context will automatically be closed when it is garbage collected,
+ in which case you may see a ResourceWarning about the unclosed context.
+ """
+ super().term()
+
+ # -------------------------------------------------------------------------
+ # Hooks for ctxopt completion
+ # -------------------------------------------------------------------------
+
+ def __dir__(self) -> list[str]:
+ keys = dir(self.__class__)
+ keys.extend(ContextOption.__members__)
+ return keys
+
+ # -------------------------------------------------------------------------
+ # Creating Sockets
+ # -------------------------------------------------------------------------
+
+ def _add_socket(self, socket: Any) -> None:
+ """Add a weakref to a socket for Context.destroy / reference counting"""
+ self._sockets.add(socket)
+
+ def _rm_socket(self, socket: Any) -> None:
+ """Remove a socket for Context.destroy / reference counting"""
+ # allow _sockets to be None in case of process teardown
+ if getattr(self, "_sockets", None) is not None:
+ self._sockets.discard(socket)
+
+ def destroy(self, linger: int | None = None) -> None:
+ """Close all sockets associated with this context and then terminate
+ the context.
+
+ .. warning::
+
+ destroy involves calling :meth:`Socket.close`, which is **NOT** threadsafe.
+ If there are active sockets in other threads, this must not be called.
+
+ Parameters
+ ----------
+
+ linger : int, optional
+ If specified, set LINGER on sockets prior to closing them.
+ """
+ if self.closed:
+ return
+
+ sockets: list[_SocketType] = list(getattr(self, "_sockets", None) or [])
+ for s in sockets:
+ if s and not s.closed:
+ if self._warn_destroy_close and warn is not None:
+ # warn can be None during process teardown
+ warn(
+ f"Destroying context with unclosed socket {s}",
+ ResourceWarning,
+ stacklevel=3,
+ source=s,
+ )
+ if linger is not None:
+ s.setsockopt(SocketOption.LINGER, linger)
+ s.close()
+
+ self.term()
+
+ def socket(
+ self: _ContextType,
+ socket_type: int,
+ socket_class: Callable[[_ContextType, int], _SocketType] | None = None,
+ **kwargs: Any,
+ ) -> _SocketType:
+ """Create a Socket associated with this Context.
+
+ Parameters
+ ----------
+ socket_type : int
+ The socket type, which can be any of the 0MQ socket types:
+ REQ, REP, PUB, SUB, PAIR, DEALER, ROUTER, PULL, PUSH, etc.
+
+ socket_class: zmq.Socket
+ The socket class to instantiate, if different from the default for this Context.
+ e.g. for creating an asyncio socket attached to a default Context or vice versa.
+
+ .. versionadded:: 25
+
+ kwargs:
+ will be passed to the __init__ method of the socket class.
+ """
+ if self.closed:
+ raise ZMQError(Errno.ENOTSUP)
+ if socket_class is None:
+ socket_class = self._socket_class
+ s: _SocketType = (
+ socket_class( # set PYTHONTRACEMALLOC=2 to get the calling frame
+ self, socket_type, **kwargs
+ )
+ )
+ for opt, value in self.sockopts.items():
+ try:
+ s.setsockopt(opt, value)
+ except ZMQError:
+ # ignore ZMQErrors, which are likely for socket options
+ # that do not apply to a particular socket type, e.g.
+ # SUBSCRIBE for non-SUB sockets.
+ pass
+ self._add_socket(s)
+ return s
+
+ def setsockopt(self, opt: int, value: Any) -> None:
+ """set default socket options for new sockets created by this Context
+
+ .. versionadded:: 13.0
+ """
+ self.sockopts[opt] = value
+
+ def getsockopt(self, opt: int) -> OptValT:
+ """get default socket options for new sockets created by this Context
+
+ .. versionadded:: 13.0
+ """
+ return self.sockopts[opt]
+
+ def _set_attr_opt(self, name: str, opt: int, value: OptValT) -> None:
+ """set default sockopts as attributes"""
+ if name in ContextOption.__members__:
+ return self.set(opt, value)
+ elif name in SocketOption.__members__:
+ self.sockopts[opt] = value
+ else:
+ raise AttributeError(f"No such context or socket option: {name}")
+
+ def _get_attr_opt(self, name: str, opt: int) -> OptValT:
+ """get default sockopts as attributes"""
+ if name in ContextOption.__members__:
+ return self.get(opt)
+ else:
+ if opt not in self.sockopts:
+ raise AttributeError(name)
+ else:
+ return self.sockopts[opt]
+
+ def __delattr__(self, key: str) -> None:
+ """delete default sockopts as attributes"""
+ if key in self.__dict__:
+ self.__dict__.pop(key)
+ return
+ key = key.upper()
+ try:
+ opt = getattr(SocketOption, key)
+ except AttributeError:
+ raise AttributeError(f"No such socket option: {key!r}")
+ else:
+ if opt not in self.sockopts:
+ raise AttributeError(key)
+ else:
+ del self.sockopts[opt]
+
+
+SyncContext: TypeAlias = Context[SyncSocket]
+
+
+__all__ = ['Context', 'SyncContext']
diff --git a/lib/python3.12/site-packages/zmq/sugar/frame.py b/lib/python3.12/site-packages/zmq/sugar/frame.py
new file mode 100644
index 0000000000000000000000000000000000000000..3239d357e1c67bc0bc41b3e01b479f3c893544f2
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/frame.py
@@ -0,0 +1,134 @@
+"""0MQ Frame pure Python methods."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import zmq
+from zmq.backend import Frame as FrameBase
+
+from .attrsettr import AttributeSetter
+
+
+def _draft(v, feature):
+ zmq.error._check_version(v, feature)
+ if not zmq.DRAFT_API:
+ raise RuntimeError(
+ f"libzmq and pyzmq must be built with draft support for {feature}"
+ )
+
+
+class Frame(FrameBase, AttributeSetter):
+ """
+ A zmq message Frame class for non-copying send/recvs and access to message properties.
+
+ A ``zmq.Frame`` wraps an underlying ``zmq_msg_t``.
+
+ Message *properties* can be accessed by treating a Frame like a dictionary (``frame["User-Id"]``).
+
+ .. versionadded:: 14.4, libzmq 4
+
+ Frames created by ``recv(copy=False)`` can be used to access message properties and attributes,
+ such as the CURVE User-Id.
+
+ For example::
+
+ frames = socket.recv_multipart(copy=False)
+ user_id = frames[0]["User-Id"]
+
+ This class is used if you want to do non-copying send and recvs.
+ When you pass a chunk of bytes to this class, e.g. ``Frame(buf)``, the
+ ref-count of `buf` is increased by two: once because the Frame saves `buf` as
+ an instance attribute and another because a ZMQ message is created that
+ points to the buffer of `buf`. This second ref-count increase makes sure
+ that `buf` lives until all messages that use it have been sent.
+ Once 0MQ sends all the messages and it doesn't need the buffer of ``buf``,
+ 0MQ will call ``Py_DECREF(s)``.
+
+ Parameters
+ ----------
+
+ data : object, optional
+ any object that provides the buffer interface will be used to
+ construct the 0MQ message data.
+ track : bool
+ whether a MessageTracker_ should be created to track this object.
+ Tracking a message has a cost at creation, because it creates a threadsafe
+ Event object.
+ copy : bool
+ default: use copy_threshold
+ Whether to create a copy of the data to pass to libzmq
+ or share the memory with libzmq.
+ If unspecified, copy_threshold is used.
+ copy_threshold: int
+ default: :const:`zmq.COPY_THRESHOLD`
+ If copy is unspecified, messages smaller than this many bytes
+ will be copied and messages larger than this will be shared with libzmq.
+ """
+
+ def __getitem__(self, key):
+ # map Frame['User-Id'] to Frame.get('User-Id')
+ return self.get(key)
+
+ def __repr__(self):
+ """Return the str form of the message."""
+ nbytes = len(self)
+ msg_suffix = ""
+ if nbytes > 16:
+ msg_bytes = bytes(memoryview(self.buffer)[:12])
+ if nbytes >= 1e9:
+ unit = "GB"
+ n = nbytes // 1e9
+ elif nbytes >= 2**20:
+ unit = "MB"
+ n = nbytes // 1e6
+ elif nbytes >= 1e3:
+ unit = "kB"
+ n = nbytes // 1e3
+ else:
+ unit = "B"
+ n = nbytes
+ msg_suffix = f'...{n:.0f}{unit}'
+ else:
+ msg_bytes = self.bytes
+
+ _module = self.__class__.__module__
+ if _module == "zmq.sugar.frame":
+ _module = "zmq"
+ return f"<{_module}.{self.__class__.__name__}({msg_bytes!r}{msg_suffix})>"
+
+ @property
+ def group(self):
+ """The RADIO-DISH group of the message.
+
+ Requires libzmq >= 4.2 and pyzmq built with draft APIs enabled.
+
+ .. versionadded:: 17
+ """
+ _draft((4, 2), "RADIO-DISH")
+ return self.get('group')
+
+ @group.setter
+ def group(self, group):
+ _draft((4, 2), "RADIO-DISH")
+ self.set('group', group)
+
+ @property
+ def routing_id(self):
+ """The CLIENT-SERVER routing id of the message.
+
+ Requires libzmq >= 4.2 and pyzmq built with draft APIs enabled.
+
+ .. versionadded:: 17
+ """
+ _draft((4, 2), "CLIENT-SERVER")
+ return self.get('routing_id')
+
+ @routing_id.setter
+ def routing_id(self, routing_id):
+ _draft((4, 2), "CLIENT-SERVER")
+ self.set('routing_id', routing_id)
+
+
+# keep deprecated alias
+Message = Frame
+__all__ = ['Frame', 'Message']
diff --git a/lib/python3.12/site-packages/zmq/sugar/poll.py b/lib/python3.12/site-packages/zmq/sugar/poll.py
new file mode 100644
index 0000000000000000000000000000000000000000..27baad46e75626d68a0691b6c89ba0fbd7c353e2
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/poll.py
@@ -0,0 +1,172 @@
+"""0MQ polling related functions and classes."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import annotations
+
+from typing import Any
+
+from zmq.backend import zmq_poll
+from zmq.constants import POLLERR, POLLIN, POLLOUT
+
+# -----------------------------------------------------------------------------
+# Polling related methods
+# -----------------------------------------------------------------------------
+
+
+class Poller:
+ """A stateful poll interface that mirrors Python's built-in poll."""
+
+ sockets: list[tuple[Any, int]]
+ _map: dict
+
+ def __init__(self) -> None:
+ self.sockets = []
+ self._map = {}
+
+ def __contains__(self, socket: Any) -> bool:
+ return socket in self._map
+
+ def register(self, socket: Any, flags: int = POLLIN | POLLOUT):
+ """p.register(socket, flags=POLLIN|POLLOUT)
+
+ Register a 0MQ socket or native fd for I/O monitoring.
+
+ register(s,0) is equivalent to unregister(s).
+
+ Parameters
+ ----------
+ socket : zmq.Socket or native socket
+ A zmq.Socket or any Python object having a ``fileno()``
+ method that returns a valid file descriptor.
+ flags : int
+ The events to watch for. Can be POLLIN, POLLOUT or POLLIN|POLLOUT.
+ If `flags=0`, socket will be unregistered.
+ """
+ if flags:
+ if socket in self._map:
+ idx = self._map[socket]
+ self.sockets[idx] = (socket, flags)
+ else:
+ idx = len(self.sockets)
+ self.sockets.append((socket, flags))
+ self._map[socket] = idx
+ elif socket in self._map:
+ # uregister sockets registered with no events
+ self.unregister(socket)
+ else:
+ # ignore new sockets with no events
+ pass
+
+ def modify(self, socket, flags=POLLIN | POLLOUT):
+ """Modify the flags for an already registered 0MQ socket or native fd."""
+ self.register(socket, flags)
+
+ def unregister(self, socket: Any):
+ """Remove a 0MQ socket or native fd for I/O monitoring.
+
+ Parameters
+ ----------
+ socket : Socket
+ The socket instance to stop polling.
+ """
+ idx = self._map.pop(socket)
+ self.sockets.pop(idx)
+ # shift indices after deletion
+ for socket, flags in self.sockets[idx:]:
+ self._map[socket] -= 1
+
+ def poll(self, timeout: int | None = None) -> list[tuple[Any, int]]:
+ """Poll the registered 0MQ or native fds for I/O.
+
+ If there are currently events ready to be processed, this function will return immediately.
+ Otherwise, this function will return as soon the first event is available or after timeout
+ milliseconds have elapsed.
+
+ Parameters
+ ----------
+ timeout : int
+ The timeout in milliseconds. If None, no `timeout` (infinite). This
+ is in milliseconds to be compatible with ``select.poll()``.
+
+ Returns
+ -------
+ events : list
+ The list of events that are ready to be processed.
+ This is a list of tuples of the form ``(socket, event_mask)``, where the 0MQ Socket
+ or integer fd is the first element, and the poll event mask (POLLIN, POLLOUT) is the second.
+ It is common to call ``events = dict(poller.poll())``,
+ which turns the list of tuples into a mapping of ``socket : event_mask``.
+ """
+ if timeout is None or timeout < 0:
+ timeout = -1
+ elif isinstance(timeout, float):
+ timeout = int(timeout)
+ return zmq_poll(self.sockets, timeout=timeout)
+
+
+def select(
+ rlist: list, wlist: list, xlist: list, timeout: float | None = None
+) -> tuple[list, list, list]:
+ """select(rlist, wlist, xlist, timeout=None) -> (rlist, wlist, xlist)
+
+ Return the result of poll as a lists of sockets ready for r/w/exception.
+
+ This has the same interface as Python's built-in ``select.select()`` function.
+
+ Parameters
+ ----------
+ timeout : float, optional
+ The timeout in seconds. If None, no timeout (infinite). This is in seconds to be
+ compatible with ``select.select()``.
+ rlist : list
+ sockets/FDs to be polled for read events
+ wlist : list
+ sockets/FDs to be polled for write events
+ xlist : list
+ sockets/FDs to be polled for error events
+
+ Returns
+ -------
+ rlist: list
+ list of sockets or FDs that are readable
+ wlist: list
+ list of sockets or FDs that are writable
+ xlist: list
+ list of sockets or FDs that had error events (rare)
+ """
+ if timeout is None:
+ timeout = -1
+ # Convert from sec -> ms for zmq_poll.
+ # zmq_poll accepts 3.x style timeout in ms
+ timeout = int(timeout * 1000.0)
+ if timeout < 0:
+ timeout = -1
+ sockets = []
+ for s in set(rlist + wlist + xlist):
+ flags = 0
+ if s in rlist:
+ flags |= POLLIN
+ if s in wlist:
+ flags |= POLLOUT
+ if s in xlist:
+ flags |= POLLERR
+ sockets.append((s, flags))
+ return_sockets = zmq_poll(sockets, timeout)
+ rlist, wlist, xlist = [], [], []
+ for s, flags in return_sockets:
+ if flags & POLLIN:
+ rlist.append(s)
+ if flags & POLLOUT:
+ wlist.append(s)
+ if flags & POLLERR:
+ xlist.append(s)
+ return rlist, wlist, xlist
+
+
+# -----------------------------------------------------------------------------
+# Symbols to export
+# -----------------------------------------------------------------------------
+
+__all__ = ['Poller', 'select']
diff --git a/lib/python3.12/site-packages/zmq/sugar/socket.py b/lib/python3.12/site-packages/zmq/sugar/socket.py
new file mode 100644
index 0000000000000000000000000000000000000000..168fcaa116174f679a1d3a317b783690e1ac4967
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/socket.py
@@ -0,0 +1,1125 @@
+"""0MQ Socket pure Python methods."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import annotations
+
+import errno
+import pickle
+import random
+import sys
+from typing import (
+ Any,
+ Callable,
+ Generic,
+ List,
+ Literal,
+ Sequence,
+ TypeVar,
+ Union,
+ cast,
+ overload,
+)
+from warnings import warn
+
+import zmq
+from zmq._typing import TypeAlias
+from zmq.backend import Socket as SocketBase
+from zmq.error import ZMQBindError, ZMQError
+from zmq.utils import jsonapi
+from zmq.utils.interop import cast_int_addr
+
+from ..constants import SocketOption, SocketType, _OptType
+from .attrsettr import AttributeSetter
+from .poll import Poller
+
+try:
+ DEFAULT_PROTOCOL = pickle.DEFAULT_PROTOCOL
+except AttributeError:
+ DEFAULT_PROTOCOL = pickle.HIGHEST_PROTOCOL
+
+_SocketType = TypeVar("_SocketType", bound="Socket")
+
+_JSONType: TypeAlias = "int | str | bool | list[_JSONType] | dict[str, _JSONType]"
+
+
+class _SocketContext(Generic[_SocketType]):
+ """Context Manager for socket bind/unbind"""
+
+ socket: _SocketType
+ kind: str
+ addr: str
+
+ def __repr__(self):
+ return f""
+
+ def __init__(
+ self: _SocketContext[_SocketType], socket: _SocketType, kind: str, addr: str
+ ):
+ assert kind in {"bind", "connect"}
+ self.socket = socket
+ self.kind = kind
+ self.addr = addr
+
+ def __enter__(self: _SocketContext[_SocketType]) -> _SocketType:
+ return self.socket
+
+ def __exit__(self, *args):
+ if self.socket.closed:
+ return
+ if self.kind == "bind":
+ self.socket.unbind(self.addr)
+ elif self.kind == "connect":
+ self.socket.disconnect(self.addr)
+
+
+SocketReturnType = TypeVar("SocketReturnType")
+
+
+class Socket(SocketBase, AttributeSetter, Generic[SocketReturnType]):
+ """The ZMQ socket object
+
+ To create a Socket, first create a Context::
+
+ ctx = zmq.Context.instance()
+
+ then call ``ctx.socket(socket_type)``::
+
+ s = ctx.socket(zmq.ROUTER)
+
+ .. versionadded:: 25
+
+ Sockets can now be shadowed by passing another Socket.
+ This helps in creating an async copy of a sync socket or vice versa::
+
+ s = zmq.Socket(async_socket)
+
+ Which previously had to be::
+
+ s = zmq.Socket.shadow(async_socket.underlying)
+ """
+
+ _shadow = False
+ _shadow_obj = None
+ _monitor_socket = None
+ _type_name = 'UNKNOWN'
+
+ @overload
+ def __init__(
+ self: Socket[bytes],
+ ctx_or_socket: zmq.Context,
+ socket_type: int,
+ *,
+ copy_threshold: int | None = None,
+ ): ...
+
+ @overload
+ def __init__(
+ self: Socket[bytes],
+ *,
+ shadow: Socket | int,
+ copy_threshold: int | None = None,
+ ): ...
+
+ @overload
+ def __init__(
+ self: Socket[bytes],
+ ctx_or_socket: Socket,
+ ): ...
+
+ def __init__(
+ self: Socket[bytes],
+ ctx_or_socket: zmq.Context | Socket | None = None,
+ socket_type: int = 0,
+ *,
+ shadow: Socket | int = 0,
+ copy_threshold: int | None = None,
+ ):
+ shadow_context: zmq.Context | None = None
+ if isinstance(ctx_or_socket, zmq.Socket):
+ # positional Socket(other_socket)
+ shadow = ctx_or_socket
+ ctx_or_socket = None
+
+ shadow_address: int = 0
+
+ if shadow:
+ self._shadow = True
+ # hold a reference to the shadow object
+ self._shadow_obj = shadow
+ if not isinstance(shadow, int):
+ if isinstance(shadow, zmq.Socket):
+ shadow_context = shadow.context
+ try:
+ shadow = cast(int, shadow.underlying)
+ except AttributeError:
+ pass
+ shadow_address = cast_int_addr(shadow)
+ else:
+ self._shadow = False
+
+ super().__init__(
+ ctx_or_socket,
+ socket_type,
+ shadow=shadow_address,
+ copy_threshold=copy_threshold,
+ )
+ if self._shadow_obj and shadow_context:
+ # keep self.context reference if shadowing a Socket object
+ self.context = shadow_context
+
+ try:
+ socket_type = cast(int, self.get(zmq.TYPE))
+ except Exception:
+ pass
+ else:
+ try:
+ self.__dict__["type"] = stype = SocketType(socket_type)
+ except ValueError:
+ self._type_name = str(socket_type)
+ else:
+ self._type_name = stype.name
+
+ def __del__(self):
+ if not self._shadow and not self.closed:
+ if warn is not None:
+ # warn can be None during process teardown
+ warn(
+ f"Unclosed socket {self}",
+ ResourceWarning,
+ stacklevel=2,
+ source=self,
+ )
+ self.close()
+
+ _repr_cls = "zmq.Socket"
+
+ def __repr__(self):
+ cls = self.__class__
+ # look up _repr_cls on exact class, not inherited
+ _repr_cls = cls.__dict__.get("_repr_cls", None)
+ if _repr_cls is None:
+ _repr_cls = f"{cls.__module__}.{cls.__name__}"
+
+ closed = ' closed' if self._closed else ''
+
+ return f"<{_repr_cls}(zmq.{self._type_name}) at {hex(id(self))}{closed}>"
+
+ # socket as context manager:
+ def __enter__(self: _SocketType) -> _SocketType:
+ """Sockets are context managers
+
+ .. versionadded:: 14.4
+ """
+ return self
+
+ def __exit__(self, *args, **kwargs):
+ self.close()
+
+ # -------------------------------------------------------------------------
+ # Socket creation
+ # -------------------------------------------------------------------------
+
+ def __copy__(self: _SocketType, memo=None) -> _SocketType:
+ """Copying a Socket creates a shadow copy"""
+ return self.__class__.shadow(self.underlying)
+
+ __deepcopy__ = __copy__
+
+ @classmethod
+ def shadow(cls: type[_SocketType], address: int | zmq.Socket) -> _SocketType:
+ """Shadow an existing libzmq socket
+
+ address is a zmq.Socket or an integer (or FFI pointer)
+ representing the address of the libzmq socket.
+
+ .. versionadded:: 14.1
+
+ .. versionadded:: 25
+ Support for shadowing `zmq.Socket` objects,
+ instead of just integer addresses.
+ """
+ return cls(shadow=address)
+
+ def close(self, linger=None) -> None:
+ """
+ Close the socket.
+
+ If linger is specified, LINGER sockopt will be set prior to closing.
+
+ Note: closing a zmq Socket may not close the underlying sockets
+ if there are undelivered messages.
+ Only after all messages are delivered or discarded by reaching the socket's LINGER timeout
+ (default: forever)
+ will the underlying sockets be closed.
+
+ This can be called to close the socket by hand. If this is not
+ called, the socket will automatically be closed when it is
+ garbage collected,
+ in which case you may see a ResourceWarning about the unclosed socket.
+ """
+ if self.context:
+ self.context._rm_socket(self)
+ super().close(linger=linger)
+
+ # -------------------------------------------------------------------------
+ # Connect/Bind context managers
+ # -------------------------------------------------------------------------
+
+ def _connect_cm(self: _SocketType, addr: str) -> _SocketContext[_SocketType]:
+ """Context manager to disconnect on exit
+
+ .. versionadded:: 20.0
+ """
+ return _SocketContext(self, 'connect', addr)
+
+ def _bind_cm(self: _SocketType, addr: str) -> _SocketContext[_SocketType]:
+ """Context manager to unbind on exit
+
+ .. versionadded:: 20.0
+ """
+ try:
+ # retrieve last_endpoint
+ # to support binding on random ports via
+ # `socket.bind('tcp://127.0.0.1:0')`
+ addr = cast(bytes, self.get(zmq.LAST_ENDPOINT)).decode("utf8")
+ except (AttributeError, ZMQError, UnicodeDecodeError):
+ pass
+ return _SocketContext(self, 'bind', addr)
+
+ def bind(self: _SocketType, addr: str) -> _SocketContext[_SocketType]:
+ """s.bind(addr)
+
+ Bind the socket to an address.
+
+ This causes the socket to listen on a network port. Sockets on the
+ other side of this connection will use ``Socket.connect(addr)`` to
+ connect to this socket.
+
+ Returns a context manager which will call unbind on exit.
+
+ .. versionadded:: 20.0
+ Can be used as a context manager.
+
+ .. versionadded:: 26.0
+ binding to port 0 can be used as a context manager
+ for binding to a random port.
+ The URL can be retrieved as `socket.last_endpoint`.
+
+ Parameters
+ ----------
+ addr : str
+ The address string. This has the form 'protocol://interface:port',
+ for example 'tcp://127.0.0.1:5555'. Protocols supported include
+ tcp, udp, pgm, epgm, inproc and ipc. If the address is unicode, it is
+ encoded to utf-8 first.
+
+ """
+ try:
+ super().bind(addr)
+ except ZMQError as e:
+ e.strerror += f" (addr={addr!r})"
+ raise
+ return self._bind_cm(addr)
+
+ def connect(self: _SocketType, addr: str) -> _SocketContext[_SocketType]:
+ """s.connect(addr)
+
+ Connect to a remote 0MQ socket.
+
+ Returns a context manager which will call disconnect on exit.
+
+ .. versionadded:: 20.0
+ Can be used as a context manager.
+
+ Parameters
+ ----------
+ addr : str
+ The address string. This has the form 'protocol://interface:port',
+ for example 'tcp://127.0.0.1:5555'. Protocols supported are
+ tcp, udp, pgm, inproc and ipc. If the address is unicode, it is
+ encoded to utf-8 first.
+
+ """
+ try:
+ super().connect(addr)
+ except ZMQError as e:
+ e.strerror += f" (addr={addr!r})"
+ raise
+ return self._connect_cm(addr)
+
+ # -------------------------------------------------------------------------
+ # Deprecated aliases
+ # -------------------------------------------------------------------------
+
+ @property
+ def socket_type(self) -> int:
+ warn("Socket.socket_type is deprecated, use Socket.type", DeprecationWarning)
+ return cast(int, self.type)
+
+ # -------------------------------------------------------------------------
+ # Hooks for sockopt completion
+ # -------------------------------------------------------------------------
+
+ def __dir__(self):
+ keys = dir(self.__class__)
+ keys.extend(SocketOption.__members__)
+ return keys
+
+ # -------------------------------------------------------------------------
+ # Getting/Setting options
+ # -------------------------------------------------------------------------
+ setsockopt = SocketBase.set
+ getsockopt = SocketBase.get
+
+ def __setattr__(self, key, value):
+ """Override to allow setting zmq.[UN]SUBSCRIBE even though we have a subscribe method"""
+ if key in self.__dict__:
+ object.__setattr__(self, key, value)
+ return
+ _key = key.lower()
+ if _key in ('subscribe', 'unsubscribe'):
+ if isinstance(value, str):
+ value = value.encode('utf8')
+ if _key == 'subscribe':
+ self.set(zmq.SUBSCRIBE, value)
+ else:
+ self.set(zmq.UNSUBSCRIBE, value)
+ return
+ super().__setattr__(key, value)
+
+ def fileno(self) -> int:
+ """Return edge-triggered file descriptor for this socket.
+
+ This is a read-only edge-triggered file descriptor for both read and write events on this socket.
+ It is important that all available events be consumed when an event is detected,
+ otherwise the read event will not trigger again.
+
+ .. versionadded:: 17.0
+ """
+ return self.FD
+
+ def subscribe(self, topic: str | bytes) -> None:
+ """Subscribe to a topic
+
+ Only for SUB sockets.
+
+ .. versionadded:: 15.3
+ """
+ if isinstance(topic, str):
+ topic = topic.encode('utf8')
+ self.set(zmq.SUBSCRIBE, topic)
+
+ def unsubscribe(self, topic: str | bytes) -> None:
+ """Unsubscribe from a topic
+
+ Only for SUB sockets.
+
+ .. versionadded:: 15.3
+ """
+ if isinstance(topic, str):
+ topic = topic.encode('utf8')
+ self.set(zmq.UNSUBSCRIBE, topic)
+
+ def set_string(self, option: int, optval: str, encoding='utf-8') -> None:
+ """Set socket options with a unicode object.
+
+ This is simply a wrapper for setsockopt to protect from encoding ambiguity.
+
+ See the 0MQ documentation for details on specific options.
+
+ Parameters
+ ----------
+ option : int
+ The name of the option to set. Can be any of: SUBSCRIBE,
+ UNSUBSCRIBE, IDENTITY
+ optval : str
+ The value of the option to set.
+ encoding : str
+ The encoding to be used, default is utf8
+ """
+ if not isinstance(optval, str):
+ raise TypeError(f"strings only, not {type(optval)}: {optval!r}")
+ return self.set(option, optval.encode(encoding))
+
+ setsockopt_unicode = setsockopt_string = set_string
+
+ def get_string(self, option: int, encoding='utf-8') -> str:
+ """Get the value of a socket option.
+
+ See the 0MQ documentation for details on specific options.
+
+ Parameters
+ ----------
+ option : int
+ The option to retrieve.
+
+ Returns
+ -------
+ optval : str
+ The value of the option as a unicode string.
+ """
+ if SocketOption(option)._opt_type != _OptType.bytes:
+ raise TypeError(f"option {option} will not return a string to be decoded")
+ return cast(bytes, self.get(option)).decode(encoding)
+
+ getsockopt_unicode = getsockopt_string = get_string
+
+ def bind_to_random_port(
+ self: _SocketType,
+ addr: str,
+ min_port: int = 49152,
+ max_port: int = 65536,
+ max_tries: int = 100,
+ ) -> int:
+ """Bind this socket to a random port in a range.
+
+ If the port range is unspecified, the system will choose the port.
+
+ Parameters
+ ----------
+ addr : str
+ The address string without the port to pass to ``Socket.bind()``.
+ min_port : int, optional
+ The minimum port in the range of ports to try (inclusive).
+ max_port : int, optional
+ The maximum port in the range of ports to try (exclusive).
+ max_tries : int, optional
+ The maximum number of bind attempts to make.
+
+ Returns
+ -------
+ port : int
+ The port the socket was bound to.
+
+ Raises
+ ------
+ ZMQBindError
+ if `max_tries` reached before successful bind
+ """
+ if min_port == 49152 and max_port == 65536:
+ # if LAST_ENDPOINT is supported, and min_port / max_port weren't specified,
+ # we can bind to port 0 and let the OS do the work
+ self.bind(f"{addr}:*")
+ url = cast(bytes, self.last_endpoint).decode('ascii', 'replace')
+ _, port_s = url.rsplit(':', 1)
+ return int(port_s)
+
+ for i in range(max_tries):
+ try:
+ port = random.randrange(min_port, max_port)
+ self.bind(f'{addr}:{port}')
+ except ZMQError as exception:
+ en = exception.errno
+ if en == zmq.EADDRINUSE:
+ continue
+ elif sys.platform == 'win32' and en == errno.EACCES:
+ continue
+ else:
+ raise
+ else:
+ return port
+ raise ZMQBindError("Could not bind socket to random port.")
+
+ def get_hwm(self) -> int:
+ """Get the High Water Mark.
+
+ On libzmq ≥ 3, this gets SNDHWM if available, otherwise RCVHWM
+ """
+ # return sndhwm, fallback on rcvhwm
+ try:
+ return cast(int, self.get(zmq.SNDHWM))
+ except zmq.ZMQError:
+ pass
+
+ return cast(int, self.get(zmq.RCVHWM))
+
+ def set_hwm(self, value: int) -> None:
+ """Set the High Water Mark.
+
+ On libzmq ≥ 3, this sets both SNDHWM and RCVHWM
+
+
+ .. warning::
+
+ New values only take effect for subsequent socket
+ bind/connects.
+ """
+ raised = None
+ try:
+ self.sndhwm = value
+ except Exception as e:
+ raised = e
+ try:
+ self.rcvhwm = value
+ except Exception as e:
+ raised = e
+
+ if raised:
+ raise raised
+
+ hwm = property(
+ get_hwm,
+ set_hwm,
+ None,
+ """Property for High Water Mark.
+
+ Setting hwm sets both SNDHWM and RCVHWM as appropriate.
+ It gets SNDHWM if available, otherwise RCVHWM.
+ """,
+ )
+
+ # -------------------------------------------------------------------------
+ # Sending and receiving messages
+ # -------------------------------------------------------------------------
+
+ @overload
+ def send(
+ self,
+ data: Any,
+ flags: int = ...,
+ copy: bool = ...,
+ *,
+ track: Literal[True],
+ routing_id: int | None = ...,
+ group: str | None = ...,
+ ) -> zmq.MessageTracker: ...
+
+ @overload
+ def send(
+ self,
+ data: Any,
+ flags: int = ...,
+ copy: bool = ...,
+ *,
+ track: Literal[False],
+ routing_id: int | None = ...,
+ group: str | None = ...,
+ ) -> None: ...
+
+ @overload
+ def send(
+ self,
+ data: Any,
+ flags: int = ...,
+ *,
+ copy: bool = ...,
+ routing_id: int | None = ...,
+ group: str | None = ...,
+ ) -> None: ...
+
+ @overload
+ def send(
+ self,
+ data: Any,
+ flags: int = ...,
+ copy: bool = ...,
+ track: bool = ...,
+ routing_id: int | None = ...,
+ group: str | None = ...,
+ ) -> zmq.MessageTracker | None: ...
+
+ def send(
+ self,
+ data: Any,
+ flags: int = 0,
+ copy: bool = True,
+ track: bool = False,
+ routing_id: int | None = None,
+ group: str | None = None,
+ ) -> zmq.MessageTracker | None:
+ """Send a single zmq message frame on this socket.
+
+ This queues the message to be sent by the IO thread at a later time.
+
+ With flags=NOBLOCK, this raises :class:`ZMQError` if the queue is full;
+ otherwise, this waits until space is available.
+ See :class:`Poller` for more general non-blocking I/O.
+
+ Parameters
+ ----------
+ data : bytes, Frame, memoryview
+ The content of the message. This can be any object that provides
+ the Python buffer API (i.e. `memoryview(data)` can be called).
+ flags : int
+ 0, NOBLOCK, SNDMORE, or NOBLOCK|SNDMORE.
+ copy : bool
+ Should the message be sent in a copying or non-copying manner.
+ track : bool
+ Should the message be tracked for notification that ZMQ has
+ finished with it? (ignored if copy=True)
+ routing_id : int
+ For use with SERVER sockets
+ group : str
+ For use with RADIO sockets
+
+ Returns
+ -------
+ None : if `copy` or not track
+ None if message was sent, raises an exception otherwise.
+ MessageTracker : if track and not copy
+ a MessageTracker object, whose `done` property will
+ be False until the send is completed.
+
+ Raises
+ ------
+ TypeError
+ If a unicode object is passed
+ ValueError
+ If `track=True`, but an untracked Frame is passed.
+ ZMQError
+ If the send does not succeed for any reason (including
+ if NOBLOCK is set and the outgoing queue is full).
+
+
+ .. versionchanged:: 17.0
+
+ DRAFT support for routing_id and group arguments.
+ """
+ if routing_id is not None:
+ if not isinstance(data, zmq.Frame):
+ data = zmq.Frame(
+ data,
+ track=track,
+ copy=copy or None,
+ copy_threshold=self.copy_threshold,
+ )
+ data.routing_id = routing_id
+ if group is not None:
+ if not isinstance(data, zmq.Frame):
+ data = zmq.Frame(
+ data,
+ track=track,
+ copy=copy or None,
+ copy_threshold=self.copy_threshold,
+ )
+ data.group = group
+ return super().send(data, flags=flags, copy=copy, track=track)
+
+ def send_multipart(
+ self,
+ msg_parts: Sequence,
+ flags: int = 0,
+ copy: bool = True,
+ track: bool = False,
+ **kwargs,
+ ):
+ """Send a sequence of buffers as a multipart message.
+
+ The zmq.SNDMORE flag is added to all msg parts before the last.
+
+ Parameters
+ ----------
+ msg_parts : iterable
+ A sequence of objects to send as a multipart message. Each element
+ can be any sendable object (Frame, bytes, buffer-providers)
+ flags : int, optional
+ Any valid flags for :func:`Socket.send`.
+ SNDMORE is added automatically for frames before the last.
+ copy : bool, optional
+ Should the frame(s) be sent in a copying or non-copying manner.
+ If copy=False, frames smaller than self.copy_threshold bytes
+ will be copied anyway.
+ track : bool, optional
+ Should the frame(s) be tracked for notification that ZMQ has
+ finished with it (ignored if copy=True).
+
+ Returns
+ -------
+ None : if copy or not track
+ MessageTracker : if track and not copy
+ a MessageTracker object, whose `done` property will
+ be False until the last send is completed.
+ """
+ # typecheck parts before sending:
+ for i, msg in enumerate(msg_parts):
+ if isinstance(msg, (zmq.Frame, bytes, memoryview)):
+ continue
+ try:
+ memoryview(msg)
+ except Exception:
+ rmsg = repr(msg)
+ if len(rmsg) > 32:
+ rmsg = rmsg[:32] + '...'
+ raise TypeError(
+ f"Frame {i} ({rmsg}) does not support the buffer interface."
+ )
+ for msg in msg_parts[:-1]:
+ self.send(msg, zmq.SNDMORE | flags, copy=copy, track=track)
+ # Send the last part without the extra SNDMORE flag.
+ return self.send(msg_parts[-1], flags, copy=copy, track=track)
+
+ @overload
+ def recv_multipart(
+ self, flags: int = ..., *, copy: Literal[True], track: bool = ...
+ ) -> list[bytes]: ...
+
+ @overload
+ def recv_multipart(
+ self, flags: int = ..., *, copy: Literal[False], track: bool = ...
+ ) -> list[zmq.Frame]: ...
+
+ @overload
+ def recv_multipart(self, flags: int = ..., *, track: bool = ...) -> list[bytes]: ...
+
+ @overload
+ def recv_multipart(
+ self, flags: int = 0, copy: bool = True, track: bool = False
+ ) -> list[zmq.Frame] | list[bytes]: ...
+
+ def recv_multipart(
+ self, flags: int = 0, copy: bool = True, track: bool = False
+ ) -> list[zmq.Frame] | list[bytes]:
+ """Receive a multipart message as a list of bytes or Frame objects
+
+ Parameters
+ ----------
+ flags : int, optional
+ Any valid flags for :func:`Socket.recv`.
+ copy : bool, optional
+ Should the message frame(s) be received in a copying or non-copying manner?
+ If False a Frame object is returned for each part, if True a copy of
+ the bytes is made for each frame.
+ track : bool, optional
+ Should the message frame(s) be tracked for notification that ZMQ has
+ finished with it? (ignored if copy=True)
+
+ Returns
+ -------
+ msg_parts : list
+ A list of frames in the multipart message; either Frames or bytes,
+ depending on `copy`.
+
+ Raises
+ ------
+ ZMQError
+ for any of the reasons :func:`~Socket.recv` might fail
+ """
+ parts = [self.recv(flags, copy=copy, track=track)]
+ # have first part already, only loop while more to receive
+ while self.getsockopt(zmq.RCVMORE):
+ part = self.recv(flags, copy=copy, track=track)
+ parts.append(part)
+ # cast List[Union] to Union[List]
+ # how do we get mypy to recognize that return type is invariant on `copy`?
+ return cast(Union[List[zmq.Frame], List[bytes]], parts)
+
+ def _deserialize(
+ self,
+ recvd: bytes,
+ load: Callable[[bytes], Any],
+ ) -> Any:
+ """Deserialize a received message
+
+ Override in subclass (e.g. Futures) if recvd is not the raw bytes.
+
+ The default implementation expects bytes and returns the deserialized message immediately.
+
+ Parameters
+ ----------
+
+ load: callable
+ Callable that deserializes bytes
+ recvd:
+ The object returned by self.recv
+
+ """
+ return load(recvd)
+
+ def send_serialized(self, msg, serialize, flags=0, copy=True, **kwargs):
+ """Send a message with a custom serialization function.
+
+ .. versionadded:: 17
+
+ Parameters
+ ----------
+ msg : The message to be sent. Can be any object serializable by `serialize`.
+ serialize : callable
+ The serialization function to use.
+ serialize(msg) should return an iterable of sendable message frames
+ (e.g. bytes objects), which will be passed to send_multipart.
+ flags : int, optional
+ Any valid flags for :func:`Socket.send`.
+ copy : bool, optional
+ Whether to copy the frames.
+
+ """
+ frames = serialize(msg)
+ return self.send_multipart(frames, flags=flags, copy=copy, **kwargs)
+
+ def recv_serialized(self, deserialize, flags=0, copy=True):
+ """Receive a message with a custom deserialization function.
+
+ .. versionadded:: 17
+
+ Parameters
+ ----------
+ deserialize : callable
+ The deserialization function to use.
+ deserialize will be called with one argument: the list of frames
+ returned by recv_multipart() and can return any object.
+ flags : int, optional
+ Any valid flags for :func:`Socket.recv`.
+ copy : bool, optional
+ Whether to recv bytes or Frame objects.
+
+ Returns
+ -------
+ obj : object
+ The object returned by the deserialization function.
+
+ Raises
+ ------
+ ZMQError
+ for any of the reasons :func:`~Socket.recv` might fail
+ """
+ frames = self.recv_multipart(flags=flags, copy=copy)
+ return self._deserialize(frames, deserialize)
+
+ def send_string(
+ self,
+ u: str,
+ flags: int = 0,
+ copy: bool = True,
+ encoding: str = 'utf-8',
+ **kwargs,
+ ) -> zmq.Frame | None:
+ """Send a Python unicode string as a message with an encoding.
+
+ 0MQ communicates with raw bytes, so you must encode/decode
+ text (str) around 0MQ.
+
+ Parameters
+ ----------
+ u : str
+ The unicode string to send.
+ flags : int, optional
+ Any valid flags for :func:`Socket.send`.
+ encoding : str
+ The encoding to be used
+ """
+ if not isinstance(u, str):
+ raise TypeError("str objects only")
+ return self.send(u.encode(encoding), flags=flags, copy=copy, **kwargs)
+
+ send_unicode = send_string
+
+ def recv_string(self, flags: int = 0, encoding: str = 'utf-8') -> str:
+ """Receive a unicode string, as sent by send_string.
+
+ Parameters
+ ----------
+ flags : int
+ Any valid flags for :func:`Socket.recv`.
+ encoding : str
+ The encoding to be used
+
+ Returns
+ -------
+ s : str
+ The Python unicode string that arrives as encoded bytes.
+
+ Raises
+ ------
+ ZMQError
+ for any of the reasons :func:`Socket.recv` might fail
+ """
+ msg = self.recv(flags=flags)
+ return self._deserialize(msg, lambda buf: buf.decode(encoding))
+
+ recv_unicode = recv_string
+
+ def send_pyobj(
+ self, obj: Any, flags: int = 0, protocol: int = DEFAULT_PROTOCOL, **kwargs
+ ) -> zmq.Frame | None:
+ """
+ Send a Python object as a message using pickle to serialize.
+
+ .. warning::
+
+ Never deserialize an untrusted message with pickle,
+ which can involve arbitrary code execution.
+ Make sure to authenticate the sources of messages
+ before unpickling them, e.g. with transport-level security
+ (e.g. CURVE, ZAP, or IPC permissions)
+ or signed messages.
+
+ Parameters
+ ----------
+ obj : Python object
+ The Python object to send.
+ flags : int
+ Any valid flags for :func:`Socket.send`.
+ protocol : int
+ The pickle protocol number to use. The default is pickle.DEFAULT_PROTOCOL
+ where defined, and pickle.HIGHEST_PROTOCOL elsewhere.
+ """
+ msg = pickle.dumps(obj, protocol)
+ return self.send(msg, flags=flags, **kwargs)
+
+ def recv_pyobj(self, flags: int = 0) -> Any:
+ """
+ Receive a Python object as a message using UNSAFE pickle to serialize.
+
+ .. warning::
+
+ Never deserialize an untrusted message with pickle,
+ which can involve arbitrary code execution.
+ Make sure to authenticate the sources of messages
+ before unpickling them, e.g. with transport-level security
+ (such as CURVE or IPC permissions)
+ or authenticating messages themselves before deserializing.
+
+ Parameters
+ ----------
+ flags : int
+ Any valid flags for :func:`Socket.recv`.
+
+ Returns
+ -------
+ obj : Python object
+ The Python object that arrives as a message.
+
+ Raises
+ ------
+ ZMQError
+ for any of the reasons :func:`~Socket.recv` might fail
+ """
+ msg = self.recv(flags)
+ return self._deserialize(msg, pickle.loads)
+
+ def send_json(self, obj: Any, flags: int = 0, **kwargs) -> None:
+ """Send a Python object as a message using json to serialize.
+
+ Keyword arguments are passed on to json.dumps
+
+ Parameters
+ ----------
+ obj : Python object
+ The Python object to send
+ flags : int
+ Any valid flags for :func:`Socket.send`
+ """
+ send_kwargs = {}
+ for key in ('routing_id', 'group'):
+ if key in kwargs:
+ send_kwargs[key] = kwargs.pop(key)
+ msg = jsonapi.dumps(obj, **kwargs)
+ return self.send(msg, flags=flags, **send_kwargs)
+
+ def recv_json(self, flags: int = 0, **kwargs) -> _JSONType:
+ """Receive a Python object as a message using json to serialize.
+
+ Keyword arguments are passed on to json.loads
+
+ Parameters
+ ----------
+ flags : int
+ Any valid flags for :func:`Socket.recv`.
+
+ Returns
+ -------
+ obj : Python object
+ The Python object that arrives as a message.
+
+ Raises
+ ------
+ ZMQError
+ for any of the reasons :func:`~Socket.recv` might fail
+ """
+ msg = self.recv(flags)
+ return self._deserialize(msg, lambda buf: jsonapi.loads(buf, **kwargs))
+
+ _poller_class = Poller
+
+ def poll(self, timeout: int | None = None, flags: int = zmq.POLLIN) -> int:
+ """Poll the socket for events.
+
+ See :class:`Poller` to wait for multiple sockets at once.
+
+ Parameters
+ ----------
+ timeout : int
+ The timeout (in milliseconds) to wait for an event. If unspecified
+ (or specified None), will wait forever for an event.
+ flags : int
+ default: POLLIN.
+ POLLIN, POLLOUT, or POLLIN|POLLOUT. The event flags to poll for.
+
+ Returns
+ -------
+ event_mask : int
+ The poll event mask (POLLIN, POLLOUT),
+ 0 if the timeout was reached without an event.
+ """
+
+ if self.closed:
+ raise ZMQError(zmq.ENOTSUP)
+
+ p = self._poller_class()
+ p.register(self, flags)
+ evts = dict(p.poll(timeout))
+ # return 0 if no events, otherwise return event bitfield
+ return evts.get(self, 0)
+
+ def get_monitor_socket(
+ self: _SocketType, events: int | None = None, addr: str | None = None
+ ) -> _SocketType:
+ """Return a connected PAIR socket ready to receive the event notifications.
+
+ .. versionadded:: libzmq-4.0
+ .. versionadded:: 14.0
+
+ Parameters
+ ----------
+ events : int
+ default: `zmq.EVENT_ALL`
+ The bitmask defining which events are wanted.
+ addr : str
+ The optional endpoint for the monitoring sockets.
+
+ Returns
+ -------
+ socket : zmq.Socket
+ The PAIR socket, connected and ready to receive messages.
+ """
+ # safe-guard, method only available on libzmq >= 4
+ if zmq.zmq_version_info() < (4,):
+ raise NotImplementedError(
+ f"get_monitor_socket requires libzmq >= 4, have {zmq.zmq_version()}"
+ )
+
+ # if already monitoring, return existing socket
+ if self._monitor_socket:
+ if self._monitor_socket.closed:
+ self._monitor_socket = None
+ else:
+ return self._monitor_socket
+
+ if addr is None:
+ # create endpoint name from internal fd
+ addr = f"inproc://monitor.s-{self.FD}"
+ if events is None:
+ # use all events
+ events = zmq.EVENT_ALL
+ # attach monitoring socket
+ self.monitor(addr, events)
+ # create new PAIR socket and connect it
+ self._monitor_socket = self.context.socket(zmq.PAIR)
+ self._monitor_socket.connect(addr)
+ return self._monitor_socket
+
+ def disable_monitor(self) -> None:
+ """Shutdown the PAIR socket (created using get_monitor_socket)
+ that is serving socket events.
+
+ .. versionadded:: 14.4
+ """
+ self._monitor_socket = None
+ self.monitor(None, 0)
+
+
+SyncSocket: TypeAlias = Socket[bytes]
+
+__all__ = ['Socket', 'SyncSocket']
diff --git a/lib/python3.12/site-packages/zmq/sugar/stopwatch.py b/lib/python3.12/site-packages/zmq/sugar/stopwatch.py
new file mode 100644
index 0000000000000000000000000000000000000000..2001e670a92761a5c3a10e40d93f39e2c21a3a10
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/stopwatch.py
@@ -0,0 +1,36 @@
+"""Deprecated Stopwatch implementation"""
+
+# Copyright (c) PyZMQ Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+
+class Stopwatch:
+ """Deprecated zmq.Stopwatch implementation
+
+ You can use Python's builtin timers (time.monotonic, etc.).
+ """
+
+ def __init__(self):
+ import warnings
+
+ warnings.warn(
+ "zmq.Stopwatch is deprecated. Use stdlib time.monotonic and friends instead",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ self._start = 0
+ import time
+
+ try:
+ self._monotonic = time.monotonic
+ except AttributeError:
+ self._monotonic = time.time
+
+ def start(self):
+ """Start the counter"""
+ self._start = self._monotonic()
+
+ def stop(self):
+ """Return time since start in microseconds"""
+ stop = self._monotonic()
+ return int(1e6 * (stop - self._start))
diff --git a/lib/python3.12/site-packages/zmq/sugar/tracker.py b/lib/python3.12/site-packages/zmq/sugar/tracker.py
new file mode 100644
index 0000000000000000000000000000000000000000..973fdbd6e2740951969e0f071dd07837cad1d632
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/tracker.py
@@ -0,0 +1,116 @@
+"""Tracker for zero-copy messages with 0MQ."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import annotations
+
+import time
+from threading import Event
+
+from zmq.backend import Frame
+from zmq.error import NotDone
+
+
+class MessageTracker:
+ """A class for tracking if 0MQ is done using one or more messages.
+
+ When you send a 0MQ message, it is not sent immediately. The 0MQ IO thread
+ sends the message at some later time. Often you want to know when 0MQ has
+ actually sent the message though. This is complicated by the fact that
+ a single 0MQ message can be sent multiple times using different sockets.
+ This class allows you to track all of the 0MQ usages of a message.
+
+ Parameters
+ ----------
+ towatch : Event, MessageTracker, zmq.Frame
+ This objects to track. This class can track the low-level
+ Events used by the Message class, other MessageTrackers or
+ actual Messages.
+ """
+
+ events: set[Event]
+ peers: set[MessageTracker]
+
+ def __init__(self, *towatch: tuple[MessageTracker | Event | Frame]):
+ """Create a message tracker to track a set of messages.
+
+ Parameters
+ ----------
+ *towatch : tuple of Event, MessageTracker, Message instances.
+ This list of objects to track. This class can track the low-level
+ Events used by the Message class, other MessageTrackers or
+ actual Messages.
+ """
+ self.events = set()
+ self.peers = set()
+ for obj in towatch:
+ if isinstance(obj, Event):
+ self.events.add(obj)
+ elif isinstance(obj, MessageTracker):
+ self.peers.add(obj)
+ elif isinstance(obj, Frame):
+ if not obj.tracker:
+ raise ValueError("Not a tracked message")
+ self.peers.add(obj.tracker)
+ else:
+ raise TypeError(f"Require Events or Message Frames, not {type(obj)}")
+
+ @property
+ def done(self):
+ """Is 0MQ completely done with the message(s) being tracked?"""
+ for evt in self.events:
+ if not evt.is_set():
+ return False
+ for pm in self.peers:
+ if not pm.done:
+ return False
+ return True
+
+ def wait(self, timeout: float | int = -1):
+ """Wait for 0MQ to be done with the message or until `timeout`.
+
+ Parameters
+ ----------
+ timeout : float
+ default: -1, which means wait forever.
+ Maximum time in (s) to wait before raising NotDone.
+
+ Returns
+ -------
+ None
+ if done before `timeout`
+
+ Raises
+ ------
+ NotDone
+ if `timeout` reached before I am done.
+ """
+ tic = time.time()
+ remaining: float
+ if timeout is False or timeout < 0:
+ remaining = 3600 * 24 * 7 # a week
+ else:
+ remaining = timeout
+ for evt in self.events:
+ if remaining < 0:
+ raise NotDone
+ evt.wait(timeout=remaining)
+ if not evt.is_set():
+ raise NotDone
+ toc = time.time()
+ remaining -= toc - tic
+ tic = toc
+
+ for peer in self.peers:
+ if remaining < 0:
+ raise NotDone
+ peer.wait(timeout=remaining)
+ toc = time.time()
+ remaining -= toc - tic
+ tic = toc
+
+
+_FINISHED_TRACKER = MessageTracker()
+
+__all__ = ['MessageTracker', '_FINISHED_TRACKER']
diff --git a/lib/python3.12/site-packages/zmq/sugar/version.py b/lib/python3.12/site-packages/zmq/sugar/version.py
new file mode 100644
index 0000000000000000000000000000000000000000..33200d5086af936a688a605b76512e5735c25505
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/sugar/version.py
@@ -0,0 +1,67 @@
+"""PyZMQ and 0MQ version functions."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+from __future__ import annotations
+
+import re
+from typing import Match, cast
+
+from zmq.backend import zmq_version_info
+
+__version__: str = "26.4.0"
+_version_pat = re.compile(r"(\d+)\.(\d+)\.(\d+)(.*)")
+_match = cast(Match, _version_pat.match(__version__))
+_version_groups = _match.groups()
+
+VERSION_MAJOR = int(_version_groups[0])
+VERSION_MINOR = int(_version_groups[1])
+VERSION_PATCH = int(_version_groups[2])
+VERSION_EXTRA = _version_groups[3].lstrip(".")
+
+version_info: tuple[int, int, int] | tuple[int, int, int, float] = (
+ VERSION_MAJOR,
+ VERSION_MINOR,
+ VERSION_PATCH,
+)
+
+if VERSION_EXTRA:
+ version_info = (
+ VERSION_MAJOR,
+ VERSION_MINOR,
+ VERSION_PATCH,
+ float('inf'),
+ )
+
+__revision__: str = ''
+
+
+def pyzmq_version() -> str:
+ """return the version of pyzmq as a string"""
+ if __revision__:
+ return '+'.join([__version__, __revision__[:6]])
+ else:
+ return __version__
+
+
+def pyzmq_version_info() -> tuple[int, int, int] | tuple[int, int, int, float]:
+ """return the pyzmq version as a tuple of at least three numbers
+
+ If pyzmq is a development version, `inf` will be appended after the third integer.
+ """
+ return version_info
+
+
+def zmq_version() -> str:
+ """return the version of libzmq as a string"""
+ return "{}.{}.{}".format(*zmq_version_info())
+
+
+__all__ = [
+ 'zmq_version',
+ 'zmq_version_info',
+ 'pyzmq_version',
+ 'pyzmq_version_info',
+ '__version__',
+ '__revision__',
+]
diff --git a/lib/python3.12/site-packages/zmq/tests/__init__.py b/lib/python3.12/site-packages/zmq/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5be08c6a8a2c01f8bcce399252a7794d646a670d
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/tests/__init__.py
@@ -0,0 +1,262 @@
+# Copyright (c) PyZMQ Developers.
+# Distributed under the terms of the Modified BSD License.
+
+import os
+import platform
+import signal
+import sys
+import time
+import warnings
+from functools import partial
+from threading import Thread
+from typing import List
+from unittest import SkipTest, TestCase
+
+from pytest import mark
+
+import zmq
+from zmq.utils import jsonapi
+
+try:
+ import gevent
+
+ from zmq import green as gzmq
+
+ have_gevent = True
+except ImportError:
+ have_gevent = False
+
+
+PYPY = platform.python_implementation() == 'PyPy'
+
+# -----------------------------------------------------------------------------
+# skip decorators (directly from unittest)
+# -----------------------------------------------------------------------------
+warnings.warn(
+ "zmq.tests is deprecated in pyzmq 25, we recommend managing your own contexts and sockets.",
+ DeprecationWarning,
+)
+
+
+def _id(x):
+ return x
+
+
+skip_pypy = mark.skipif(PYPY, reason="Doesn't work on PyPy")
+require_zmq_4 = mark.skipif(zmq.zmq_version_info() < (4,), reason="requires zmq >= 4")
+
+# -----------------------------------------------------------------------------
+# Base test class
+# -----------------------------------------------------------------------------
+
+
+def term_context(ctx, timeout):
+ """Terminate a context with a timeout"""
+ t = Thread(target=ctx.term)
+ t.daemon = True
+ t.start()
+ t.join(timeout=timeout)
+ if t.is_alive():
+ # reset Context.instance, so the failure to term doesn't corrupt subsequent tests
+ zmq.sugar.context.Context._instance = None
+ raise RuntimeError(
+ "context could not terminate, open sockets likely remain in test"
+ )
+
+
+class BaseZMQTestCase(TestCase):
+ green = False
+ teardown_timeout = 10
+ test_timeout_seconds = int(os.environ.get("ZMQ_TEST_TIMEOUT") or 60)
+ sockets: List[zmq.Socket]
+
+ @property
+ def _is_pyzmq_test(self):
+ return self.__class__.__module__.split(".", 1)[0] == __name__.split(".", 1)[0]
+
+ @property
+ def _should_test_timeout(self):
+ return (
+ self._is_pyzmq_test
+ and hasattr(signal, 'SIGALRM')
+ and self.test_timeout_seconds
+ )
+
+ @property
+ def Context(self):
+ if self.green:
+ return gzmq.Context
+ else:
+ return zmq.Context
+
+ def socket(self, socket_type):
+ s = self.context.socket(socket_type)
+ self.sockets.append(s)
+ return s
+
+ def _alarm_timeout(self, timeout, *args):
+ raise TimeoutError(f"Test did not complete in {timeout} seconds")
+
+ def setUp(self):
+ super().setUp()
+ if self.green and not have_gevent:
+ raise SkipTest("requires gevent")
+
+ self.context = self.Context.instance()
+ self.sockets = []
+ if self._should_test_timeout:
+ # use SIGALRM to avoid test hangs
+ signal.signal(
+ signal.SIGALRM, partial(self._alarm_timeout, self.test_timeout_seconds)
+ )
+ signal.alarm(self.test_timeout_seconds)
+
+ def tearDown(self):
+ if self._should_test_timeout:
+ # cancel the timeout alarm, if there was one
+ signal.alarm(0)
+ contexts = {self.context}
+ while self.sockets:
+ sock = self.sockets.pop()
+ contexts.add(sock.context) # in case additional contexts are created
+ sock.close(0)
+ for ctx in contexts:
+ try:
+ term_context(ctx, self.teardown_timeout)
+ except Exception:
+ # reset Context.instance, so the failure to term doesn't corrupt subsequent tests
+ zmq.sugar.context.Context._instance = None
+ raise
+
+ super().tearDown()
+
+ def create_bound_pair(
+ self, type1=zmq.PAIR, type2=zmq.PAIR, interface='tcp://127.0.0.1'
+ ):
+ """Create a bound socket pair using a random port."""
+ s1 = self.context.socket(type1)
+ s1.setsockopt(zmq.LINGER, 0)
+ port = s1.bind_to_random_port(interface)
+ s2 = self.context.socket(type2)
+ s2.setsockopt(zmq.LINGER, 0)
+ s2.connect(f'{interface}:{port}')
+ self.sockets.extend([s1, s2])
+ return s1, s2
+
+ def ping_pong(self, s1, s2, msg):
+ s1.send(msg)
+ msg2 = s2.recv()
+ s2.send(msg2)
+ msg3 = s1.recv()
+ return msg3
+
+ def ping_pong_json(self, s1, s2, o):
+ if jsonapi.jsonmod is None:
+ raise SkipTest("No json library")
+ s1.send_json(o)
+ o2 = s2.recv_json()
+ s2.send_json(o2)
+ o3 = s1.recv_json()
+ return o3
+
+ def ping_pong_pyobj(self, s1, s2, o):
+ s1.send_pyobj(o)
+ o2 = s2.recv_pyobj()
+ s2.send_pyobj(o2)
+ o3 = s1.recv_pyobj()
+ return o3
+
+ def assertRaisesErrno(self, errno, func, *args, **kwargs):
+ try:
+ func(*args, **kwargs)
+ except zmq.ZMQError as e:
+ self.assertEqual(
+ e.errno,
+ errno,
+ f"wrong error raised, expected '{zmq.ZMQError(errno)}' \
+got '{zmq.ZMQError(e.errno)}'",
+ )
+ else:
+ self.fail("Function did not raise any error")
+
+ def _select_recv(self, multipart, socket, **kwargs):
+ """call recv[_multipart] in a way that raises if there is nothing to receive"""
+ # zmq 3.1 has a bug, where poll can return false positives,
+ # so we wait a little bit just in case
+ # See LIBZMQ-280 on JIRA
+ time.sleep(0.1)
+
+ r, w, x = zmq.select([socket], [], [], timeout=kwargs.pop('timeout', 5))
+ assert len(r) > 0, "Should have received a message"
+ kwargs['flags'] = zmq.DONTWAIT | kwargs.get('flags', 0)
+
+ recv = socket.recv_multipart if multipart else socket.recv
+ return recv(**kwargs)
+
+ def recv(self, socket, **kwargs):
+ """call recv in a way that raises if there is nothing to receive"""
+ return self._select_recv(False, socket, **kwargs)
+
+ def recv_multipart(self, socket, **kwargs):
+ """call recv_multipart in a way that raises if there is nothing to receive"""
+ return self._select_recv(True, socket, **kwargs)
+
+
+class PollZMQTestCase(BaseZMQTestCase):
+ pass
+
+
+class GreenTest:
+ """Mixin for making green versions of test classes"""
+
+ green = True
+ teardown_timeout = 10
+
+ def assertRaisesErrno(self, errno, func, *args, **kwargs):
+ if errno == zmq.EAGAIN:
+ raise SkipTest("Skipping because we're green.")
+ try:
+ func(*args, **kwargs)
+ except zmq.ZMQError:
+ e = sys.exc_info()[1]
+ self.assertEqual(
+ e.errno,
+ errno,
+ f"wrong error raised, expected '{zmq.ZMQError(errno)}' \
+got '{zmq.ZMQError(e.errno)}'",
+ )
+ else:
+ self.fail("Function did not raise any error")
+
+ def tearDown(self):
+ if self._should_test_timeout:
+ # cancel the timeout alarm, if there was one
+ signal.alarm(0)
+ contexts = {self.context}
+ while self.sockets:
+ sock = self.sockets.pop()
+ contexts.add(sock.context) # in case additional contexts are created
+ sock.close()
+ try:
+ gevent.joinall(
+ [gevent.spawn(ctx.term) for ctx in contexts],
+ timeout=self.teardown_timeout,
+ raise_error=True,
+ )
+ except gevent.Timeout:
+ raise RuntimeError(
+ "context could not terminate, open sockets likely remain in test"
+ )
+
+ def skip_green(self):
+ raise SkipTest("Skipping because we are green")
+
+
+def skip_green(f):
+ def skipping_test(self, *args, **kwargs):
+ if self.green:
+ raise SkipTest("Skipping because we are green")
+ else:
+ return f(self, *args, **kwargs)
+
+ return skipping_test
diff --git a/lib/python3.12/site-packages/zmq/tests/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/zmq/tests/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..265c9bcdf03fae570904a2310c8c9a2a055cab9a
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/tests/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/utils/__init__.py b/lib/python3.12/site-packages/zmq/utils/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lib/python3.12/site-packages/zmq/utils/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/zmq/utils/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..24127407966eec86d2a6f96854908f915f42c9d5
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/utils/__pycache__/__init__.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/utils/__pycache__/garbage.cpython-312.pyc b/lib/python3.12/site-packages/zmq/utils/__pycache__/garbage.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..473964386cf48aba10bd2594fe285600026ee59c
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/utils/__pycache__/garbage.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/utils/__pycache__/interop.cpython-312.pyc b/lib/python3.12/site-packages/zmq/utils/__pycache__/interop.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7400d79a4a9c8825614720179ae138721a5402c3
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/utils/__pycache__/interop.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/utils/__pycache__/jsonapi.cpython-312.pyc b/lib/python3.12/site-packages/zmq/utils/__pycache__/jsonapi.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..09a7ca398de0a802c7fa7d7b91f877a0bcc2f2ec
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/utils/__pycache__/jsonapi.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/utils/__pycache__/monitor.cpython-312.pyc b/lib/python3.12/site-packages/zmq/utils/__pycache__/monitor.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..aa3e0081d9ed7d148523c7850ab35f0e070ceeb7
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/utils/__pycache__/monitor.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/utils/__pycache__/strtypes.cpython-312.pyc b/lib/python3.12/site-packages/zmq/utils/__pycache__/strtypes.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..73a64904a8b30abbc91523dc2fb63a8e6e658159
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/utils/__pycache__/strtypes.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/utils/__pycache__/win32.cpython-312.pyc b/lib/python3.12/site-packages/zmq/utils/__pycache__/win32.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..acd207fd41c2fec90ea15b823d8bbbc192d8b8b3
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/utils/__pycache__/win32.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/utils/__pycache__/z85.cpython-312.pyc b/lib/python3.12/site-packages/zmq/utils/__pycache__/z85.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..36c8b0a87d0371b14f89418b7fc1eb27bee9779c
Binary files /dev/null and b/lib/python3.12/site-packages/zmq/utils/__pycache__/z85.cpython-312.pyc differ
diff --git a/lib/python3.12/site-packages/zmq/utils/garbage.py b/lib/python3.12/site-packages/zmq/utils/garbage.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c700313d917f2dd44a3d62b4a2a79bb0a1f9de0
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/garbage.py
@@ -0,0 +1,213 @@
+"""Garbage collection thread for representing zmq refcount of Python objects
+used in zero-copy sends.
+"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import atexit
+import struct
+import warnings
+from collections import namedtuple
+from os import getpid
+from threading import Event, Lock, Thread
+
+import zmq
+
+gcref = namedtuple('gcref', ['obj', 'event'])
+
+
+class GarbageCollectorThread(Thread):
+ """Thread in which garbage collection actually happens."""
+
+ def __init__(self, gc):
+ super().__init__()
+ self.gc = gc
+ self.daemon = True
+ self.pid = getpid()
+ self.ready = Event()
+
+ def run(self):
+ # detect fork at beginning of the thread
+ if getpid is None or getpid() != self.pid:
+ self.ready.set()
+ return
+ try:
+ s = self.gc.context.socket(zmq.PULL)
+ s.linger = 0
+ s.bind(self.gc.url)
+ finally:
+ self.ready.set()
+
+ while True:
+ # detect fork
+ if getpid is None or getpid() != self.pid:
+ return
+ msg = s.recv()
+ if msg == b'DIE':
+ break
+ fmt = 'L' if len(msg) == 4 else 'Q'
+ key = struct.unpack(fmt, msg)[0]
+ tup = self.gc.refs.pop(key, None)
+ if tup and tup.event:
+ tup.event.set()
+ del tup
+ s.close()
+
+
+class GarbageCollector:
+ """PyZMQ Garbage Collector
+
+ Used for representing the reference held by libzmq during zero-copy sends.
+ This object holds a dictionary, keyed by Python id,
+ of the Python objects whose memory are currently in use by zeromq.
+
+ When zeromq is done with the memory, it sends a message on an inproc PUSH socket
+ containing the packed size_t (32 or 64-bit unsigned int),
+ which is the key in the dict.
+ When the PULL socket in the gc thread receives that message,
+ the reference is popped from the dict,
+ and any tracker events that should be signaled fire.
+ """
+
+ refs = None
+ _context = None
+ _lock = None
+ url = "inproc://pyzmq.gc.01"
+
+ def __init__(self, context=None):
+ super().__init__()
+ self.refs = {}
+ self.pid = None
+ self.thread = None
+ self._context = context
+ self._lock = Lock()
+ self._stay_down = False
+ self._push = None
+ self._push_mutex = None
+ atexit.register(self._atexit)
+
+ @property
+ def context(self):
+ if self._context is None:
+ if Thread.__module__.startswith('gevent'):
+ # gevent has monkey-patched Thread, use green Context
+ from zmq import green
+
+ self._context = green.Context()
+ else:
+ self._context = zmq.Context()
+ return self._context
+
+ @context.setter
+ def context(self, ctx):
+ if self.is_alive():
+ if self.refs:
+ warnings.warn(
+ "Replacing gc context while gc is running", RuntimeWarning
+ )
+ self.stop()
+ self._context = ctx
+
+ def _atexit(self):
+ """atexit callback
+
+ sets _stay_down flag so that gc doesn't try to start up again in other atexit handlers
+ """
+ self._stay_down = True
+ self.stop()
+
+ def stop(self):
+ """stop the garbage-collection thread"""
+ if not self.is_alive():
+ return
+ self._stop()
+
+ def _clear(self):
+ """Clear state
+
+ called after stop or when setting up a new subprocess
+ """
+ self._push = None
+ self._push_mutex = None
+ self.thread = None
+ self.refs.clear()
+ self.context = None
+
+ def _stop(self):
+ push = self.context.socket(zmq.PUSH)
+ push.connect(self.url)
+ push.send(b'DIE')
+ push.close()
+ if self._push:
+ self._push.close()
+ self.thread.join()
+ self.context.term()
+ self._clear()
+
+ @property
+ def _push_socket(self):
+ """The PUSH socket for use in the zmq message destructor callback."""
+ if getattr(self, "_stay_down", False):
+ raise RuntimeError("zmq gc socket requested during shutdown")
+ if not self.is_alive() or self._push is None:
+ self._push = self.context.socket(zmq.PUSH)
+ self._push.connect(self.url)
+ return self._push
+
+ def start(self):
+ """Start a new garbage collection thread.
+
+ Creates a new zmq Context used for garbage collection.
+ Under most circumstances, this will only be called once per process.
+ """
+ if self.thread is not None and self.pid != getpid():
+ # It's re-starting, must free earlier thread's context
+ # since a fork probably broke it
+ self._clear()
+ self.pid = getpid()
+ self.refs = {}
+ self.thread = GarbageCollectorThread(self)
+ self.thread.start()
+ self.thread.ready.wait()
+
+ def is_alive(self):
+ """Is the garbage collection thread currently running?
+
+ Includes checks for process shutdown or fork.
+ """
+ if (
+ getpid is None
+ or getpid() != self.pid
+ or self.thread is None
+ or not self.thread.is_alive()
+ ):
+ return False
+ return True
+
+ def store(self, obj, event=None):
+ """store an object and (optionally) event for zero-copy"""
+ if not self.is_alive():
+ if self._stay_down:
+ return 0
+ # safely start the gc thread
+ # use lock and double check,
+ # so we don't start multiple threads
+ with self._lock:
+ if not self.is_alive():
+ self.start()
+ tup = gcref(obj, event)
+ theid = id(tup)
+ self.refs[theid] = tup
+ return theid
+
+ def __del__(self):
+ if not self.is_alive():
+ return
+ try:
+ self.stop()
+ except Exception as e:
+ raise (e)
+
+
+gc = GarbageCollector()
diff --git a/lib/python3.12/site-packages/zmq/utils/getpid_compat.h b/lib/python3.12/site-packages/zmq/utils/getpid_compat.h
new file mode 100644
index 0000000000000000000000000000000000000000..3fe1bec4c24ac472fb9ab8940da43adc1de60257
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/getpid_compat.h
@@ -0,0 +1,7 @@
+#pragma once
+#ifdef _WIN32
+ #include
+ #define getpid _getpid
+#else
+ #include
+#endif
diff --git a/lib/python3.12/site-packages/zmq/utils/interop.py b/lib/python3.12/site-packages/zmq/utils/interop.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab4ffd9a813c7c0e490428123b08b9e38c85d52b
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/interop.py
@@ -0,0 +1,29 @@
+"""Utils for interoperability with other libraries.
+
+Just CFFI pointer casting for now.
+"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from typing import Any
+
+
+def cast_int_addr(n: Any) -> int:
+ """Cast an address to a Python int
+
+ This could be a Python integer or a CFFI pointer
+ """
+ if isinstance(n, int):
+ return n
+ try:
+ import cffi # type: ignore
+ except ImportError:
+ pass
+ else:
+ # from pyzmq, this is an FFI void *
+ ffi = cffi.FFI()
+ if isinstance(n, ffi.CData):
+ return int(ffi.cast("size_t", n))
+
+ raise ValueError(f"Cannot cast {n!r} to int")
diff --git a/lib/python3.12/site-packages/zmq/utils/ipcmaxlen.h b/lib/python3.12/site-packages/zmq/utils/ipcmaxlen.h
new file mode 100644
index 0000000000000000000000000000000000000000..7af9a261be4be890aab7df2b570d59e09ff2a3e2
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/ipcmaxlen.h
@@ -0,0 +1,27 @@
+/*
+
+Platform-independant detection of IPC path max length
+
+Copyright (c) 2012 Godefroid Chapelle
+
+Distributed under the terms of the New BSD License. The full license is in
+the file LICENSE.BSD, distributed as part of this software.
+ */
+
+#pragma once
+
+#if defined(HAVE_SYS_UN_H)
+#if defined _MSC_VER
+#include
+#else
+#include
+#endif
+int get_ipc_path_max_len(void) {
+ struct sockaddr_un *dummy;
+ return sizeof(dummy->sun_path) - 1;
+}
+#else
+int get_ipc_path_max_len(void) {
+ return 0;
+}
+#endif
diff --git a/lib/python3.12/site-packages/zmq/utils/jsonapi.py b/lib/python3.12/site-packages/zmq/utils/jsonapi.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a6ee0785229f449ccc9efc3e778909699866de0
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/jsonapi.py
@@ -0,0 +1,38 @@
+"""JSON serialize to/from utf8 bytes
+
+.. versionchanged:: 22.2
+ Remove optional imports of different JSON implementations.
+ Now that we require recent Python, unconditionally use the standard library.
+ Custom JSON libraries can be used via custom serialization functions.
+"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+from __future__ import annotations
+
+import json
+from typing import Any
+
+# backward-compatibility, unused
+jsonmod = json
+
+
+def dumps(o: Any, **kwargs) -> bytes:
+ """Serialize object to JSON bytes (utf-8).
+
+ Keyword arguments are passed along to :py:func:`json.dumps`.
+ """
+ return json.dumps(o, **kwargs).encode("utf8")
+
+
+def loads(s: bytes | str, **kwargs) -> dict | list | str | int | float:
+ """Load object from JSON bytes (utf-8).
+
+ Keyword arguments are passed along to :py:func:`json.loads`.
+ """
+ if isinstance(s, bytes):
+ s = s.decode("utf8")
+ return json.loads(s, **kwargs)
+
+
+__all__ = ['dumps', 'loads']
diff --git a/lib/python3.12/site-packages/zmq/utils/monitor.py b/lib/python3.12/site-packages/zmq/utils/monitor.py
new file mode 100644
index 0000000000000000000000000000000000000000..872e24dc2953ba17877d39c1628d3f38d53484d6
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/monitor.py
@@ -0,0 +1,128 @@
+"""Module holding utility and convenience functions for zmq event monitoring."""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import annotations
+
+import struct
+from typing import Awaitable, TypedDict, overload
+
+import zmq
+import zmq.asyncio
+from zmq.error import _check_version
+
+
+class _MonitorMessage(TypedDict):
+ event: int
+ value: int
+ endpoint: bytes
+
+
+def parse_monitor_message(msg: list[bytes]) -> _MonitorMessage:
+ """decode zmq_monitor event messages.
+
+ Parameters
+ ----------
+ msg : list(bytes)
+ zmq multipart message that has arrived on a monitor PAIR socket.
+
+ First frame is::
+
+ 16 bit event id
+ 32 bit event value
+ no padding
+
+ Second frame is the endpoint as a bytestring
+
+ Returns
+ -------
+ event : dict
+ event description as dict with the keys `event`, `value`, and `endpoint`.
+ """
+ if len(msg) != 2 or len(msg[0]) != 6:
+ raise RuntimeError(f"Invalid event message format: {msg}")
+ event_id, value = struct.unpack("=hi", msg[0])
+ event: _MonitorMessage = {
+ 'event': zmq.Event(event_id),
+ 'value': zmq.Event(value),
+ 'endpoint': msg[1],
+ }
+ return event
+
+
+async def _parse_monitor_msg_async(
+ awaitable_msg: Awaitable[list[bytes]],
+) -> _MonitorMessage:
+ """Like parse_monitor_msg, but awaitable
+
+ Given awaitable message, return awaitable for the parsed monitor message.
+ """
+
+ msg = await awaitable_msg
+ # 4.0-style event API
+ return parse_monitor_message(msg)
+
+
+@overload
+def recv_monitor_message(
+ socket: zmq.asyncio.Socket,
+ flags: int = 0,
+) -> Awaitable[_MonitorMessage]: ...
+
+
+@overload
+def recv_monitor_message(
+ socket: zmq.Socket[bytes],
+ flags: int = 0,
+) -> _MonitorMessage: ...
+
+
+def recv_monitor_message(
+ socket: zmq.Socket,
+ flags: int = 0,
+) -> _MonitorMessage | Awaitable[_MonitorMessage]:
+ """Receive and decode the given raw message from the monitoring socket and return a dict.
+
+ Requires libzmq ≥ 4.0
+
+ The returned dict will have the following entries:
+ event : int
+ the event id as described in `libzmq.zmq_socket_monitor`
+ value : int
+ the event value associated with the event, see `libzmq.zmq_socket_monitor`
+ endpoint : str
+ the affected endpoint
+
+ .. versionchanged:: 23.1
+ Support for async sockets added.
+ When called with a async socket,
+ returns an awaitable for the monitor message.
+
+ Parameters
+ ----------
+ socket : zmq.Socket
+ The PAIR socket (created by other.get_monitor_socket()) on which to recv the message
+ flags : int
+ standard zmq recv flags
+
+ Returns
+ -------
+ event : dict
+ event description as dict with the keys `event`, `value`, and `endpoint`.
+ """
+
+ _check_version((4, 0), 'libzmq event API')
+ # will always return a list
+ msg = socket.recv_multipart(flags)
+
+ # transparently handle asyncio socket,
+ # returns a Future instead of a dict
+ if isinstance(msg, Awaitable):
+ return _parse_monitor_msg_async(msg)
+
+ # 4.0-style event API
+ return parse_monitor_message(msg)
+
+
+__all__ = ['parse_monitor_message', 'recv_monitor_message']
diff --git a/lib/python3.12/site-packages/zmq/utils/mutex.h b/lib/python3.12/site-packages/zmq/utils/mutex.h
new file mode 100644
index 0000000000000000000000000000000000000000..b6275ea28b0799607b93a69679a86f51a95fbb28
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/mutex.h
@@ -0,0 +1,84 @@
+/*
+* simplified from mutex.c from Foundation Library, in the Public Domain
+* https://github.com/rampantpixels/foundation_lib/blob/master/foundation/mutex.c
+*
+* This file is Copyright (C) PyZMQ Developers
+* Distributed under the terms of the Modified BSD License.
+*
+*/
+
+#pragma once
+
+#include
+
+#if defined(_WIN32)
+# include
+#else
+# include
+#endif
+
+typedef struct {
+#if defined(_WIN32)
+ CRITICAL_SECTION csection;
+#else
+ pthread_mutex_t mutex;
+#endif
+} mutex_t;
+
+
+static void
+_mutex_initialize(mutex_t* mutex) {
+#if defined(_WIN32)
+ InitializeCriticalSectionAndSpinCount(&mutex->csection, 4000);
+#else
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutex->mutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+#endif
+}
+
+static void
+_mutex_finalize(mutex_t* mutex) {
+#if defined(_WIN32)
+ DeleteCriticalSection(&mutex->csection);
+#else
+ pthread_mutex_destroy(&mutex->mutex);
+#endif
+}
+
+mutex_t*
+mutex_allocate(void) {
+ mutex_t* mutex = (mutex_t*)malloc(sizeof(mutex_t));
+ _mutex_initialize(mutex);
+ return mutex;
+}
+
+void
+mutex_deallocate(mutex_t* mutex) {
+ if (!mutex)
+ return;
+ _mutex_finalize(mutex);
+ free(mutex);
+}
+
+int
+mutex_lock(mutex_t* mutex) {
+#if defined(_WIN32)
+ EnterCriticalSection(&mutex->csection);
+ return 0;
+#else
+ return pthread_mutex_lock(&mutex->mutex);
+#endif
+}
+
+int
+mutex_unlock(mutex_t* mutex) {
+#if defined(_WIN32)
+ LeaveCriticalSection(&mutex->csection);
+ return 0;
+#else
+ return pthread_mutex_unlock(&mutex->mutex);
+#endif
+}
diff --git a/lib/python3.12/site-packages/zmq/utils/pyversion_compat.h b/lib/python3.12/site-packages/zmq/utils/pyversion_compat.h
new file mode 100644
index 0000000000000000000000000000000000000000..fb19dcf09d6840ac12e995a308bb96838813797a
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/pyversion_compat.h
@@ -0,0 +1,12 @@
+#include "Python.h"
+
+// default to Python's own target Windows version(s)
+// override by setting WINVER, _WIN32_WINNT, (maybe also NTDDI_VERSION?) macros
+#ifdef Py_WINVER
+#ifndef WINVER
+#define WINVER Py_WINVER
+#endif
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT Py_WINVER
+#endif
+#endif
diff --git a/lib/python3.12/site-packages/zmq/utils/strtypes.py b/lib/python3.12/site-packages/zmq/utils/strtypes.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d90a04809ca4f29e1457099b63c03302e837a1e
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/strtypes.py
@@ -0,0 +1,62 @@
+"""Declare basic string types unambiguously for various Python versions.
+
+Authors
+-------
+* MinRK
+"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+
+import warnings
+
+bytes = bytes
+unicode = str
+basestring = (str,)
+
+
+def cast_bytes(s, encoding='utf8', errors='strict'):
+ """cast unicode or bytes to bytes"""
+ warnings.warn(
+ "zmq.utils.strtypes is deprecated in pyzmq 23.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ if isinstance(s, bytes):
+ return s
+ elif isinstance(s, str):
+ return s.encode(encoding, errors)
+ else:
+ raise TypeError(f"Expected unicode or bytes, got {s!r}")
+
+
+def cast_unicode(s, encoding='utf8', errors='strict'):
+ """cast bytes or unicode to unicode"""
+ warnings.warn(
+ "zmq.utils.strtypes is deprecated in pyzmq 23.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ if isinstance(s, bytes):
+ return s.decode(encoding, errors)
+ elif isinstance(s, str):
+ return s
+ else:
+ raise TypeError(f"Expected unicode or bytes, got {s!r}")
+
+
+# give short 'b' alias for cast_bytes, so that we can use fake b'stuff'
+# to simulate b'stuff'
+b = asbytes = cast_bytes
+u = cast_unicode
+
+__all__ = [
+ 'asbytes',
+ 'bytes',
+ 'unicode',
+ 'basestring',
+ 'b',
+ 'u',
+ 'cast_bytes',
+ 'cast_unicode',
+]
diff --git a/lib/python3.12/site-packages/zmq/utils/win32.py b/lib/python3.12/site-packages/zmq/utils/win32.py
new file mode 100644
index 0000000000000000000000000000000000000000..019d429715af914d4aeb508bda342f764a74e9d0
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/win32.py
@@ -0,0 +1,130 @@
+"""Win32 compatibility utilities."""
+
+# -----------------------------------------------------------------------------
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+# -----------------------------------------------------------------------------
+from __future__ import annotations
+
+import os
+from typing import Any, Callable
+
+
+class allow_interrupt:
+ """Utility for fixing CTRL-C events on Windows.
+
+ On Windows, the Python interpreter intercepts CTRL-C events in order to
+ translate them into ``KeyboardInterrupt`` exceptions. It (presumably)
+ does this by setting a flag in its "console control handler" and
+ checking it later at a convenient location in the interpreter.
+
+ However, when the Python interpreter is blocked waiting for the ZMQ
+ poll operation to complete, it must wait for ZMQ's ``select()``
+ operation to complete before translating the CTRL-C event into the
+ ``KeyboardInterrupt`` exception.
+
+ The only way to fix this seems to be to add our own "console control
+ handler" and perform some application-defined operation that will
+ unblock the ZMQ polling operation in order to force ZMQ to pass control
+ back to the Python interpreter.
+
+ This context manager performs all that Windows-y stuff, providing you
+ with a hook that is called when a CTRL-C event is intercepted. This
+ hook allows you to unblock your ZMQ poll operation immediately, which
+ will then result in the expected ``KeyboardInterrupt`` exception.
+
+ Without this context manager, your ZMQ-based application will not
+ respond normally to CTRL-C events on Windows. If a CTRL-C event occurs
+ while blocked on ZMQ socket polling, the translation to a
+ ``KeyboardInterrupt`` exception will be delayed until the I/O completes
+ and control returns to the Python interpreter (this may never happen if
+ you use an infinite timeout).
+
+ A no-op implementation is provided on non-Win32 systems to avoid the
+ application from having to conditionally use it.
+
+ Example usage:
+
+ .. sourcecode:: python
+
+ def stop_my_application():
+ # ...
+
+ with allow_interrupt(stop_my_application):
+ # main polling loop.
+
+ In a typical ZMQ application, you would use the "self pipe trick" to
+ send message to a ``PAIR`` socket in order to interrupt your blocking
+ socket polling operation.
+
+ In a Tornado event loop, you can use the ``IOLoop.stop`` method to
+ unblock your I/O loop.
+ """
+
+ def __init__(self, action: Callable[[], Any] | None = None) -> None:
+ """Translate ``action`` into a CTRL-C handler.
+
+ ``action`` is a callable that takes no arguments and returns no
+ value (returned value is ignored). It must *NEVER* raise an
+ exception.
+
+ If unspecified, a no-op will be used.
+ """
+ if os.name != "nt":
+ return
+ self._init_action(action)
+
+ def _init_action(self, action):
+ from ctypes import WINFUNCTYPE, windll
+ from ctypes.wintypes import BOOL, DWORD
+
+ kernel32 = windll.LoadLibrary('kernel32')
+
+ #
+ PHANDLER_ROUTINE = WINFUNCTYPE(BOOL, DWORD)
+ SetConsoleCtrlHandler = self._SetConsoleCtrlHandler = (
+ kernel32.SetConsoleCtrlHandler
+ )
+ SetConsoleCtrlHandler.argtypes = (PHANDLER_ROUTINE, BOOL)
+ SetConsoleCtrlHandler.restype = BOOL
+
+ if action is None:
+
+ def action():
+ return None
+
+ self.action = action
+
+ @PHANDLER_ROUTINE
+ def handle(event):
+ if event == 0: # CTRL_C_EVENT
+ action()
+ # Typical C implementations would return 1 to indicate that
+ # the event was processed and other control handlers in the
+ # stack should not be executed. However, that would
+ # prevent the Python interpreter's handler from translating
+ # CTRL-C to a `KeyboardInterrupt` exception, so we pretend
+ # that we didn't handle it.
+ return 0
+
+ self.handle = handle
+
+ def __enter__(self):
+ """Install the custom CTRL-C handler."""
+ if os.name != "nt":
+ return
+ result = self._SetConsoleCtrlHandler(self.handle, 1)
+ if result == 0:
+ # Have standard library automatically call `GetLastError()` and
+ # `FormatMessage()` into a nice exception object :-)
+ raise OSError()
+
+ def __exit__(self, *args):
+ """Remove the custom CTRL-C handler."""
+ if os.name != "nt":
+ return
+ result = self._SetConsoleCtrlHandler(self.handle, 0)
+ if result == 0:
+ # Have standard library automatically call `GetLastError()` and
+ # `FormatMessage()` into a nice exception object :-)
+ raise OSError()
diff --git a/lib/python3.12/site-packages/zmq/utils/z85.py b/lib/python3.12/site-packages/zmq/utils/z85.py
new file mode 100644
index 0000000000000000000000000000000000000000..016bdfe12f303f1542fe00fc92ab1a72a20a20b9
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/z85.py
@@ -0,0 +1,58 @@
+"""Python implementation of Z85 85-bit encoding
+
+Z85 encoding is a plaintext encoding for a bytestring interpreted as 32bit integers.
+Since the chunks are 32bit, a bytestring must be a multiple of 4 bytes.
+See ZMQ RFC 32 for details.
+
+
+"""
+
+# Copyright (C) PyZMQ Developers
+# Distributed under the terms of the Modified BSD License.
+from __future__ import annotations
+
+import struct
+
+# Z85CHARS is the base 85 symbol table
+Z85CHARS = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"
+# Z85MAP maps integers in [0,84] to the appropriate character in Z85CHARS
+Z85MAP = {c: idx for idx, c in enumerate(Z85CHARS)}
+
+_85s = [85**i for i in range(5)][::-1]
+
+
+def encode(rawbytes):
+ """encode raw bytes into Z85"""
+ # Accepts only byte arrays bounded to 4 bytes
+ if len(rawbytes) % 4:
+ raise ValueError(f"length must be multiple of 4, not {len(rawbytes)}")
+
+ nvalues = len(rawbytes) // 4
+ values = struct.unpack(f'>{nvalues:d}I', rawbytes)
+ encoded = []
+ for v in values:
+ for offset in _85s:
+ encoded.append(Z85CHARS[(v // offset) % 85])
+
+ return bytes(encoded)
+
+
+def decode(z85bytes):
+ """decode Z85 bytes to raw bytes, accepts ASCII string"""
+ if isinstance(z85bytes, str):
+ try:
+ z85bytes = z85bytes.encode('ascii')
+ except UnicodeEncodeError:
+ raise ValueError('string argument should contain only ASCII characters')
+
+ if len(z85bytes) % 5:
+ raise ValueError(f"Z85 length must be multiple of 5, not {len(z85bytes)}")
+
+ nvalues = len(z85bytes) // 5
+ values = []
+ for i in range(0, len(z85bytes), 5):
+ value = 0
+ for j, offset in enumerate(_85s):
+ value += Z85MAP[z85bytes[i + j]] * offset
+ values.append(value)
+ return struct.pack(f'>{nvalues:d}I', *values)
diff --git a/lib/python3.12/site-packages/zmq/utils/zmq_compat.h b/lib/python3.12/site-packages/zmq/utils/zmq_compat.h
new file mode 100644
index 0000000000000000000000000000000000000000..3dcb47177636174732166c9b52a6e836cab38ef1
--- /dev/null
+++ b/lib/python3.12/site-packages/zmq/utils/zmq_compat.h
@@ -0,0 +1,85 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2010 Brian Granger, Min Ragan-Kelley
+//
+// Distributed under the terms of the New BSD License. The full license is in
+// the file LICENSE.BSD, distributed as part of this software.
+//-----------------------------------------------------------------------------
+
+#pragma once
+
+#if defined(_MSC_VER)
+#define pyzmq_int64_t __int64
+#define pyzmq_uint32_t unsigned __int32
+#else
+#include
+#define pyzmq_int64_t int64_t
+#define pyzmq_uint32_t uint32_t
+#endif
+
+
+#include "zmq.h"
+
+#define _missing (-1)
+
+#if (ZMQ_VERSION >= 40303)
+ // libzmq >= 4.3.3 defines zmq_fd_t for us
+ #define ZMQ_FD_T zmq_fd_t
+#else
+ #ifdef _WIN32
+ #if defined(_MSC_VER) && _MSC_VER <= 1400
+ #define ZMQ_FD_T UINT_PTR
+ #else
+ #define ZMQ_FD_T SOCKET
+ #endif
+ #else
+ #define ZMQ_FD_T int
+ #endif
+#endif
+
+#if (ZMQ_VERSION >= 40200)
+ // Nothing to remove
+#else
+ #define zmq_curve_public(z85_public_key, z85_secret_key) _missing
+#endif
+
+// use unambiguous aliases for zmq_send/recv functions
+
+#if ZMQ_VERSION_MAJOR >= 4
+// nothing to remove
+ #if ZMQ_VERSION_MAJOR == 4 && ZMQ_VERSION_MINOR == 0
+ // zmq 4.1 deprecates zmq_utils.h
+ // we only get zmq_curve_keypair from it
+ #include "zmq_utils.h"
+ #endif
+#else
+ #define zmq_curve_keypair(z85_public_key, z85_secret_key) _missing
+#endif
+
+// libzmq 4.2 draft API
+#ifdef ZMQ_BUILD_DRAFT_API
+ #if ZMQ_VERSION >= 40200
+ #define PYZMQ_DRAFT_42
+ #endif
+#endif
+#ifndef PYZMQ_DRAFT_42
+ #define zmq_join(s, group) _missing
+ #define zmq_leave(s, group) _missing
+ #define zmq_msg_set_routing_id(msg, routing_id) _missing
+ #define zmq_msg_routing_id(msg) 0
+ #define zmq_msg_set_group(msg, group) _missing
+ #define zmq_msg_group(msg) NULL
+#endif
+
+#if ZMQ_VERSION >= 40100
+// nothing to remove
+#else
+ #define zmq_msg_gets(msg, prop) _missing
+ #define zmq_has(capability) _missing
+ #define zmq_proxy_steerable(in, out, mon, ctrl) _missing
+#endif
+
+// 3.x deprecations - these symbols haven't been removed,
+// but let's protect against their planned removal
+#define zmq_device(device_type, isocket, osocket) _missing
+#define zmq_init(io_threads) ((void*)NULL)
+#define zmq_term zmq_ctx_destroy