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”Kn1 D÷:…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 @@ + + + + embedding_in_wx3 + + + wxVERTICAL + + + + + + + wxALL|wxEXPAND + 5 + + + + wxHORIZONTAL + + + + + + wxALL|wxEXPAND + 2 + + + + + + + wxALL|wxEXPAND + 2 + + + + + + + + wxALL|wxEXPAND + 2 + + + + 0 + + + wxEXPAND + + + + wxLEFT|wxRIGHT|wxEXPAND + 5 + + + + + wxEXPAND + + + + + \ 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